Table of Contents
Biferno provides the file and folder classes for server-side file
management operations. In this chapter we will discuss Biferno pathname
conventions and then describe the main methods and properties of these
classes.
Biferno pathname conventions are similar to those used by the HTTP protocol with a few differences.
We can define a pathname in two ways:
Using a relative path, i.e. starting from the current Web directory or from the web server root directory.
Using an absolute path, i.e. tracing the full file pathname starting from the root directory of the file system.
Relative pathnames follow the HTTP protocol and HTML
language rules. The "mypage.bfr" path indicates that the file is in the
current Web directory, which is the directory of the .bfr file that is
executing. If the path starts with a slash (/), e.g. "/mypage.bfr", the
file is in the server or site root directory.
To clarify the difference, assume we have implemented on our web server a
virtual host called mysite.com that points to the "mysite" directory, which
is a subdirectory of the server root directory. According to the HTTP
protocol a file in the mysite.com domain with path "/mypage.bfr" is located
in the "mysite" directory. If from a script contained in the "mysite"
folder we want to refer to a file called "myfile.txt" in the same "mysite"
directory, we can use either the relative path ("myfile.txt") or the
absolute path from the site root ("/myfile.txt").
To identify an absolute path in Biferno we use the "file://" string in
front of the pathname, which starts with the disk name (unit or partition)
on Mac OS and Windows (e.g. file://Mac OS HD/Internet/Web pages/mysite/myfile.txt or
file://C/Inetpub/myFile.txt), or with the name of the
first directory on the path starting form the file system root on UNIX
(e.g. file://export/home/usr/mysite/myfile.txt).
The syntax of Biferno paths allows the use of the ".." characters to denote
the parent directory.
A path can identify a file or a directory. A path to a directory always ends with the slash character.
To discover the absolute path of the root directory of the Web server (or
the current application, if associated to a virtual host) we can use the
predefined property serverInfo.root. The corresponding
absolute path can be derived from a relative path using the static method
ResolvePath of the file class. We can write:
<?
realPath = file.ResolvePath("documents/doc00001.txt")
?>
As we will see in the following when discussing alias handling, this method resolves also any aliases that may be contained in the path.
A file path can also be obtained in its native format, which depends on the
platform (operating system and file system) we are working on, using the
static method NativePath of the file class. We can write:
<?
filePath = "file://C/Internet/Web pages/mysite/myfile.txt"
nativePath = file.NativePath(filePath)
?>
On Windows the result is "C:\Internet\Web pages\mysite\myfile.txt".
In Biferno a file class variable can be initialized by simply passing to
the class constructor a string containing the path (relative or absolute)
of the file to associate to the variable. In the following example we
instantiate three variables of the file class using the three kinds of path
previously mentioned:
<?
file1 = file("documents/doc00001.txt")
file2 = file("/mysite/documents/doc00002.txt")
file3 = file("file://home/usr/user1/mysite/documents/doc00003.txt")
?>
Normally, if the path passed to the constructor actually corresponds to an existing file on disk, the file is associated to the variable that is created and an attempt is made to open the file in read/write mode. If permissions do not allow to open the file in read/write mode, an error is generated.
This standard behavior can be modified by a number of optional parameters of the file class constructor, with prototype:
file(string path, int openMode, int permission)
Table 13.1. File class: Possible values of the openMode parameter
| openMode Value | File Opening Mode |
|---|---|
| openFileAlways | If the file does not exist, is created and opened. |
| openFileExisting | If the file does not exist, an error is generated. |
| createFileNew | Create a new file. If the file does already exist, an error is generated. |
| createFileAlways | Create a new file. If the file does already exist, is deleted and replaced by the newly created file. |
| dontOpen | Do not open the file, simply check path validity. |
The openMode parameter can assume one of the constant values listed in
Table 13.1, “File class: Possible values of the openMode parameter”. The default value for this parameter is
normally openFileExisting, except if the file is an alias. In this case the
default is dontOpen.
The permission parameter can assume the constant values rw (read/write) or
r (read only). The default value is rw.
The following example shows how to use the parameters:
<?
// If the file exists, is opened read-only
file1 = file("doc00001.txt", openFileExisting, r)
// If the file does not exist, is created and opened
file2 = file("doc00002.txt", openFileAlways)
// A new file is created and opened
file3 = file("doc00003.txt", createFileAlways)
?>
All constant values we have described so far are actually static public
constants of the file class. In the previous example we have seen how in
Biferno the class name can be omitted in front of static constants when
calling a method of the same class (including the constructor). In the
general case we would have to write something like
file.openFileExisting
After creating a file class object, the complete file path on disk is
stored in the read-only property path, while the read-only property name
contains only the filename. We have mentioned that a file class variable
can be created without opening the file on disk. A file can be opened and
closed using the Open and Close methods (without parameters). All files
initialized and opened by a script are automatically closed when the script
is terminated (or, more precisely, on the exit of the corresponding file
variable scope).
To check if a file is open on disk we can use the static method IsOpen of
the file class, with prototype:
static boolean IsOpen(string filePath)
This method returns true if the file that filePath points to is currently
open (by some process active on the system), false if it is not open. To
check if a file that an object of the file class points to is open, we use
the read-only property isOpen.
<?
if (file1.isOpen)
file1.Close()
?>
To know in advance if a file or directory exists we can use the static
method Exists of the file class, with prototype:
static boolean Exists(string path)
This method returns true if the path parameter is a valid path that points
to an existing file or directory, false otherwise.
A similar method is the CheckPath method, which returns the integer value 0
(zero) if the file exists, or otherwise returns the error code generated by
the file system.
We can check in a similar fashion if a path points to an alias (i.e., as we
will see in the following, to a link to a file) or to a directory, using
the static methods IsAlias and IsFolder of the file class. These methods
return a Boolean value and take as parameter a string containing a file
path. As for the IsOpen method, the read-only property isAlias corresponds
to the IsAlias static method.
Data can be written in a file using specific methods of the file class.
The Put method allows to write a string into a file with a given offset
(distance, in characters, from the beginning of the file) and has
prototype:
void Put(int offset, string text)
The inserted text overwrites the current content of the file starting from the given offset and, if necessary, increases the length of the file.
In the following example we create a new file called "test.txt" in the current directory and insert the test "This is the first line of the file" followed by a line feed.
<?
myFile = file("test.txt", openFileAlways)
myFile.Put(0, "This is the first line of the file\n")
?>
Since the file has just been created and is empty, we specify a zero offset. In general, the offset parameter has always to be less than the logical file size (total number of bytes, or characters).
To insert a new line of text in the file it is convenient to use the
Append method, which always adds the text at the end of the file. In the
case of the previous example, we can write the following code line:
myFile.Append("This is the second line of the file \n")
After the execution of this instruction the "test.txt" file contains the following two lines:
This is the first line of the file
This is the second line of the file
The Append method can also be used if the file is empty. In practice we
will use the Put method only when we want to replace the content of an
existing file with new data. Let's take the "test.txt" file we created and
replace the second line with another string.
<?
myFile = file("test.txt")
offset = 36
str = "New text"
myFile.Put(offset, str)
myFile.length = offset + str.length
?>
The offset value was computed by manually counting the characters of the
first row, including the line feed. Notice that, since the str string is
shorter than the second line in the file, it is necessary to adjust the
file length to eliminate the remaining characters. A file size can be
decreased or increased at will by directly operating on the length
property (with the limitations imposed by the physically available space
on disk).
To read data from a file we use the Get method, with prototype:
string Get(int offset, int len = all)
In this case the offset represents the number of characters to skip from
the beginning of the file before reading data. The len parameter
represents the total number of character that we want to extract. The
default value for len is all (predefined constant of the file class),
which extracts all characters until the end of the file is reached. The
Get method returns the characters extracted from a file in a string. In
the following example we assign the entire content of the "test.txt" file
to the text variable.
<?
myFile = file("test.txt")
text = myFile.Get(0, all)
text = myFile.Get() // same as the above
?>
A file can also be read one line at a time using the GetNextLine method
with prototype:
string GetNextLine(void)
A line of a file is defined as a sequence of characters followed by a new line (CR, LF or CR+LF). In the following example we extract and print one at a time all lines of the "test.txt" file.
<?
myFile = file("test.txt")
do {
$myFile.GetNextLine()
} while (myFile.curLine != file.EOF)
?>
To check if the loop reached the end of the file, i.e. when the last line
of text has been extracted, we check the value of the curLine property,
which contains the index of the current line (the line after the last line
extracted calling the GetNextLine method). After the last line has been
extracted (read), the curLine property contains the value corresponding to
the EOF (End Of File) constant of the file class.
The Get method ignores the value of curLine and in general the position
reached by GetNextLine on the current file. It is advisable to avoid
mixing calls to GetNextLine and calls to Get on the same file instance.
To rename a file we can use the Rename method of the file class and pass it a
string that contains the new name to be assigned to the file (just the
name, not the full path).
myFile.Rename("doc001.txt")
It is necessary to verify in advance if a file with the same name already exists in the same directory, otherwise the operation will fail.
To move a file from a directory into another directory, the file class provides
the Move method, with prototype:
void Move(string filePath, boolean replace = false)
The filePath parameter specifies the new path to assign to the file, or,
alternatively, the path to the directory that the file should be moved to.
The replace parameter specifies if the file that is moved should replace a
file with the same name in the target directory, if one exists. To move a
file into a subdirectory we can write:
<?
myFile.Move("docs/")
?>
In the following example we move a file into a subdirectory and rename an existing file with the same name, if present:
<?
fileName = "doc001.txt"
newPath = "docs/" + fileName
if (file.Exists(newPath))
{
oldFile = file(newPath)
oldFile.Rename(fileName + ".old")
}
myFile = file(fileName)
myFile.Move(newPath)
?>
Notice that renaming the old file may fail if the name to be assigned is already used by another file in the same directory. In real life every time the name or location of a file is changed, the possible presence of duplicates should be checked.
The Copy method of the file class allows to create a copy of a file in a
different location. This method has the same parameters as the Move method
and the same considerations hold. The new file created by the Copy method
is not automatically opened (if opening it is necessary, use the Open
method).
In practice one will often want to move (or copy) a file and rename it at
the same time. This can be done by specifying not only a new directory,
but also a new file name in the filePath parameter. An example of renaming
by using the Move method is:
<?
myFile = file("texts/text1.txt")
myFile.Move("archived/doc001.txt")
?>
The same can be done for the Copy method, a file can be duplicated and a new name can be
assigned to the copy at the same time.
To create and manipulate a directory (also called a folder) Biferno
provides the folder class, which is in many respects similar to the file
class. This section describes the fundamental operations that can be
executed on a directory using methods of the folder class.
When a folder class variable is instantiated by passing to the class
constructor a string containing the path to a directory, we can decide if
we wish to create a new directory or to have the object point to an
existing directory.
The folder class constructor has the same parameters of the file class
constructor, except the permission parameter. The prototype is:
folder(string path, int openMode)
The openMode parameter for the folder class can assume the values listed
in Table 13.2, “Folder class: Possible values of the openMode parameter”. The default value is folderExisting.
To create the "docs" directory in our site root we will write:
<?
docsFolder = folder("/mysite/docs/", createFolderIfNeeded)
?>
Table 13.2. Folder class: Possible values of the openMode parameter
| openMode Value | Directory Creation Mode |
|---|---|
| createFolderIfNeeded | Creates a new directory if the directory does not exist. Otherwise the object points to the existing directory. |
| createFolderNew | Creates a new directory if the directory does not exist. Otherwise an error is generated. |
| folderExisting | Does not create a new directory. If the directory does not exist an error is generated. |
If the directory specified by the path parameter exists, the docsFolder
variable points to the existing directory.
The static method:
static void Create(string path)
can be used to create a directory without instantiating a variable. This method has no effect if the directory exists.
To delete a directory we use the Delete method of the folder class, as in:
<?
myFolder = folder("garbage/")
myFolder.Delete()
?>
A directory can only be deleted if it is empty. As in the case of files,
the corresponding variable is no longer valid after deletion, even if the
path property still contains the path to the deleted directory.
To rename a directory we can use the Rename method of the folder class passing
a string that contains the new name to be assigned to the directory. An
example is:
<?
myFolder = folder("/mysite/docs/")
myFolder.Rename("documents")
?>
Using the Walk method of the folder class a directory can be examined and
operations can be executed automatically on every file contained in the
directory, without previous knowledge of the number, nature and names of
the files.
The Walk method has the following prototype:
int Walk(string suffix, boolean recursive, string callBack, obj *userData)
The suffix parameters denotes the type, i.e. the extension of the files we
want to examine. Passing the ".txt" string will filter only text files,
while the ".*" string will result in no filtering, i.e. all file types
will be examined. Notice that passing an empty string in the suffix
parameter will result in filtering only files with no extension. The
Boolean recursive parameter specifies if we want to explore all
subdirectories of the target directory. The callBack parameter is the name
of a function we want to execute on each file found in the target
directory. The userData optional parameter can be an object of any class
and can be only passed by reference.
The method returns an integer value that contains an error code generated
by the callback function. If there have been no errors during execution,
the function must return the value 0 (zero).
The callBack function must have the following prototype:
function int function_name (string filePath, int variant, obj *userData)
When the Walk method is invoked, it examines one by one all files present
in the directory, possibly discarding files that should be excluded on the
base of the value of the suffix parameter (hidden files are always
excluded). For each file it calls the callback function passing as
parameters the file path, a value (variant), which tells to the function
if the path points to an alias or directory, and, if present, the userData
object.
In the following example the Walk method is used to collect general
information on a directory (number of elements and their byte size). The
GetItemInfo callback function modifies the properties of an object of the
folderInfo class (defined at the beginning of our script) which, when the
Walk method terminates, contains all desired information on the target
directory. Notice the use of the variant parameter and of the isFolderBit
and isAliasBit constants of the file class. The size (number of characters
or bytes) is contained in the length property of the file class. The resForkLength property of the file class is meaningful only on the Mac OS
platform and contains the size of the resource fork of Macintosh file
(this property has always value zero on all other platforms).
<?
class folderInfo
{
unsigned totItems = 0
unsigned subFolders = 0
unsigned aliases = 0
unsigned totSize = 0
}
function int GetItemInfo(string filePath, int variant, folderInfo
*fInfo)
{
fInfo.totItems++
if (variant & file.isFolderBit) // is a directory
fInfo.subFolders++
else
{
if (variant & file.isAliasBit) // is an alias
fInfo.aliases++
theFile = file(filePath)
fInfo.totSize += theFile.length + theFile.resForkLength
}
return 0
}
fInfo = folderInfo()
myFolder = folder("file://HD/Projects/mysite/")
theErr = myFolder.Walk(".*", true, "GetItemInfo", &fInfo)
?>
<html>
<body>
Folder: $myFolder.path$<br>
Items: $fInfo.totItems$<br>
Subfolders: $fInfo.subFolders$<br>
Aliases: $fInfo.aliases$<br>
Total Size: $fInfo.totSize$ bytes<br>
</body>
</html>
This script will produce an output similar to the following:
Folder: file://HD/Projects/mysite/
Items: 24
Subfolders: 2
Aliases: 2
Size: 89835 bytes
The behavior of the Walk method is slightly dependent on the operating
system if the content of the folder is modified while executing the method
itself. In particular, on MacOS Classic the identification of a file is
implemented via a sequence number (position) within the folder and such
numbering might become inconsistent if files are removed from the folder
using the Walk method. For this reason it is not trivial to use the Walk
method to delete the content of a directory on the MacOS Classic platform.
As mentioned previously an alias is a pointer, or symbolic link, to a physical file or directory. In general an alias can reside in a different directory than the original. In computer jargon, resolve an alias means to obtain the path to the original file or directory that the alias points to.
Beware that no operations on aliases are supported on the Windows platform.
When instantiating a variable of the file class passing the path to an
alias, the class constructor does not automatically resolve the alias, but
rather returns an object that points directly to the alias. Conversely, if
a file path contains directory aliases, they are all automatically resolved
by the constructor, as well as by other methods of the file class that
accept a path as a parameter (Rename, Copy, Move, etc.).
An error message is returned if we specify in the constructor an opening
mode other than dontOpen (which is the default for aliases), because an
alias can not be opened. We have mentioned the IsAlias method, which
determines if a path points to a physical file or to an alias. Once
established that a path points to an alias, the ResolvePath static method
of the file class can be used to obtain the path to the original file. This
method has prototype:
static string ResolvePath(string aliasPath)
The following example shows the use of this method in combination with the
IsAlias method.
<?
myPath = "file://HD/WebSTAR Server Suite 4.1/mysite/test.txt"
if (file.IsAlias(myPath))
{
originalPath = file.ResolvePath(myPath)
myFile = file(originalPath)
}
?>
Alternatively, we can use the ResolveAlias method that, invoked on a file
class object pointing to an alias, returns a string containing the path to
the original file. The previous example can be rewritten as follows:
<?
myFile = file("file://HD/WebSTAR Server Suite 4.1/mysite/test.txt")
if (myFile.isAlias)
{
myFile = file(myFile.ResolveAlias())
}
?>
If, after the first initialization, the myFile variable points to an alias,
the alias is resolved and the variable is reinitialized to make it point to
the original file.
The Move, Rename and Copy methods of the file class applied to an alias act
on the alias and not on the original file.
We can create an alias to a file using the MakeAlias method of the file class, with
prototype:
void MakeAlias(string aliasPath)
An example is:
<?
myFile = file("test.txt")
// Create an alias in the same directory
myFile.MakeAlias("test_alias.txt")
// Create an alias in another directory
myFile.MakeAlias("file://HD/Aliases/test_alias.txt")
?>
An alias to a directory can be created in a similar way using the MakeAlias
method of the folder class.
On Linux file and directory access permissions can be modified at the owner, group and other granularity. The possible permission values are:
Read only
Read and write
Read, write and execute.
To modify permissions on a file or directory, we use the static method
fchmod (common to the file and folder classes). This method takes two
parameters, the file or directory path, and an integer resulting from the
sum of predefined constants for the file and folder classes. The constants
are listed in Table 13.3, “File class: Predefined constants for file and directory access
permissions on Linux”.
Table 13.3. File class: Predefined constants for file and directory access permissions on Linux
| Predefined Costant | Corresponding Access Permission |
|---|---|
| S_IRGRP | Read only for the group. |
| S_IROTH | Read only for other users. |
| S_IRUSR | Read only for the owner. |
| S_IWGRP | Read and write only for the group. |
| S_IWOTH | Read and write for other users. |
| S_IWUSR | Read and write for the owner. |
| S_IXGRP | Read, write and execution for the group. |
| S_IXOTH | Read, write and execution for other users. |
| S_IXUSR | Read, write and execution for the owner. |
To assign read/write permission to the owner and read-only permission to
everybody else on the file corresponding to the myFile variable we write:
file.fchmod(myFile.path, S_IWUSR + S_IRGRP + S_IROTH)
Information on the permissions assigned to a file or directory can be
obtained using the fgetmod static method (common to the file and folder
classes). An example is:
permission = file.fgetmod(myFile.path)
On the Mac OS platform we can obtain and, if desired, modify the type and
creator of a file using the corresponding properties osType and osCreator
of the file class. In the following example, after verification that the
platform is "MacOS", we check that a file is a text file and assign it as
creator the signature of the “SimpleText” application:
<?
if (biferno.os.Begins("MacOS"))
{
if (myFile.osType == "TEXT")
myFile.osCreator = "ttxt"
}
?>
The two properties osType and osCreator are meaningful only on the Mac OS
platform and contain the empty string on all other platforms.
The biferno.os predefined property returns a string that identified the
operating system of the server running Biferno. This string always starts
with the generic system name followed by a : character (colon), e.g. with
"MacOS:" on the Macintosh Classic platform, with "Unix:" on Linux and
MacOSX, and with "Windows:" on Windows. The rest of the string is the
system version.
For all other properties of the file and folder classes (user, group,
modifTime, creatTime, etc…) see the online Reference Guide as
referenced in Chapter 2, Documentation.