1 /* 2 * Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved. 3 * 4 * Please see the accompanying license file, LICENSE.TXT, for information 5 * on using and copying this software. 6 */ 7 /* 8 Name 9 vmfilobj.h - File object metaclass 10 Function 11 Implements an intrinsic class interface to operating system file I/O. 12 Notes 13 14 Modified 15 06/28/01 MJRoberts - Creation 16 */ 17 18 #ifndef VMFILOBJ_H 19 #define VMFILOBJ_H 20 21 #include "os.h" 22 #include "utf8.h" 23 #include "vmglob.h" 24 #include "vmobj.h" 25 #include "vmstr.h" 26 27 /* 28 * File intrinsic class. Our extension keeps track of our osfildef (our 29 * underlying native file handle), our character set object, and our flags. 30 * 31 * osfildef *fp 32 *. objid charset 33 *. byte mode (text/data/raw) 34 *. byte access (read/write/both) 35 *. uint32 flags 36 *. uint32 res_start 37 *. uint32 res_end 38 * 39 * The 'res_start' and 'res_end' fields are valid only for resource files. 40 * These give the starting seek position and ending seek position (which 41 * is the offset of the first byte AFTER THE END of the resource - in 42 * other words, the first invalid offset in the file) of the data of a 43 * resource, which might be embedded in a file containing other data. 44 * 45 * Our image file data leaves out the file handle, since it can't be 46 * loaded: 47 * 48 * objid charset 49 *. byte mode 50 *. byte access 51 *. uint32 flags 52 * 53 * An open file is inherently transient, so its state cannot be loaded, 54 * restored, or undone. 55 */ 56 57 class CVmObjFile: public CVmObject 58 { 59 friend class CVmMetaclassFile; 60 61 public: 62 /* metaclass registration object */ 63 static class CVmMetaclass *metaclass_reg_; get_metaclass_reg()64 class CVmMetaclass *get_metaclass_reg() const { return metaclass_reg_; } 65 66 /* am I of the given metaclass? */ is_of_metaclass(class CVmMetaclass * meta)67 virtual int is_of_metaclass(class CVmMetaclass *meta) const 68 { 69 /* try my own metaclass and my base class */ 70 return (meta == metaclass_reg_ 71 || CVmObject::is_of_metaclass(meta)); 72 } 73 74 /* create from stack arguments */ 75 static vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, 76 uint argc); 77 78 /* reserve constant data */ reserve_const_data(VMG_ class CVmConstMapper *,vm_obj_id_t)79 virtual void reserve_const_data(VMG_ class CVmConstMapper *, 80 vm_obj_id_t /*self*/) 81 { 82 /* we can't be converted to constant data */ 83 } 84 85 /* convert to constant data */ convert_to_const_data(VMG_ class CVmConstMapper *,vm_obj_id_t)86 virtual void convert_to_const_data(VMG_ class CVmConstMapper *, 87 vm_obj_id_t /*self*/) 88 { 89 /* 90 * we can't be converted to constant data and reference nothing 91 * that can be converted to constant data 92 */ 93 } 94 95 /* create an empty file object */ 96 static vm_obj_id_t create(VMG_ int in_root_set); 97 98 /* create with the given character set and file handle */ 99 static vm_obj_id_t create(VMG_ int in_root_set, 100 vm_obj_id_t charset, osfildef *fp, 101 unsigned long flags, int mode, int access, 102 int create_readbuf, 103 unsigned long res_start, unsigned long res_end); 104 105 /* notify of deletion */ 106 void notify_delete(VMG_ int in_root_set); 107 108 /* set a property */ 109 void set_prop(VMG_ class CVmUndo *undo, 110 vm_obj_id_t self, vm_prop_id_t prop, const vm_val_t *val); 111 112 /* call a static property */ 113 static int call_stat_prop(VMG_ vm_val_t *result, 114 const uchar **pc_ptr, uint *argc, 115 vm_prop_id_t prop); 116 117 /* undo operations */ notify_new_savept()118 void notify_new_savept() { } apply_undo(VMG_ struct CVmUndoRecord *)119 void apply_undo(VMG_ struct CVmUndoRecord *) { } 120 121 /* mark references */ mark_undo_ref(VMG_ struct CVmUndoRecord *)122 void mark_undo_ref(VMG_ struct CVmUndoRecord *) { } 123 void mark_refs(VMG_ uint state); 124 125 /* we keep no weak references */ remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *)126 void remove_stale_undo_weak_ref(VMG_ struct CVmUndoRecord *) { } remove_stale_weak_refs(VMG0_)127 void remove_stale_weak_refs(VMG0_) { } 128 129 /* load from an image file */ 130 void load_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); 131 132 /* reload from an image file (on restart) */ 133 void reload_from_image(VMG_ vm_obj_id_t, const char *ptr, size_t); 134 135 /* rebuild for image file */ 136 virtual ulong rebuild_image(VMG_ char *buf, ulong buflen); 137 138 /* save to a file */ 139 void save_to_file(VMG_ class CVmFile *fp); 140 141 /* restore from a file */ 142 void restore_from_file(VMG_ vm_obj_id_t self, 143 class CVmFile *fp, class CVmObjFixup *fixups); 144 145 /* evaluate a property */ 146 virtual int get_prop(VMG_ vm_prop_id_t prop, vm_val_t *val, 147 vm_obj_id_t self, vm_obj_id_t *source_obj, 148 uint *argc); 149 150 /* 151 * Check the safety settings to determine if an open is allowed on the 152 * given file with the given access mode. If the access is not 153 * allowed, we'll throw an error. 154 */ 155 static void check_safety_for_open(VMG_ const char *fname, int access); 156 157 protected: 158 /* create with no initial contents */ CVmObjFile()159 CVmObjFile() { ext_ = 0; } 160 161 /* create with the given character set and file object */ 162 CVmObjFile(VMG_ vm_obj_id_t charset, osfildef *fp, unsigned long flags, 163 int mode, int access, int create_readbuf, 164 unsigned long res_start, unsigned long res_end); 165 166 /* allocate our extension */ 167 void alloc_ext(VMG_ vm_obj_id_t charset, osfildef *fp, 168 unsigned long flags, int mode, int access, 169 int create_readbuf, 170 unsigned long res_start, unsigned long res_end); 171 172 /* load or reload data from the image file */ 173 void load_image_data(VMG_ const char *ptr, size_t siz); 174 175 /* property evaluator - undefined property */ getp_undef(VMG_ vm_obj_id_t,vm_val_t *,uint *)176 int getp_undef(VMG_ vm_obj_id_t, vm_val_t *, uint *) { return FALSE; } 177 178 /* property evaluator - get my character set */ 179 int getp_get_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 180 181 /* property evaluator - set my character set */ 182 int getp_set_charset(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 183 184 /* property evaluator - close the file */ 185 int getp_close_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 186 187 /* property evaluator - read from the file */ 188 int getp_read_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 189 190 /* property evaluator - write to the file */ 191 int getp_write_file(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 192 193 /* property evaluator - read bytes from the file */ 194 int getp_read_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 195 196 /* property evaluator - write bytes to the file */ 197 int getp_write_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 198 199 /* property evaluator - get seek position */ 200 int getp_get_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 201 202 /* property evaluator - set seek position */ 203 int getp_set_pos(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 204 205 /* property evaluator - set seek position to end of file */ 206 int getp_set_pos_end(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 207 208 /* property evaluator - openFileText */ getp_open_text(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)209 int getp_open_text(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) 210 { return s_getp_open_text(vmg_ retval, argc, FALSE); } 211 212 /* property evaluator - openFileData */ getp_open_data(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)213 int getp_open_data(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) 214 { return s_getp_open_data(vmg_ retval, argc); } 215 216 /* property evaluator - openFileRaw */ getp_open_raw(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)217 int getp_open_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) 218 { return s_getp_open_raw(vmg_ retval, argc, FALSE); } 219 220 /* property evaluator - openResourceText */ getp_open_res_text(VMG_ vm_obj_id_t self,vm_val_t * ret,uint * argc)221 int getp_open_res_text(VMG_ vm_obj_id_t self, vm_val_t *ret, uint *argc) 222 { return s_getp_open_text(vmg_ ret, argc, TRUE); } 223 224 /* property evaluator - get size */ 225 int getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *ret, uint *argc); 226 227 /* property evaluator - openResourceRaw */ getp_open_res_raw(VMG_ vm_obj_id_t self,vm_val_t * retval,uint * argc)228 int getp_open_res_raw(VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc) 229 { return s_getp_open_raw(vmg_ retval, argc, TRUE); } 230 231 232 /* static property evaluators for the openFileXxx creator methods */ 233 static int s_getp_open_text(VMG_ vm_val_t *retval, uint *argc, 234 int is_resource_file); 235 static int s_getp_open_data(VMG_ vm_val_t *retval, uint *argc); 236 static int s_getp_open_raw(VMG_ vm_val_t *retval, uint *argc, 237 int is_resource_file); 238 239 /* generic binary file opener - common to 'raw' and 'data' files */ 240 static int open_binary(VMG_ vm_val_t *retval, uint *argc, int mode, 241 int is_resource_file); 242 243 /* read a value in text/data mode */ 244 void read_text_mode(VMG_ vm_val_t *retval); 245 void read_data_mode(VMG_ vm_val_t *retval); 246 247 /* write a value in text/data mode */ 248 void write_text_mode(VMG_ const vm_val_t *val); 249 void write_data_mode(VMG_ const vm_val_t *val); 250 251 /* 252 * check to make sure we can perform operations on the file - if we're 253 * in the 'out of sync' state, we'll throw an exception 254 */ 255 void check_valid_file(VMG0_); 256 257 /* 258 * check that we have read or write access to the file - throws a mode 259 * exception if we don't have the requested access mode 260 */ 261 void check_read_access(VMG0_); 262 void check_write_access(VMG0_); 263 264 /* 265 * Note a file seek position change. 266 * 267 * 'is_explicit' indicates whether the seek is an explicit osfseek() 268 * operation or simply a change in position due to a read or write. If 269 * we're explicitly seeking to a new location with osfseek(), 270 * 'is_explicit' is true; if we're simply reading or writing, which 271 * affects the file position implicitly, 'is_explicit' is false. 272 */ 273 void note_file_seek(VMG_ vm_obj_id_t self, int is_explicit); 274 275 /* 276 * check for a switch between reading and writing, flushing our 277 * underlying stdio buffers if necessary 278 */ 279 void switch_read_write_mode(int writing); 280 281 /* get my extension, properly cast */ get_ext()282 struct vmobjfile_ext_t *get_ext() const 283 { return (struct vmobjfile_ext_t *)ext_; } 284 285 /* property evaluation function table */ 286 static int (CVmObjFile::*func_table_[])( 287 VMG_ vm_obj_id_t self, vm_val_t *retval, uint *argc); 288 }; 289 290 /* 291 * File object extension 292 */ 293 struct vmobjfile_ext_t 294 { 295 /* our native file handle */ 296 osfildef *fp; 297 298 /* our character set object */ 299 vm_obj_id_t charset; 300 301 /* format mode */ 302 unsigned char mode; 303 304 /* read/write access */ 305 unsigned char access; 306 307 /* flags */ 308 unsigned long flags; 309 310 /* read buffer, if we have one */ 311 struct vmobjfile_readbuf_t *readbuf; 312 313 /* 314 * Base offset of the data in the file. For a resource file, this 315 * gives the offset of the first byte of the resource data within the 316 * larger file containing the resource. For an ordinary file, this is 317 * always zero. 318 */ 319 unsigned long res_start; 320 321 /* 322 * Resource end offset. For a resource file, this is the offset of 323 * the first byte after the end of the resource data. For an ordinary 324 * file, this is not valid and must be ignored. 325 */ 326 unsigned long res_end; 327 }; 328 329 /* 330 * File object text read buffer. This is attached to the file extension 331 * if the file is opened in text mode with read access. 332 */ 333 struct vmobjfile_readbuf_t 334 { 335 /* current read pointer */ 336 utf8_ptr ptr; 337 338 /* bytes remaining in read buffer */ 339 size_t rem; 340 341 /* read buffer */ 342 char buf[512]; 343 344 /* refill the buffer, if it's empty */ 345 int refill(CCharmapToUni *charmap, osfildef *fp, 346 int is_res_file, unsigned long res_seek_end); 347 }; 348 349 /* 350 * file extension flags 351 */ 352 353 /* 354 * File out of sync. This is set when we load a File object from an image 355 * file; since the file cannot remain open across a preinit-save-load 356 * cycle, no operations can be performed on the File object after it's 357 * loaded from an image file. 358 */ 359 #define VMOBJFILE_OUT_OF_SYNC 0x0001 360 361 /* 362 * last operation left the underlying stdio buffers dirty, so we must take 363 * care to seek explicitly in the file if changing read/write mode on the 364 * next operation 365 */ 366 #define VMOBJFILE_STDIO_BUF_DIRTY 0x0002 367 368 /* 369 * last operation was 'write' - we must keep track of the last operation 370 * so that we will know to explicitly seek when switching between read and 371 * write operations 372 */ 373 #define VMOBJFILE_LAST_OP_WRITE 0x0004 374 375 /* 376 * This file is a resource. When this is set, the start and end seek 377 * positions are used to limit operations on the file to the resource's 378 * valid range within the OS-level file; this is necessary because the 379 * resource could be embedded in a larger file that contains other data. 380 */ 381 #define VMOBJFILE_IS_RESOURCE 0x0008 382 383 384 /* 385 * modes 386 */ 387 #define VMOBJFILE_MODE_TEXT 0x01 388 #define VMOBJFILE_MODE_DATA 0x02 389 #define VMOBJFILE_MODE_RAW 0x03 390 391 /* 392 * access types 393 */ 394 #define VMOBJFILE_ACCESS_READ 0x01 395 #define VMOBJFILE_ACCESS_WRITE 0x02 396 #define VMOBJFILE_ACCESS_RW_KEEP 0x03 397 #define VMOBJFILE_ACCESS_RW_TRUNC 0x04 398 399 400 401 /* ------------------------------------------------------------------------ */ 402 /* 403 * 'Data' mode type tags. Each item in a data-mode file is written with 404 * a byte giving the type tag, followed immediately by the data value. 405 * 406 * For compatibility with TADS 2 'binary' mode files, we use the same 407 * type tags that TADS 2 used for types in common. We are upwardly 408 * compatible with the TADS 2 format: we recognize all TADS 2 type tags, 409 * but we provide support for additional types that TADS 2 does not use. 410 * All of the tag values below 0x20 are compatible with TADS 2; those 411 * tag values 0x20 and above are not backwards compatible, so files that 412 * include these values will not be readable by TADS 2 programs. 413 */ 414 415 /* 416 * integer - 32-bit int in little-endian format (4 bytes) - TADS 2 417 * compatible 418 */ 419 #define VMOBJFILE_TAG_INT 0x01 420 421 /* 422 * string - 16-bit byte-length prefix in little-endian format followed 423 * by the bytes of the string, in UTF-8 format - TADS 2 compatible, 424 * although any string which includes characters outside of the ASCII 425 * set (Unicode code points 0 to 127 inclusive) will not be properly 426 * interpreted by TADS 2 programs. 427 * 428 * For compatibility with TADS 2, the length prefix includes in its byte 429 * count the two bytes of the length prefix itself, so this value is 430 * actually two higher than the number of bytes in the string proper. 431 */ 432 #define VMOBJFILE_TAG_STRING 0x03 433 434 /* true - no associated value - TADS 2 compatible */ 435 #define VMOBJFILE_TAG_TRUE 0x08 436 437 /* enum - 32-bit enum value in little-endian format */ 438 #define VMOBJFILE_TAG_ENUM 0x20 439 440 /* 441 * BigNumber - 16-bit length prefix in little-endian format followed by 442 * the bytes of the BigNumber 443 */ 444 #define VMOBJFILE_TAG_BIGNUM 0x21 445 446 /* 447 * ByteArray - 32-bit length prefix in little-endian format followed by 448 * the bytes of the byte array 449 */ 450 #define VMOBJFILE_TAG_BYTEARRAY 0x22 451 452 453 /* ------------------------------------------------------------------------ */ 454 /* 455 * Registration table object 456 */ 457 class CVmMetaclassFile: public CVmMetaclass 458 { 459 public: 460 /* get the global name */ get_meta_name()461 const char *get_meta_name() const { return "file/030002"; } 462 463 /* create from image file */ create_for_image_load(VMG_ vm_obj_id_t id)464 void create_for_image_load(VMG_ vm_obj_id_t id) 465 { 466 new (vmg_ id) CVmObjFile(); 467 G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); 468 } 469 470 /* create from restoring from saved state */ create_for_restore(VMG_ vm_obj_id_t id)471 void create_for_restore(VMG_ vm_obj_id_t id) 472 { 473 new (vmg_ id) CVmObjFile(); 474 G_obj_table->set_obj_gc_characteristics(id, TRUE, FALSE); 475 } 476 477 /* create dynamically using stack arguments */ create_from_stack(VMG_ const uchar ** pc_ptr,uint argc)478 vm_obj_id_t create_from_stack(VMG_ const uchar **pc_ptr, uint argc) 479 { return CVmObjFile::create_from_stack(vmg_ pc_ptr, argc); } 480 481 /* call a static property */ call_stat_prop(VMG_ vm_val_t * result,const uchar ** pc_ptr,uint * argc,vm_prop_id_t prop)482 int call_stat_prop(VMG_ vm_val_t *result, 483 const uchar **pc_ptr, uint *argc, 484 vm_prop_id_t prop) 485 { 486 return CVmObjFile::call_stat_prop(vmg_ result, pc_ptr, argc, prop); 487 } 488 }; 489 490 491 #endif /* VMFILOBJ_H */ 492 493 /* 494 * Register the class 495 */ 496 VM_REGISTER_METACLASS(CVmObjFile) 497