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