| | 1 | #charset "us-ascii" |
| | 2 | |
| | 3 | /* |
| | 4 | * Copyright (c) 2001, 2006 Michael J. Roberts |
| | 5 | * |
| | 6 | * This file is part of TADS 3. |
| | 7 | * |
| | 8 | * This header defines the File intrinsic class. |
| | 9 | */ |
| | 10 | |
| | 11 | #ifndef _FILE_H_ |
| | 12 | #define _FILE_H_ |
| | 13 | |
| | 14 | /* include our base class definition */ |
| | 15 | #include "systype.h" |
| | 16 | |
| | 17 | /* |
| | 18 | * File methods use the CharacterSet and ByteArray intrinsic |
| | 19 | * classes, so include their headers to make sure they're available to |
| | 20 | * File users. |
| | 21 | */ |
| | 22 | #include "charset.h" |
| | 23 | #include "bytearr.h" |
| | 24 | |
| | 25 | |
| | 26 | /* ------------------------------------------------------------------------ */ |
| | 27 | /* |
| | 28 | * File access modes. These are used when calling the file open methods |
| | 29 | * to specify how the file is to be accessed. |
| | 30 | */ |
| | 31 | |
| | 32 | /* |
| | 33 | * Read mode - the file is opened for reading (writing is not allowed). |
| | 34 | * When opened in this mode, the file must exist, or a |
| | 35 | * FileNotFoundException is thrown from the open method. |
| | 36 | */ |
| | 37 | #define FileAccessRead 0x0001 |
| | 38 | |
| | 39 | /* |
| | 40 | * Write mode - the file is opened for writing (reading is not allowed). |
| | 41 | * When opened in this mode, if the file doesn't already exist, a new file |
| | 42 | * is created; if the file does already exist, the existing data in the |
| | 43 | * file are discarded (i.e., the file is truncated to zero length) on |
| | 44 | * open. |
| | 45 | */ |
| | 46 | #define FileAccessWrite 0x0002 |
| | 47 | |
| | 48 | /* |
| | 49 | * Read/write mode, keeping existing contents - the file is opened for |
| | 50 | * both reading and writing. If the file does not exist, a new file is |
| | 51 | * created. If the file does already exist, the existing contents of the |
| | 52 | * file are kept intact on open. |
| | 53 | */ |
| | 54 | #define FileAccessReadWriteKeep 0x0003 |
| | 55 | |
| | 56 | /* |
| | 57 | * Read/write mode, truncating existing contents - the file is opened for |
| | 58 | * both reading and writing. If the file does not exist, a new file is |
| | 59 | * created. If the file does already exist, the existing contents of the |
| | 60 | * file are discarded (i.e., the file is truncated to zero length) on |
| | 61 | * open. |
| | 62 | */ |
| | 63 | #define FileAccessReadWriteTrunc 0x0004 |
| | 64 | |
| | 65 | |
| | 66 | /* ------------------------------------------------------------------------ */ |
| | 67 | /* |
| | 68 | * Special file identifiers. These identifiers can be passed to the 'open' |
| | 69 | * routines in place of the filename string argument. |
| | 70 | * |
| | 71 | * The actual name and location of a special file is determined by the |
| | 72 | * interpreter. Since games use these internal identifiers rather than the |
| | 73 | * actual system filenames when accessing special files, different |
| | 74 | * interpreters can adapt to different local conventions without bothering |
| | 75 | * the game code with the details. The game code simply refers to the file |
| | 76 | * it wants using the virtual identifier, and the interpreter takes care of |
| | 77 | * the rest. |
| | 78 | * |
| | 79 | * Note that special files generally bypass the interpreter "file safety" |
| | 80 | * settings. This is important because it allows the library and games a |
| | 81 | * degree of controlled access to the file system, even when the file |
| | 82 | * safety settings wouldn't normally allow similar access for arbitrary |
| | 83 | * file operations. Even though this special file access can bypass the |
| | 84 | * file safety level, it doesn't compromise security, because the |
| | 85 | * interpreter has exclusive control over the names and locations of the |
| | 86 | * special files - thus a game can only access the particular files that |
| | 87 | * the interpreter designates as special, and can't use special files to |
| | 88 | * access arbitrary file system entities. |
| | 89 | */ |
| | 90 | |
| | 91 | /* |
| | 92 | * The library defaults file. This is the special file where the library |
| | 93 | * stores user-controlled start-up default settings. |
| | 94 | */ |
| | 95 | #define LibraryDefaultsFile 0x0001 |
| | 96 | |
| | 97 | |
| | 98 | /* ------------------------------------------------------------------------ */ |
| | 99 | /* |
| | 100 | * The File intrinsic class provides access to files in the external file |
| | 101 | * system. This lets you create, read, and write files. The class |
| | 102 | * supports text files (with translations to and from local character |
| | 103 | * sets), "data" files (using the special TADS 2 binary file format), and |
| | 104 | * "raw" files (this mode lets you manipulate files in arbitrary text or |
| | 105 | * binary formats by giving you direct access to the raw bytes in the |
| | 106 | * file). |
| | 107 | */ |
| | 108 | intrinsic class File 'file/030002': Object |
| | 109 | { |
| | 110 | /* |
| | 111 | * File has no constructors, so it is not possible to create a File |
| | 112 | * with the 'new' operator. To create a file, use one of the static |
| | 113 | * creator methods instead: |
| | 114 | * |
| | 115 | * f = File.openTextFile() |
| | 116 | * |
| | 117 | * All of the open methods throw exceptions if the open fails: |
| | 118 | * |
| | 119 | * FileNotFoundException - indicates that the requested file doesn't |
| | 120 | * exist. This is thrown when the access mode requires an existing |
| | 121 | * file but the named file does not exist. |
| | 122 | * |
| | 123 | * FileCreationException - indicates that the requested file could not |
| | 124 | * be created. This is thrown when the access mode requires creating |
| | 125 | * a new file but the named file cannot be created. |
| | 126 | * |
| | 127 | * FileOpenException - indicates that the requested file could not be |
| | 128 | * opened. This is thrown when the access mode allows either an |
| | 129 | * existing file to be opened or a new file to be created, but neither |
| | 130 | * could be accomplished. |
| | 131 | * |
| | 132 | * FileSafetyException - the requested access mode is not allowed for |
| | 133 | * the given file due to the current file safety level set by the |
| | 134 | * user. Users can set the file safety level (through command-line |
| | 135 | * switches or other preference mechanisms which vary by interpreter) |
| | 136 | * to restrict the types of file operations that applications are |
| | 137 | * allowed to perform, in order to protect their systems from |
| | 138 | * malicious programs. This exception indicates that the user has set |
| | 139 | * a safety level that is too restrictive for the requested operation. |
| | 140 | */ |
| | 141 | |
| | 142 | /* |
| | 143 | * Static creator method: open a text file. Returns a File object |
| | 144 | * that can be used to read or write the file. 'access' is the |
| | 145 | * read/write mode, and must be one of FileAccessRead or |
| | 146 | * FileAccessWrite. 'charset' is a CharacterSet object, or can |
| | 147 | * optionally be a string naming a character set, in which case a |
| | 148 | * CharacterSet object for the named character set will automatically |
| | 149 | * be created. If 'charset' is omitted, a default "us-ascii" |
| | 150 | * character set will be used. |
| | 151 | * |
| | 152 | * When a file is opened in text mode for reading, each call to |
| | 153 | * readFile() reads and returns a line of text from the file. When a |
| | 154 | * file is opened in text mode for writing, any existing file is |
| | 155 | * discarded and replaced with the new data. Each read and write to a |
| | 156 | * text file is mapped through the CharacterSet in effect at the time |
| | 157 | * of the read or write. |
| | 158 | */ |
| | 159 | static openTextFile(filename, access, charset?); |
| | 160 | |
| | 161 | /* |
| | 162 | * Static creator method: open a file in 'data' mode. Returns a File |
| | 163 | * object that can be used to read or write the file. 'access' |
| | 164 | * indicates the desired read/write access and the disposition of any |
| | 165 | * existing file; any of the FileAccessXxx modes can be used. |
| | 166 | * |
| | 167 | * When a file is opened in data mode, you can read and write |
| | 168 | * integers, strings, and 'true' values to the file, and the values in |
| | 169 | * the file are marked with their datatype in a private data format. |
| | 170 | * Because the file uses a tads-specific format, this mode cannot be |
| | 171 | * used to read files created by other applications or write files for |
| | 172 | * use by other applications; however, this storage format is |
| | 173 | * convenient for storing simple data values because the File object |
| | 174 | * takes care of converting to and from a portable binary format. |
| | 175 | */ |
| | 176 | static openDataFile(filename, access); |
| | 177 | |
| | 178 | /* |
| | 179 | * Static creator method: open a file in 'raw' mode. Returns a File |
| | 180 | * object that can be used to read or write the file. 'access' |
| | 181 | * indicates the desired read/write access mode and the disposition of |
| | 182 | * any existing file; any of the FileAccessXxx modes can be used. |
| | 183 | * |
| | 184 | * When a file is opened in raw mode, only ByteArray values can be |
| | 185 | * read and written. The File object performs no translations of the |
| | 186 | * bytes read or written. This mode requires the calling program |
| | 187 | * itself to perform all data conversions to and from a raw byte |
| | 188 | * format, but the benefit of this extra work is that this mode can be |
| | 189 | * used to read and write files in arbitrary data formats, including |
| | 190 | * formats defined by other applications. |
| | 191 | */ |
| | 192 | static openRawFile(filename, access); |
| | 193 | |
| | 194 | /* |
| | 195 | * get the CharacterSet object the File is currently using; returns |
| | 196 | * nil for a non-text file |
| | 197 | */ |
| | 198 | getCharacterSet(); |
| | 199 | |
| | 200 | /* |
| | 201 | * Set the CharacterSet object the File is to use from now on. This |
| | 202 | * is not meaningful except for text files. 'charset' must be a |
| | 203 | * CharacterSet object; in particular note that a character set name |
| | 204 | * given as a string is not allowed here. |
| | 205 | */ |
| | 206 | setCharacterSet(charset); |
| | 207 | |
| | 208 | /* |
| | 209 | * Close the file. Flushes any buffered information to the underlying |
| | 210 | * system file and releases any system resources (such as share locks |
| | 211 | * or system buffers) associated with the file. After this routine is |
| | 212 | * called, no further operations on the file can be performed (a |
| | 213 | * FileClosedException will be thrown if any subsequent operations are |
| | 214 | * attempted). |
| | 215 | * |
| | 216 | * It's not strictly necessary to call closeFile() on a File, since the |
| | 217 | * system will automatically do this work when the File object becomes |
| | 218 | * unreachable and is discarded by the garbage collector. However, it |
| | 219 | * is good practice to close a file explicitly by calling this method |
| | 220 | * as soon as the program reaches a point at which it knows it's done |
| | 221 | * with the file, because garbage collection might not run for a |
| | 222 | * significant amount of time after the program is actually done with |
| | 223 | * the file, in which case the system resources associated with the |
| | 224 | * file would be needlessly retained for this extended time. |
| | 225 | */ |
| | 226 | closeFile(); |
| | 227 | |
| | 228 | /* |
| | 229 | * Read from the file. Returns a data value that depends on the file |
| | 230 | * mode, as described below, or nil at end of file. |
| | 231 | * |
| | 232 | * If the file is open in text mode, this reads a line of text from the |
| | 233 | * file and returns a string with the text of the line read. A line of |
| | 234 | * text is a sequence of characters terminated with a line-ending |
| | 235 | * sequence, which is a carriage return, line feed, CR/LF pair, LF/CR |
| | 236 | * pair, or a Unicode line terminator character (0x2028) if the file is |
| | 237 | * being read with one of the Unicode encodings. If the line read ends |
| | 238 | * in a line-ending sequence, the returned text will end in a '\n' |
| | 239 | * character, regardless of which of the possible line-ending sequences |
| | 240 | * is actually in the file, so the caller need not worry about the |
| | 241 | * details of the external file's format. Every line read from the |
| | 242 | * file will end in a '\n' except possibly the last line - if the file |
| | 243 | * does not end with a line-ending sequence, then the last line read |
| | 244 | * from the file will not end in a '\n' character. All bytes read from |
| | 245 | * the file will be mapped to characters through the CharacterSet |
| | 246 | * object currently in effect in the file, so the returned string will |
| | 247 | * always be a standard Unicode string, regardless of the byte encoding |
| | 248 | * of the file. |
| | 249 | * |
| | 250 | * If the file is open in 'data' mode, this reads one data element |
| | 251 | * using the private tads-specific data format. The result is a value |
| | 252 | * of one of the types writable with writeFile() in 'data' mode. In |
| | 253 | * order to read a 'data' file, the file must have been previously |
| | 254 | * written in 'data' mode. |
| | 255 | */ |
| | 256 | readFile(); |
| | 257 | |
| | 258 | /* |
| | 259 | * Write to the file. Writes the given value to the file in a format |
| | 260 | * that depends on the file mode, as described below. No return |
| | 261 | * value; if an error occurs writing the data, this throws a |
| | 262 | * FileIOException. |
| | 263 | * |
| | 264 | * If the file is open in text mode, this writes text to the file, |
| | 265 | * converting the given value to a string if necessary (and throwing |
| | 266 | * an error if such a conversion is not possible), and translating the |
| | 267 | * string to be written to bytes by mapping the string through the |
| | 268 | * CharacterSet object currently in effect for the file. Note that no |
| | 269 | * line-ending characters are automatically added to the output, so if |
| | 270 | * the caller wishes to write line terminators, it should simply |
| | 271 | * include a '\n' character at the end of each line. |
| | 272 | * |
| | 273 | * If the file is open in 'data' mode, this writes the value, which |
| | 274 | * must be a string, integer, enum, or 'true' value, in a private |
| | 275 | * tads-specific data format that can later be read using the same |
| | 276 | * format. The values are converted to the private binary format, |
| | 277 | * which is portable across platforms: a file written in 'data' mode |
| | 278 | * on one machine can be copied (byte-for-byte) to another machine, |
| | 279 | * even one that uses different hardware and a different operating |
| | 280 | * system, and read back in 'data' mode on the new machine to yield |
| | 281 | * the original values written. |
| | 282 | */ |
| | 283 | writeFile(val); |
| | 284 | |
| | 285 | /* |
| | 286 | * Read bytes from the file into the given ByteArray object. This can |
| | 287 | * only be used for a file opened in 'raw' mode. If 'start' and 'cnt' |
| | 288 | * are given, they give the starting index in the byte array at which |
| | 289 | * the bytes read are to be stored, and the number of bytes to read, |
| | 290 | * respectively; if these are omitted, one byte is read from the file |
| | 291 | * for each byte in the byte array. |
| | 292 | * |
| | 293 | * Returns the number of bytes actually read into the byte array, |
| | 294 | * which will be less than or equal to the number requested. If the |
| | 295 | * number read is less than the number requested, it means that the |
| | 296 | * end of the file was encountered, and only the returned number of |
| | 297 | * bytes were available. |
| | 298 | */ |
| | 299 | readBytes(byteArr, start?, cnt?); |
| | 300 | |
| | 301 | /* |
| | 302 | * Write bytes from the ByteArray object into the file. This can only |
| | 303 | * be used for a file opened in 'raw' mode. If 'start' and 'cnt' are |
| | 304 | * given, they give the starting index in the byte array of the bytes |
| | 305 | * to be written, and the number of bytes to write, respectively; if |
| | 306 | * these are omitted, all of the bytes in the array are written. |
| | 307 | * |
| | 308 | * No return value; if an error occurs writing the data, a |
| | 309 | * FileIOException is thrown. |
| | 310 | */ |
| | 311 | writeBytes(byteArr, start?, cnt?); |
| | 312 | |
| | 313 | /* |
| | 314 | * Get the current read/write position in the file. Returns the byte |
| | 315 | * offset in the file of the next byte to be read or written. Note |
| | 316 | * that this value is an offset, so 0 is the offset of the first byte |
| | 317 | * in the file. |
| | 318 | */ |
| | 319 | getPos(); |
| | 320 | |
| | 321 | /* |
| | 322 | * Set the current read/write position in the file. 'pos' is a byte |
| | 323 | * offset in the file; 0 is the offset of the first byte. |
| | 324 | * |
| | 325 | * For files in 'text' and 'data' modes, a caller should NEVER set the |
| | 326 | * file position to any value other than a value previously returned |
| | 327 | * by getPos(), because other positions might violate the format |
| | 328 | * constraints. For example, if you move the file position to a byte |
| | 329 | * in the middle of a line-ending sequence in a text file, subsequent |
| | 330 | * reading from the file might misinterpret the sequence as something |
| | 331 | * other than a line ending, or as an extra line ending. If you move |
| | 332 | * the position in a 'data' file to a byte in the middle of an integer |
| | 333 | * value, reading from the file would misinterpret as a data type tag |
| | 334 | * a byte that is part of the integer value instead. So it is never |
| | 335 | * meaningful or safe to set an arbitrary byte offset in these file |
| | 336 | * formats; only values known to be valid by virtue of having been |
| | 337 | * returned from getPos() can be used here in these modes. |
| | 338 | */ |
| | 339 | setPos(pos); |
| | 340 | |
| | 341 | /* |
| | 342 | * Set the current read/write position to the end of the file. This |
| | 343 | * can be used, for example, to open a 'data' mode file for |
| | 344 | * read/write/keep access (keeping the contents of an existing file) |
| | 345 | * and then adding more data after all of the existing data in the |
| | 346 | * file. |
| | 347 | */ |
| | 348 | setPosEnd(); |
| | 349 | |
| | 350 | /* |
| | 351 | * Static creator method: open a resource in 'text' mode. This acts |
| | 352 | * like openTextFile(), but rather than opening an ordinary file, this |
| | 353 | * method opens a resource. Resources differ from ordinary files in |
| | 354 | * two important respects. First, a resource is named with a |
| | 355 | * URL-style path rather than a local file system name. Second, a |
| | 356 | * resource can be embedded in the program's executable (.t3) file, or |
| | 357 | * can be embedded in an external resource bundle (.3r0, etc) file. |
| | 358 | * |
| | 359 | * Resources are read-only, so the access mode is implicitly |
| | 360 | * FileAccessRead. |
| | 361 | */ |
| | 362 | static openTextResource(resname, charset?); |
| | 363 | |
| | 364 | /* |
| | 365 | * Static creator method: open a resource in 'raw' mode. This acts |
| | 366 | * like openRawFile(), but opens a resource rather than an ordinary |
| | 367 | * file. |
| | 368 | * |
| | 369 | * Resources are read-only, so the access mode is implicitly |
| | 370 | * FileAccessRead. |
| | 371 | */ |
| | 372 | static openRawResource(resname); |
| | 373 | |
| | 374 | /* get the size in bytes of the file */ |
| | 375 | getFileSize(); |
| | 376 | } |
| | 377 | |
| | 378 | #endif /* _FILE_H_ */ |