Examples Documentation Contact Downloads

Files

Within Grace, there are several ways to treat access to files, suitable both for quick load/save jobs and more elaborate asynchronous i/o with timeouts. On top of that, Grace abstracts the filesystem itself to facilitate alias paths and a thread-local working directory.

The filesystem Class

The global fs object is a singleton instance of the filesystem class. It is the quickest path to getting files in and out of a string object:

#include <grace/filesystem.h>

void dumbify (const string &filename)
{
    string text = fs.load (filename);
    if (text)
    {
        text.replace ($("the","teh"));
        fs.save (filename, text);
    }
}

Alias Paths

Alias paths in Grace form a concept that allows filesystem locations to be more fluid. All classes in grace that deal with paths accept alias paths in their place. An alias path follow this structure:

      ALIASPATH := ([ALIAS NAME + ':'] + RELATIVE PATH) || (ABSOLUTE PATH)
    
The application will set up a number of default alias paths for you:
Alias Resolves to Function Standalone
bin: $PATH Path to Unix commands
app: App location Access to your app bundle -
home: $HOME User home directory
var: /var or home:var Convenience alias
log: var:log Logfile storage
rsrc: app:Contents/Resources Convenience alias -
schema: app:Contents/Schemas Convenience alias -
conf: See here Configuration location -
On startup, environment variables that start with _PATH_ are taken to be $PATH-like variables enumerating a list of paths an alias should expand to:

$ _PATH_QUUX=/tmp grace
>>> fs.save ("quux:foobar","Hello, world.\n")
>>> exit
$ cat /tmp/foobar
Hello, world.
$

By using alias paths where possible, you give your application a great measure of path-independence, allowing administrators and distribution builders more control over its behavior.

Directories

The fs.ls call gives you the contents of a directory wrapped in a value object. Typically it looks like this:


<?xml version="1.0" encoding="UTF-8"?>
<dict>
    <dict id="log">
        <string id="path">/var/log</string>
        <long id="inode">2570049</long>
        <integer id="nlink">53</integer>
        <integer id="type">2</integer>
        <unsigned id="mode">493</unsigned>
        <unsigned id="fuid">0</unsigned>
        <unsigned id="fgid">0</unsigned>
        <unsigned id="size">1802</unsigned>
        <date id="atime">2008-06-28T03:20:22</date>
        <date id="mtime">2008-06-29T08:00:10</date>
        <date id="ctime">2008-06-29T08:00:10</date>
    </dict>
    <dict id="run">
        <string id="path">/var/run,/Users/pi/var/run</string>
        <long id="inode">2991807</long>
        <integer id="nlink">21</integer>
        <integer id="type">2</integer>
        <unsigned id="mode">493</unsigned>
        <unsigned id="fuid">501</unsigned>
        <unsigned id="fgid">501</unsigned>
        <unsigned id="size">714</unsigned>
        <date id="atime">2008-06-28T03:19:48</date>
        <date id="mtime">2008-06-21T17:11:50</date>
        <date id="ctime">2008-06-21T17:11:50</date>
    </dict>
    <dict id="opencore">
        <string id="path">/Users/pi/var/openpanel</string>
        <long id="inode">2991804</long>
        <integer id="nlink">2</integer>
        <integer id="type">2</integer>
        <unsigned id="mode">493</unsigned>
        <unsigned id="fuid">501</unsigned>
        <unsigned id="fgid">501</unsigned>
        <unsigned id="size">68</unsigned>
        <date id="atime">2008-06-28T03:19:48</date>
        <date id="mtime">2006-07-10T18:51:14</date>
        <date id="ctime">2007-11-22T12:38:10</date>
    </dict>
</dict>

As you may have guessed this is a trimmed listing of the alias path var:. If you are not interested in any of the meta-information, you can also use fs.dir, which will only fill in the path variable for each file.

The type field conforms to an enum which can take the following values:
Enum value Int value Node type
filetype::unknown 0 Unknown type
filetype::data 1 Regular file object
filetype::directory 2 Directory
filetype::chardevice 3 Character device node
filetype::blockdevice 4 Block device node
filetype::fifo 5 FIFO
filetype::softlink 6 Soft link
filetype::socket 7 Unix domain socket
filetype::bundle 8 Bundle directory

Filesystem Manipulation

The filesystem class has methods for tweaking objects on the file system mostly like you'd expect from a shell:

#include <grace/filesystem.h>

bool myApp::makeStorageDir (void)
{
    if (fs.exists ("var:myapp") && fs.isdir ("var:myapp"))
        return true;
    
    if (! fs.isdir ("var:myapp")) fs.rm ("var:myapp");
    if (! fs.mkdir ("var:myapp")) return false;
    if (! fs.chown ("var:myapp", "myapp")) return false;
    if (! fs.chmod ("var:myapp", 0750)) return false;
    
    if (fs.exists ("rsrc:storage-skeleton"))
    {
        fs.cp ("rsrc:storage-skeleton", "var:myapp/storage");
    }
    
    return true;
}

The file Class

Although this is often convenient, the filesystem class only deals with entire files at a time. The file class deals with files at a lower level. Here it is in a nutshell:

file f;

// Reading line-by-line
if (! f.openread ("myfile")) return false;
while (! f.eof())
{
    string ln = f.gets ();
    fout.writeln (ln);
}
f.close ();

// Binary reading
string pngdata;
if (! f.openread ("myimage.png")) return false;
while (! f.eof())
{
    pngdata.strcat (f.read (1024));
}
f.close ();
fs.save ("loaded.png", pngdata);

// Writing line-by-line
if (! f.openwrite ("outfile.dat")) return false;
f.writeln ("Content-type: image/png");
f.writeln ("Content-length: %i" %format (pngdata.strlen()));

// Writing a BLOB
f.puts (pngdata);
f.close ();

The file constructor can also take a string as an argument, allowing for more convenient binding of a file object to a specific operation on a specific file:

   file infile ("<in.txt");
   file outfile (">out.txt");

A file that is open for reading is also iterable on a line-by-line basis, so you can do something like this:

   file infile ("<in.txt");
   foreach (line, infile)
   {
    fout.writeln (line);
   }

Nonblocking Operation

In cases where you are using a file object that is connected to a slow or fallible resource, you may want to deal with i/o operations that time out:

// try to read a line
string line;
if (! myfile.waitforline (line, 100))
{
    myfile.close ();
    return timeout;
}

int blocksize = line.toint ();

// try to read data for max 100 milliseconds
string blk = myfile.read (blocksize, 100);
if (blk.strlen() == 0)
{
    myfile.close ();
    return timeout;
}

If you use read with a timeout, note that you may also receive less than the requested size.

Previous Chapter Table of Contents Next Chapter