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