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.
Alias Paths
Directories
Filesystem Manipulation
The file Class
Nonblocking Operation
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 | - |
$ _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 |