~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads3/vmfilobj.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
#include <assert.h>
 
19
#include "t3std.h"
 
20
#include "charmap.h"
 
21
#include "vmerr.h"
 
22
#include "vmerrnum.h"
 
23
#include "vmtype.h"
 
24
#include "vmbif.h"
 
25
#include "vmcset.h"
 
26
#include "vmstack.h"
 
27
#include "vmmeta.h"
 
28
#include "vmrun.h"
 
29
#include "vmglob.h"
 
30
#include "vmfile.h"
 
31
#include "vmobj.h"
 
32
#include "vmstr.h"
 
33
#include "vmfilobj.h"
 
34
#include "vmpredef.h"
 
35
#include "vmundo.h"
 
36
#include "vmbytarr.h"
 
37
#include "vmbignum.h"
 
38
#include "vmhost.h"
 
39
 
 
40
 
 
41
/* ------------------------------------------------------------------------ */
 
42
/*
 
43
 *   statics 
 
44
 */
 
45
 
 
46
/* metaclass registration object */
 
47
static CVmMetaclassFile metaclass_reg_obj;
 
48
CVmMetaclass *CVmObjFile::metaclass_reg_ = &metaclass_reg_obj;
 
49
 
 
50
/* function table */
 
51
int (CVmObjFile::
 
52
     *CVmObjFile::func_table_[])(VMG_ vm_obj_id_t self,
 
53
                                 vm_val_t *retval, uint *argc) =
 
54
{
 
55
    &CVmObjFile::getp_undef,
 
56
    &CVmObjFile::getp_open_text,
 
57
    &CVmObjFile::getp_open_data,
 
58
    &CVmObjFile::getp_open_raw,
 
59
    &CVmObjFile::getp_get_charset,
 
60
    &CVmObjFile::getp_set_charset,
 
61
    &CVmObjFile::getp_close_file,
 
62
    &CVmObjFile::getp_read_file,
 
63
    &CVmObjFile::getp_write_file,
 
64
    &CVmObjFile::getp_read_bytes,
 
65
    &CVmObjFile::getp_write_bytes,
 
66
    &CVmObjFile::getp_get_pos,
 
67
    &CVmObjFile::getp_set_pos,
 
68
    &CVmObjFile::getp_set_pos_end,
 
69
    &CVmObjFile::getp_open_res_text,
 
70
    &CVmObjFile::getp_open_res_raw,
 
71
    &CVmObjFile::getp_get_size
 
72
};
 
73
 
 
74
/*
 
75
 *   Vector indices - we only need to define these for the static functions,
 
76
 *   since we only need them to decode calls to call_stat_prop().  
 
77
 */
 
78
enum vmobjfil_meta_fnset
 
79
{
 
80
    VMOBJFILE_OPEN_TEXT = 1,
 
81
    VMOBJFILE_OPEN_DATA = 2,
 
82
    VMOBJFILE_OPEN_RAW = 3,
 
83
    VMOBJFILE_OPEN_RES_TEXT = 14,
 
84
    VMOBJFILE_OPEN_RES_RAW = 15
 
85
};
 
86
 
 
87
/* ------------------------------------------------------------------------ */
 
88
/*
 
89
 *   Special filename designators 
 
90
 */
 
91
 
 
92
/* library defaults file */
 
93
#define SFID_LIB_DEFAULTS    0x0001
 
94
 
 
95
/* ------------------------------------------------------------------------ */
 
96
/*
 
97
 *   Create from stack 
 
98
 */
 
99
vm_obj_id_t CVmObjFile::create_from_stack(VMG_ const uchar **pc_ptr,
 
100
                                          uint argc)
 
101
{
 
102
    /* 
 
103
     *   we can't be created with 'new' - we can only be created via our
 
104
     *   static creator methods (openTextFile, openDataFile, openRawFile) 
 
105
     */
 
106
    err_throw(VMERR_BAD_DYNAMIC_NEW);
 
107
 
 
108
    /* not reached, but the compiler might not know that */
 
109
    AFTER_ERR_THROW(return VM_INVALID_OBJ;)
 
110
}
 
111
 
 
112
/* ------------------------------------------------------------------------ */
 
113
/*
 
114
 *   Create with no contents 
 
115
 */
 
116
vm_obj_id_t CVmObjFile::create(VMG_ int in_root_set)
 
117
{
 
118
    vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
 
119
 
 
120
    /* instantiate the object */
 
121
    new (vmg_ id) CVmObjFile();
 
122
 
 
123
    /* files are always transient */
 
124
    G_obj_table->set_obj_transient(id);
 
125
 
 
126
    /* return the new ID */
 
127
    return id;
 
128
}
 
129
 
 
130
/*
 
131
 *   Create with the given character set object and file handle.  
 
132
 */
 
133
vm_obj_id_t CVmObjFile::create(VMG_ int in_root_set,
 
134
                               vm_obj_id_t charset, osfildef *fp,
 
135
                               unsigned long flags, int mode, int access,
 
136
                               int create_readbuf,
 
137
                               unsigned long res_start, unsigned long res_end)
 
138
{
 
139
    vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
 
140
 
 
141
    /* instantiate the object */
 
142
    new (vmg_ id) CVmObjFile(vmg_ charset, fp, flags, mode, access,
 
143
                             create_readbuf, res_start, res_end);
 
144
 
 
145
    /* files are always transient */
 
146
    G_obj_table->set_obj_transient(id);
 
147
 
 
148
    /* return the ID */
 
149
    return id;
 
150
}
 
151
 
 
152
/* ------------------------------------------------------------------------ */
 
153
/*
 
154
 *   Instantiate 
 
155
 */
 
156
CVmObjFile::CVmObjFile(VMG_ vm_obj_id_t charset, osfildef *fp,
 
157
                       unsigned long flags, int mode, int access,
 
158
                       int create_readbuf,
 
159
                       unsigned long res_start, unsigned long res_end)
 
160
{
 
161
    /* allocate and initialize our extension */
 
162
    ext_ = 0;
 
163
    alloc_ext(vmg_ charset, fp, flags, mode, access, create_readbuf,
 
164
              res_start, res_end);
 
165
}
 
166
 
 
167
/*
 
168
 *   Allocate and initialize our extension 
 
169
 */
 
170
void CVmObjFile::alloc_ext(VMG_ vm_obj_id_t charset, osfildef *fp,
 
171
                           unsigned long flags, int mode, int access,
 
172
                           int create_readbuf,
 
173
                           unsigned long res_start, unsigned long res_end)
 
174
{
 
175
    size_t siz;
 
176
 
 
177
    /* 
 
178
     *   if we already have an extension, delete it (and release our
 
179
     *   underlying system file, if any) 
 
180
     */
 
181
    notify_delete(vmg_ FALSE);
 
182
 
 
183
    /* 
 
184
     *   Figure the needed size.  We need at least the standard extension;
 
185
     *   if we also need a read buffer, figure in space for that as well. 
 
186
     */
 
187
    siz = sizeof(vmobjfile_ext_t);
 
188
    if (create_readbuf)
 
189
        siz += sizeof(vmobjfile_readbuf_t);
 
190
 
 
191
    /* allocate space for our extension structure */
 
192
    ext_ = (char *)G_mem->get_var_heap()->alloc_mem(siz, this);
 
193
 
 
194
    /* store the data we received from the caller */
 
195
    get_ext()->fp = fp;
 
196
    get_ext()->charset = charset;
 
197
    get_ext()->flags = flags;
 
198
    get_ext()->mode = (unsigned char)mode;
 
199
    get_ext()->access = (unsigned char)access;
 
200
    get_ext()->res_start = res_start;
 
201
    get_ext()->res_end = res_end;
 
202
 
 
203
    /* 
 
204
     *   point to our read buffer, for which we allocated space contiguously
 
205
     *   with and immediately following our extension, if we have one 
 
206
     */
 
207
    if (create_readbuf)
 
208
    {
 
209
        /* point to our read buffer object */
 
210
        get_ext()->readbuf = (vmobjfile_readbuf_t *)(get_ext() + 1);
 
211
 
 
212
        /* initialize the read buffer with no initial data */
 
213
        get_ext()->readbuf->rem = 0;
 
214
        get_ext()->readbuf->ptr.set(0);
 
215
    }
 
216
    else
 
217
    {
 
218
        /* there's no read buffer at all */
 
219
        get_ext()->readbuf = 0;
 
220
    }
 
221
}
 
222
 
 
223
/* ------------------------------------------------------------------------ */
 
224
/*
 
225
 *   Notify of deletion 
 
226
 */
 
227
void CVmObjFile::notify_delete(VMG_ int /*in_root_set*/)
 
228
{
 
229
    /* if we have an extension, clean it up */
 
230
    if (ext_ != 0)
 
231
    {
 
232
        /* close our file if we have one */
 
233
        if (get_ext()->fp != 0)
 
234
            osfcls(get_ext()->fp);
 
235
 
 
236
        /* free our extension */
 
237
        G_mem->get_var_heap()->free_mem(ext_);
 
238
    }
 
239
}
 
240
 
 
241
/* ------------------------------------------------------------------------ */
 
242
/* 
 
243
 *   set a property 
 
244
 */
 
245
void CVmObjFile::set_prop(VMG_ class CVmUndo *,
 
246
                             vm_obj_id_t, vm_prop_id_t,
 
247
                             const vm_val_t *)
 
248
{
 
249
    err_throw(VMERR_INVALID_SETPROP);
 
250
}
 
251
 
 
252
/* ------------------------------------------------------------------------ */
 
253
/* 
 
254
 *   get a property 
 
255
 */
 
256
int CVmObjFile::get_prop(VMG_ vm_prop_id_t prop, vm_val_t *retval,
 
257
                            vm_obj_id_t self, vm_obj_id_t *source_obj,
 
258
                            uint *argc)
 
259
{
 
260
    uint func_idx;
 
261
 
 
262
    /* translate the property index to an index into our function table */
 
263
    func_idx = G_meta_table
 
264
               ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop);
 
265
 
 
266
    /* call the appropriate function */
 
267
    if ((this->*func_table_[func_idx])(vmg_ self, retval, argc))
 
268
    {
 
269
        *source_obj = metaclass_reg_->get_class_obj(vmg0_);
 
270
        return TRUE;
 
271
    }
 
272
 
 
273
    /* inherit default handling */
 
274
    return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc);
 
275
}
 
276
 
 
277
/* 
 
278
 *   call a static property 
 
279
 */
 
280
int CVmObjFile::call_stat_prop(VMG_ vm_val_t *result,
 
281
                               const uchar **pc_ptr, uint *argc,
 
282
                               vm_prop_id_t prop)
 
283
{
 
284
    /* translate the property into a function vector index */
 
285
    switch(G_meta_table
 
286
           ->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop))
 
287
    {
 
288
    case VMOBJFILE_OPEN_TEXT:
 
289
        return s_getp_open_text(vmg_ result, argc, FALSE);
 
290
 
 
291
    case VMOBJFILE_OPEN_DATA:
 
292
        return s_getp_open_data(vmg_ result, argc);
 
293
 
 
294
    case VMOBJFILE_OPEN_RAW:
 
295
        return s_getp_open_raw(vmg_ result, argc, FALSE);
 
296
 
 
297
    case VMOBJFILE_OPEN_RES_TEXT:
 
298
        return s_getp_open_text(vmg_ result, argc, TRUE);
 
299
 
 
300
    case VMOBJFILE_OPEN_RES_RAW:
 
301
        return s_getp_open_raw(vmg_ result, argc, TRUE);
 
302
 
 
303
    default:
 
304
        /* it's not one of ours - inherit from the base object metaclass */
 
305
        return CVmObject::call_stat_prop(vmg_ result, pc_ptr, argc, prop);
 
306
    }
 
307
}
 
308
 
 
309
/* ------------------------------------------------------------------------ */
 
310
/*
 
311
 *   load from an image file 
 
312
 */
 
313
void CVmObjFile::load_from_image(VMG_ vm_obj_id_t self,
 
314
                                 const char *ptr, size_t siz)
 
315
{
 
316
    /* load from the image data */
 
317
    load_image_data(vmg_ ptr, siz);
 
318
 
 
319
    /* 
 
320
     *   save our image data pointer in the object table, so that we can
 
321
     *   access it (without storing it ourselves) during a reload 
 
322
     */
 
323
    G_obj_table->save_image_pointer(self, ptr, siz);
 
324
}
 
325
 
 
326
/*
 
327
 *   reload the object from image data 
 
328
 */
 
329
void CVmObjFile::reload_from_image(VMG_ vm_obj_id_t /*self*/,
 
330
                                   const char *ptr, size_t siz)
 
331
{
 
332
    /* load the image data */
 
333
    load_image_data(vmg_ ptr, siz);
 
334
}
 
335
 
 
336
/*
 
337
 *   load or re-load image data 
 
338
 */
 
339
void CVmObjFile::load_image_data(VMG_ const char *ptr, size_t siz)
 
340
{
 
341
    vm_obj_id_t charset;
 
342
    unsigned long flags;
 
343
    int mode;
 
344
    int access;
 
345
 
 
346
    /* read our character set */
 
347
    charset = vmb_get_objid(ptr);
 
348
    ptr += VMB_OBJECT_ID;
 
349
 
 
350
    /* get the mode and access values */
 
351
    mode = (unsigned char)*ptr++;
 
352
    access = (unsigned char)*ptr++;
 
353
 
 
354
    /* get the flags */
 
355
    flags = t3rp4u(ptr);
 
356
 
 
357
    /* 
 
358
     *   add in the out-of-sync flag, since we've restored the state from a
 
359
     *   past state and thus we're out of sync with the external file system
 
360
     *   environment 
 
361
     */
 
362
    flags |= VMOBJFILE_OUT_OF_SYNC;
 
363
 
 
364
    /* 
 
365
     *   Initialize our extension - we have no underlying native file
 
366
     *   handle, since the file is out of sync by virtue of being loaded
 
367
     *   from a previously saved image state.  Note that we don't need a
 
368
     *   read buffer because the file is inherently out of sync and thus
 
369
     *   cannot be read.  
 
370
     */
 
371
    alloc_ext(vmg_ charset, 0, flags, mode, access, FALSE, 0, 0);
 
372
}
 
373
 
 
374
/* ------------------------------------------------------------------------ */
 
375
/* 
 
376
 *   save to a file 
 
377
 */
 
378
void CVmObjFile::save_to_file(VMG_ class CVmFile *fp)
 
379
{
 
380
    /* files are always transient, so should never be saved */
 
381
    assert(FALSE);
 
382
}
 
383
 
 
384
/* 
 
385
 *   restore from a file 
 
386
 */
 
387
void CVmObjFile::restore_from_file(VMG_ vm_obj_id_t self,
 
388
                                   CVmFile *fp, CVmObjFixup *)
 
389
{
 
390
    /* files are always transient, so should never be savd */
 
391
}
 
392
 
 
393
/* ------------------------------------------------------------------------ */
 
394
/*
 
395
 *   Mark as referenced all of the objects to which we refer 
 
396
 */
 
397
void CVmObjFile::mark_refs(VMG_ uint state)
 
398
{
 
399
    /* mark our character set object, if we have one */
 
400
    if (get_ext()->charset != VM_INVALID_OBJ)
 
401
        G_obj_table->mark_all_refs(get_ext()->charset, state);
 
402
}
 
403
 
 
404
/* ------------------------------------------------------------------------ */
 
405
/*
 
406
 *   Note that we're seeking within the file.
 
407
 */
 
408
void CVmObjFile::note_file_seek(VMG_ vm_obj_id_t self, int is_explicit)
 
409
{
 
410
    /* 
 
411
     *   if it's an explicit seek, invalidate our internal read buffer and
 
412
     *   note that the stdio buffers have been invalidated 
 
413
     */
 
414
    if (is_explicit)
 
415
    {
 
416
        /* the read buffer (if we have one) is invalid after a seek */
 
417
        if (get_ext()->readbuf != 0)
 
418
            get_ext()->readbuf->rem = 0;
 
419
 
 
420
        /* 
 
421
         *   mark the last operation as clearing stdio buffering - when we
 
422
         *   explicitly seek, stdio automatically invalidates its internal
 
423
         *   buffers 
 
424
         */
 
425
        get_ext()->flags &= ~VMOBJFILE_STDIO_BUF_DIRTY;
 
426
    }
 
427
}
 
428
 
 
429
/*
 
430
 *   Update stdio buffering for a switch between read and write mode, if
 
431
 *   necessary.  Any time we perform consecutive read and write operations,
 
432
 *   stdio requires us to explicitly seek when performing consecutive
 
433
 *   dissimilar operations.  In other words, if we read then write, we must
 
434
 *   seek before the write, and likewise if we write then read.  
 
435
 */
 
436
void CVmObjFile::switch_read_write_mode(int writing)
 
437
{
 
438
    /* if we're writing, invalidate the read buffer */
 
439
    if (writing && get_ext()->readbuf != 0)
 
440
        get_ext()->readbuf->rem = 0;
 
441
 
 
442
    /* 
 
443
     *   if we just performed a read or write operation, we must seek if
 
444
     *   we're performing the opposite type of operation now 
 
445
     */
 
446
    if ((get_ext()->flags & VMOBJFILE_STDIO_BUF_DIRTY) != 0)
 
447
    {
 
448
        int was_writing;
 
449
 
 
450
        /* check what type of operation we did last */
 
451
        was_writing = ((get_ext()->flags & VMOBJFILE_LAST_OP_WRITE) != 0);
 
452
 
 
453
        /* 
 
454
         *   if we're switching operations, explicitly seek to the current
 
455
         *   location to flush the stdio buffers 
 
456
         */
 
457
        if ((writing && !was_writing) || (!writing && was_writing))
 
458
            osfseek(get_ext()->fp, osfpos(get_ext()->fp), OSFSK_SET);
 
459
    }
 
460
 
 
461
    /* 
 
462
     *   remember that this operation is stdio-buffered, so that we'll know
 
463
     *   we need to seek if we perform the opposite type of application
 
464
     *   after this one 
 
465
     */
 
466
    get_ext()->flags |= VMOBJFILE_STDIO_BUF_DIRTY;
 
467
 
 
468
    /* remember which type of operation we're performing */
 
469
    if (writing)
 
470
        get_ext()->flags |= VMOBJFILE_LAST_OP_WRITE;
 
471
    else
 
472
        get_ext()->flags &= ~VMOBJFILE_LAST_OP_WRITE;
 
473
}
 
474
 
 
475
/* ------------------------------------------------------------------------ */
 
476
/*
 
477
 *   Retrieve the filename and access mode arguments. 
 
478
 */
 
479
void CVmObjFile::get_filename_and_access(VMG_ char *fname, size_t fname_siz,
 
480
                                         int *access, int is_resource_file)
 
481
{
 
482
    int is_special_file = FALSE;
 
483
    
 
484
    /* 
 
485
     *   check to see if we have an explicit filename string, or an integer
 
486
     *   giving a special system file ID 
 
487
     */
 
488
    if (G_stk->get(0)->typ == VM_INT)
 
489
    {
 
490
        char path[OSFNMAX];
 
491
 
 
492
        /* start with no path, in case we have trouble retrieving it */
 
493
        path[0] = '\0';
 
494
        
 
495
        /* we have an integer, which is a special file designator */
 
496
        switch (CVmBif::pop_int_val(vmg0_))
 
497
        {
 
498
        case SFID_LIB_DEFAULTS:
 
499
            /* get the system application data path */
 
500
            G_host_ifc->get_special_file_path(path, sizeof(path),
 
501
                                              OS_GSP_T3_APP_DATA);
 
502
 
 
503
            /* add the filename */
 
504
            os_build_full_path(fname, fname_siz, path, "settings.txt");
 
505
            break;
 
506
 
 
507
        default:
 
508
            /* invalid filename value */
 
509
            err_throw(VMERR_BAD_VAL_BIF);
 
510
        }
 
511
 
 
512
        /* note that we have a special file, for file safety purposes */
 
513
        is_special_file = TRUE;
 
514
    }
 
515
    else
 
516
    {
 
517
        /* we must have an explicit filename string - pop it */
 
518
        CVmBif::pop_str_val_fname(vmg_ fname, fname_siz);
 
519
    }
 
520
 
 
521
    /* 
 
522
     *   retrieve the access mode; if it's a resource file, the mode is
 
523
     *   implicitly 'read' 
 
524
     */
 
525
    if (is_resource_file)
 
526
        *access = VMOBJFILE_ACCESS_READ;
 
527
    else
 
528
        *access = CVmBif::pop_int_val(vmg0_);
 
529
 
 
530
    /*
 
531
     *   If this isn't a special file, then check the file safety mode to
 
532
     *   ensure this operation is allowed.  Reading resources is always
 
533
     *   allowed, regardless of the safety mode, since resources are
 
534
     *   read-only and are inherently constrained in the paths they can
 
535
     *   access.  Likewise, special files bypass the safety settings, because
 
536
     *   the interpreter controls the names and locations of these files,
 
537
     *   ensuring that they're inherently safe.  
 
538
     */
 
539
    if (!is_resource_file && !is_special_file)
 
540
        check_safety_for_open(vmg_ fname, *access);
 
541
}
 
542
 
 
543
/* ------------------------------------------------------------------------ */
 
544
/*
 
545
 *   Static property evaluator - open a text file 
 
546
 */
 
547
int CVmObjFile::s_getp_open_text(VMG_ vm_val_t *retval, uint *in_argc,
 
548
                                 int is_resource_file)
 
549
{
 
550
    uint argc = (in_argc != 0 ? *in_argc : 0);
 
551
    static CVmNativeCodeDesc desc_file(2, 1);
 
552
    static CVmNativeCodeDesc desc_res(1, 1);
 
553
    char fname[OSFNMAX];
 
554
    int access;
 
555
    vm_obj_id_t cset_obj;
 
556
    osfildef *fp;
 
557
    int create_readbuf;
 
558
    unsigned long res_start;
 
559
    unsigned long res_end;
 
560
    unsigned int flags;
 
561
 
 
562
    /* check arguments */
 
563
    if (get_prop_check_argc(retval, in_argc,
 
564
                            is_resource_file ? &desc_res : &desc_file))
 
565
        return TRUE;
 
566
 
 
567
    /* initialize the flags to indicate a text-mode file */
 
568
    flags = 0;
 
569
 
 
570
    /* add the resource-file flag if appropriate */
 
571
    if (is_resource_file)
 
572
        flags |= VMOBJFILE_IS_RESOURCE;
 
573
 
 
574
    /* presume we can use the entire file */
 
575
    res_start = 0;
 
576
    res_end = 0;
 
577
 
 
578
    /* retrieve the filename */
 
579
    get_filename_and_access(vmg_ fname, sizeof(fname),
 
580
                            &access, is_resource_file);
 
581
 
 
582
    /* presume we won't need a read buffer */
 
583
    create_readbuf = FALSE;
 
584
 
 
585
    /* if there's a character set name or object, retrieve it */
 
586
    if (argc > 2)
 
587
    {
 
588
        /* 
 
589
         *   check to see if it's a CharacterSet object; if it's not, it
 
590
         *   must be a string giving the character set name 
 
591
         */
 
592
        if (G_stk->get(0)->typ == VM_OBJ
 
593
            && CVmObjCharSet::is_charset(vmg_ G_stk->get(0)->val.obj))
 
594
        {
 
595
            /* retrieve the CharacterSet reference */
 
596
            cset_obj = CVmBif::pop_obj_val(vmg0_);
 
597
        }
 
598
        else
 
599
        {
 
600
            const char *str;
 
601
            size_t len;
 
602
            
 
603
            /* it's not a CharacterSet, so it must be a character set name */
 
604
            str = G_stk->get(0)->get_as_string(vmg0_);
 
605
            if (str == 0)
 
606
                err_throw(VMERR_BAD_TYPE_BIF);
 
607
 
 
608
            /* get the length and skip the length prefix */
 
609
            len = vmb_get_len(str);
 
610
            str += VMB_LEN;
 
611
 
 
612
            /* create a mapper for the given name */
 
613
            cset_obj = CVmObjCharSet::create(vmg_ FALSE, str, len);
 
614
        }
 
615
    }
 
616
    else
 
617
    {
 
618
        /* no character set is specified - use US-ASCII by default */
 
619
        cset_obj = CVmObjCharSet::create(vmg_ FALSE, "us-ascii", 8);
 
620
    }
 
621
 
 
622
    /* push the character map object onto the stack for gc protection */
 
623
    G_stk->push()->set_obj(cset_obj);
 
624
 
 
625
    /* open the file for reading or writing, as appropriate */
 
626
    switch(access)
 
627
    {
 
628
    case VMOBJFILE_ACCESS_READ:
 
629
        /* open a resource file or file system file, as appropriate */
 
630
        if (is_resource_file)
 
631
        {
 
632
            unsigned long res_len;
 
633
            
 
634
            /* it's a resource - open it */
 
635
            fp = G_host_ifc->find_resource(fname, strlen(fname), &res_len);
 
636
 
 
637
            /* 
 
638
             *   if we found the resource, note the start and end seek
 
639
             *   positions, so we can limit reading of the underlying file
 
640
             *   to the section that contains the resource data 
 
641
             */
 
642
            if (fp != 0)
 
643
            {
 
644
                /* the file is initially at the start of the resource data */
 
645
                res_start = osfpos(fp);
 
646
 
 
647
                /* note the offset of the first byte after the resource */
 
648
                res_end = res_start + res_len;
 
649
            }
 
650
        }
 
651
        else
 
652
        {
 
653
            /* 
 
654
             *   Not a resource - open an ordinary text file for reading.
 
655
             *   Even though we're going to treat the file as a text file,
 
656
             *   open it in binary mode, since we'll do our own universal
 
657
             *   newline translations; this allows us to work with files in
 
658
             *   any character set, and using almost any newline
 
659
             *   conventions, so files copied from other systems will be
 
660
             *   fully usable even if they haven't been fixed up to local
 
661
             *   conventions.  
 
662
             */
 
663
            fp = osfoprb(fname, OSFTTEXT);
 
664
        }
 
665
 
 
666
        /* make sure we opened it successfully */
 
667
        if (fp == 0)
 
668
            G_interpreter->throw_new_class(vmg_ G_predef->file_not_found_exc,
 
669
                                           0, "file not found");
 
670
 
 
671
        /* we need a read buffer */
 
672
        create_readbuf = TRUE;
 
673
        break;
 
674
 
 
675
    case VMOBJFILE_ACCESS_WRITE:
 
676
        /* open for writing */
 
677
        fp = osfopwb(fname, OSFTTEXT);
 
678
 
 
679
        /* make sure we created it successfully */
 
680
        if (fp == 0)
 
681
            G_interpreter->throw_new_class(vmg_ G_predef->file_creation_exc,
 
682
                                           0, "error creating file");
 
683
        break;
 
684
 
 
685
    case VMOBJFILE_ACCESS_RW_KEEP:
 
686
        /* open for read/write, keeping existing contents */
 
687
        fp = osfoprwb(fname, OSFTTEXT);
 
688
 
 
689
        /* make sure we were able to find or create the file */
 
690
        if (fp == 0)
 
691
            G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
 
692
                                           0, "error opening file");
 
693
        break;
 
694
 
 
695
    case VMOBJFILE_ACCESS_RW_TRUNC:
 
696
        /* open for read/write, truncating existing contents */
 
697
        fp = osfoprwtb(fname, OSFTTEXT);
 
698
 
 
699
        /* make sure we were successful */
 
700
        if (fp == 0)
 
701
            G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
 
702
                                           0, "error opening file");
 
703
        break;
 
704
 
 
705
    default:
 
706
        fp = 0;
 
707
        err_throw(VMERR_BAD_VAL_BIF);
 
708
    }
 
709
 
 
710
    /* create our file object */
 
711
    retval->set_obj(create(vmg_ FALSE, cset_obj, fp, flags,
 
712
                           VMOBJFILE_MODE_TEXT, access, create_readbuf,
 
713
                           res_start, res_end));
 
714
 
 
715
    /* discard gc protection */
 
716
    G_stk->discard();
 
717
 
 
718
    /* handled */
 
719
    return TRUE;
 
720
}
 
721
 
 
722
/*
 
723
 *   Static property evaluator - open a data file
 
724
 */
 
725
int CVmObjFile::s_getp_open_data(VMG_ vm_val_t *retval, uint *argc)
 
726
{
 
727
    /* use the generic binary file opener in 'data' mode */
 
728
    return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_DATA, FALSE);
 
729
}
 
730
 
 
731
/*
 
732
 *   Static property evaluator - open a raw file
 
733
 */
 
734
int CVmObjFile::s_getp_open_raw(VMG_ vm_val_t *retval, uint *argc,
 
735
                                int is_resource_file)
 
736
{
 
737
    /* use the generic binary file opener in 'raw' mode */
 
738
    return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_RAW,
 
739
                       is_resource_file);
 
740
}
 
741
 
 
742
/*
 
743
 *   Generic binary file opener - common to 'data' and 'raw' files 
 
744
 */
 
745
int CVmObjFile::open_binary(VMG_ vm_val_t *retval, uint *argc, int mode,
 
746
                            int is_resource_file)
 
747
{
 
748
    static CVmNativeCodeDesc file_desc(2);
 
749
    static CVmNativeCodeDesc res_desc(1);
 
750
    char fname[OSFNMAX];
 
751
    int access;
 
752
    osfildef *fp;
 
753
    unsigned long res_start;
 
754
    unsigned long res_end;
 
755
    unsigned int flags;
 
756
 
 
757
    /* check arguments */
 
758
    if (get_prop_check_argc(retval, argc,
 
759
                            is_resource_file ? &res_desc : &file_desc))
 
760
        return TRUE;
 
761
 
 
762
    /* initialize the flags */
 
763
    flags = 0;
 
764
 
 
765
    /* set the resource-file flag, if appropriate */
 
766
    if (is_resource_file)
 
767
        flags |= VMOBJFILE_IS_RESOURCE;
 
768
 
 
769
    /* presume we can use the entire file */
 
770
    res_start = 0;
 
771
    res_end = 0;
 
772
 
 
773
    /* retrieve the filename and access mode */
 
774
    get_filename_and_access(vmg_ fname, sizeof(fname),
 
775
                            &access, is_resource_file);
 
776
 
 
777
    /* open the file in binary mode, with the desired access type */
 
778
    switch(access)
 
779
    {
 
780
    case VMOBJFILE_ACCESS_READ:
 
781
        /* open the resource or ordinary file, as appropriate */
 
782
        if (is_resource_file)
 
783
        {
 
784
            unsigned long res_len;
 
785
 
 
786
            /* it's a resource - open it */
 
787
            fp = G_host_ifc->find_resource(fname, strlen(fname), &res_len);
 
788
 
 
789
            /* 
 
790
             *   if we found the resource, note the start and end seek
 
791
             *   positions, so we can limit reading of the underlying file
 
792
             *   to the section that contains the resource data 
 
793
             */
 
794
            if (fp != 0)
 
795
            {
 
796
                /* the file is initially at the start of the resource data */
 
797
                res_start = osfpos(fp);
 
798
 
 
799
                /* note the offset of the first byte after the resource */
 
800
                res_end = res_start + res_len;
 
801
            }
 
802
        }
 
803
        else
 
804
        {
 
805
            /* open the ordinary file in binary mode for reading only */
 
806
            fp = osfoprb(fname, OSFTBIN);
 
807
        }
 
808
 
 
809
        /* make sure we were able to find it and open it */
 
810
        if (fp == 0)
 
811
            G_interpreter->throw_new_class(vmg_ G_predef->file_not_found_exc,
 
812
                                           0, "file not found");
 
813
        break;
 
814
 
 
815
    case VMOBJFILE_ACCESS_WRITE:
 
816
        /* open in binary mode for writing only */
 
817
        fp = osfopwb(fname, OSFTBIN);
 
818
 
 
819
        /* make sure we were able to create the file successfully */
 
820
        if (fp == 0)
 
821
            G_interpreter->throw_new_class(vmg_ G_predef->file_creation_exc,
 
822
                                           0, "error creating file");
 
823
        break;
 
824
 
 
825
    case VMOBJFILE_ACCESS_RW_KEEP:
 
826
        /* open for read/write, keeping existing contents */
 
827
        fp = osfoprwb(fname, OSFTBIN);
 
828
 
 
829
        /* make sure we were able to find or create the file */
 
830
        if (fp == 0)
 
831
            G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
 
832
                                           0, "error opening file");
 
833
        break;
 
834
 
 
835
    case VMOBJFILE_ACCESS_RW_TRUNC:
 
836
        /* open for read/write, truncating existing contents */
 
837
        fp = osfoprwtb(fname, OSFTBIN);
 
838
 
 
839
        /* make sure we were successful */
 
840
        if (fp == 0)
 
841
            G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
 
842
                                           0, "error opening file");
 
843
        break;
 
844
 
 
845
    default:
 
846
        fp = 0;
 
847
        err_throw(VMERR_BAD_VAL_BIF);
 
848
    }
 
849
 
 
850
    /* create our file object */
 
851
    retval->set_obj(create(vmg_ FALSE, VM_INVALID_OBJ, fp,
 
852
                           flags, mode, access, FALSE, res_start, res_end));
 
853
 
 
854
    /* handled */
 
855
    return TRUE;
 
856
}
 
857
 
 
858
/* ------------------------------------------------------------------------ */
 
859
/*
 
860
 *   Check the safety settings to determine if an open operation is allowed.
 
861
 *   If the access is not allowed, we'll throw an error.  
 
862
 */
 
863
void CVmObjFile::check_safety_for_open(VMG_ const char *fname, int access)
 
864
{
 
865
    int safety;
 
866
    int in_same_dir;
 
867
    
 
868
    /* get the current file safety level from the host application */
 
869
    safety = G_host_ifc->get_io_safety();
 
870
 
 
871
    /* 
 
872
     *   Check to see if the file is in the current directory - if not, we
 
873
     *   may have to disallow the operation based on safety level settings.
 
874
     *   If the file has any sort of directory prefix, assume it's not in
 
875
     *   the same directory; if not, it must be.  This is actually overly
 
876
     *   conservative, since the path may be a relative path or even an
 
877
     *   absolute path that points to the current directory, but the
 
878
     *   important thing is whether we're allowing files to specify paths at
 
879
     *   all.  
 
880
     */
 
881
    in_same_dir = (os_get_root_name((char *)fname) == fname);
 
882
 
 
883
    /* check for conformance with the safety level setting */
 
884
    switch (access)
 
885
    {
 
886
    case VMOBJFILE_ACCESS_READ:
 
887
        /*
 
888
         *   we want only read access - we can't read at all if the safety
 
889
         *   level isn't READ_CUR or below, and we must be at level
 
890
         *   READ_ANY_WRITE_CUR or lower to read from a file not in the
 
891
         *   current directory 
 
892
         */
 
893
        if (safety > VM_IO_SAFETY_READ_CUR
 
894
            || (!in_same_dir && safety > VM_IO_SAFETY_READ_ANY_WRITE_CUR))
 
895
        {
 
896
            /* this operation is not allowed - throw an error */
 
897
            G_interpreter->throw_new_class(vmg_ G_predef->file_safety_exc,
 
898
                                           0, "prohibited file access");
 
899
        }
 
900
        break;
 
901
        
 
902
    case VMOBJFILE_ACCESS_WRITE:
 
903
    case VMOBJFILE_ACCESS_RW_KEEP:
 
904
    case VMOBJFILE_ACCESS_RW_TRUNC:
 
905
        /* 
 
906
         *   writing - we must be safety level of at least READWRITE_CUR to
 
907
         *   write at all, and we must be at level MINIMUM to write a file
 
908
         *   that's not in the current directory 
 
909
         */
 
910
        if (safety > VM_IO_SAFETY_READWRITE_CUR
 
911
            || (!in_same_dir && safety > VM_IO_SAFETY_MINIMUM))
 
912
        {
 
913
            /* this operation is not allowed - throw an error */
 
914
            G_interpreter->throw_new_class(vmg_ G_predef->file_safety_exc,
 
915
                                           0, "prohibited file access");
 
916
        }
 
917
        break;
 
918
    }
 
919
}
 
920
 
 
921
/* ------------------------------------------------------------------------ */
 
922
/*
 
923
 *   Property evaluator - get the character set 
 
924
 */
 
925
int CVmObjFile::getp_get_charset(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
926
                                 uint *argc)
 
927
{
 
928
    static CVmNativeCodeDesc desc(0);
 
929
 
 
930
    /* check arguments */
 
931
    if (get_prop_check_argc(retval, argc, &desc))
 
932
        return TRUE;
 
933
 
 
934
    /* if we have a character set object, return it; otherwise return nil */
 
935
    if (get_ext()->charset != VM_INVALID_OBJ)
 
936
        retval->set_obj(get_ext()->charset);
 
937
    else
 
938
        retval->set_nil();
 
939
 
 
940
    /* handled */
 
941
    return TRUE;
 
942
}
 
943
 
 
944
/* ------------------------------------------------------------------------ */
 
945
/*
 
946
 *   Property evaluator - set the character set 
 
947
 */
 
948
int CVmObjFile::getp_set_charset(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
949
                                 uint *argc)
 
950
{
 
951
    static CVmNativeCodeDesc desc(1);
 
952
 
 
953
    /* check arguments */
 
954
    if (get_prop_check_argc(retval, argc, &desc))
 
955
        return TRUE;
 
956
 
 
957
    /* make sure it's really a character set object */
 
958
    if (G_stk->get(0)->typ != VM_NIL
 
959
        && (G_stk->get(0)->typ != VM_OBJ
 
960
            || !CVmObjCharSet::is_charset(vmg_ G_stk->get(0)->val.obj)))
 
961
        err_throw(VMERR_BAD_TYPE_BIF);
 
962
 
 
963
    /* remember the new character set */
 
964
    if (G_stk->get(0)->typ == VM_NIL)
 
965
        get_ext()->charset = VM_INVALID_OBJ;
 
966
    else
 
967
        get_ext()->charset = G_stk->get(0)->val.obj;
 
968
 
 
969
    /* discard the argument */
 
970
    G_stk->discard();
 
971
 
 
972
    /* no return value */
 
973
    retval->set_nil();
 
974
 
 
975
    /* handled */
 
976
    return TRUE;
 
977
}
 
978
 
 
979
/* ------------------------------------------------------------------------ */
 
980
/*
 
981
 *   Check that we're in a valid state to perform file operations. 
 
982
 */
 
983
void CVmObjFile::check_valid_file(VMG0_)
 
984
{
 
985
    /* if we're 'out of sync', we can't perform any operations on it */
 
986
    if ((get_ext()->flags & VMOBJFILE_OUT_OF_SYNC) != 0)
 
987
        G_interpreter->throw_new_class(vmg_ G_predef->file_sync_exc,
 
988
                                       0, "file out of sync");
 
989
 
 
990
    /* if the file has been closed, we can't perform any operations on it */
 
991
    if (get_ext()->fp == 0)
 
992
        G_interpreter->throw_new_class(vmg_ G_predef->file_closed_exc, 0,
 
993
                                       "operation attempted on closed file");
 
994
}
 
995
 
 
996
/*
 
997
 *   Check that we have read access 
 
998
 */
 
999
void CVmObjFile::check_read_access(VMG0_)
 
1000
{
 
1001
    /* we have read access unless we're in write-only mode */
 
1002
    if (get_ext()->access == VMOBJFILE_ACCESS_WRITE)
 
1003
        G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0,
 
1004
                                       "wrong file mode");
 
1005
}
 
1006
 
 
1007
/*
 
1008
 *   Check that we have write access 
 
1009
 */
 
1010
void CVmObjFile::check_write_access(VMG0_)
 
1011
{
 
1012
    /* we have write access unless we're in read-only mode */
 
1013
    if (get_ext()->access == VMOBJFILE_ACCESS_READ)
 
1014
        G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc, 0,
 
1015
                                       "wrong file mode");
 
1016
}
 
1017
 
 
1018
 
 
1019
/* ------------------------------------------------------------------------ */
 
1020
/*
 
1021
 *   Property evaluator - close the file
 
1022
 */
 
1023
int CVmObjFile::getp_close_file(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1024
                                uint *argc)
 
1025
{
 
1026
    static CVmNativeCodeDesc desc(0);
 
1027
 
 
1028
    /* check arguments */
 
1029
    if (get_prop_check_argc(retval, argc, &desc))
 
1030
        return TRUE;
 
1031
 
 
1032
    /* make sure we are allowed to perform operations on the file */
 
1033
    check_valid_file(vmg0_);
 
1034
 
 
1035
    /* close the underlying system file */
 
1036
    osfcls(get_ext()->fp);
 
1037
 
 
1038
    /* forget the underlying system file, since it's no longer valid */
 
1039
    get_ext()->fp = 0;
 
1040
 
 
1041
    /* no return value */
 
1042
    retval->set_nil();
 
1043
 
 
1044
    /* handled */
 
1045
    return TRUE;
 
1046
}
 
1047
 
 
1048
/* ------------------------------------------------------------------------ */
 
1049
/*
 
1050
 *   Property evaluator - read from the file
 
1051
 */
 
1052
int CVmObjFile::getp_read_file(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1053
                               uint *argc)
 
1054
{
 
1055
    static CVmNativeCodeDesc desc(0);
 
1056
 
 
1057
    /* check arguments */
 
1058
    if (get_prop_check_argc(retval, argc, &desc))
 
1059
        return TRUE;
 
1060
 
 
1061
    /* push a self-reference for gc protection */
 
1062
    G_stk->push()->set_obj(self);
 
1063
 
 
1064
    /* make sure we are allowed to perform operations on the file */
 
1065
    check_valid_file(vmg0_);
 
1066
 
 
1067
    /* check that we have read access */
 
1068
    check_read_access(vmg0_);
 
1069
 
 
1070
    /* note the implicit seeking */
 
1071
    note_file_seek(vmg_ self, FALSE);
 
1072
 
 
1073
    /* flush stdio buffers as needed and note the read operation */
 
1074
    switch_read_write_mode(FALSE);
 
1075
 
 
1076
    /* read according to our mode */
 
1077
    switch(get_ext()->mode)
 
1078
    {
 
1079
    case VMOBJFILE_MODE_TEXT:
 
1080
        /* read a line of text */
 
1081
        read_text_mode(vmg_ retval);
 
1082
        break;
 
1083
 
 
1084
    case VMOBJFILE_MODE_DATA:
 
1085
        /* read in data mode */
 
1086
        read_data_mode(vmg_ retval);
 
1087
        break;
 
1088
 
 
1089
    case VMOBJFILE_MODE_RAW:
 
1090
        /* can't use this call on this type of file */
 
1091
        G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc,
 
1092
                                       0, "wrong file mode");
 
1093
        break;
 
1094
    }
 
1095
 
 
1096
    /* discard the gc protection */
 
1097
    G_stk->discard();
 
1098
 
 
1099
    /* handled */
 
1100
    return TRUE;
 
1101
}
 
1102
 
 
1103
/*
 
1104
 *   Read a value in text mode 
 
1105
 */
 
1106
void CVmObjFile::read_text_mode(VMG_ vm_val_t *retval)
 
1107
{
 
1108
    CVmObjString *str;
 
1109
    size_t str_len;
 
1110
    osfildef *fp = get_ext()->fp;
 
1111
    vmobjfile_readbuf_t *readbuf = get_ext()->readbuf;
 
1112
    CCharmapToUni *charmap;
 
1113
    int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
 
1114
 
 
1115
    /* we haven't yet constructed a string */
 
1116
    str = 0;
 
1117
    str_len = 0;
 
1118
 
 
1119
    /* get our character mapper */
 
1120
    charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset))
 
1121
              ->get_to_uni(vmg0_);
 
1122
 
 
1123
    /* assume we'll fail to read anything, in which case we'll return nil */
 
1124
    retval->set_nil();
 
1125
 
 
1126
    /* 
 
1127
     *   push the nil value - we'll always keep our intermediate value on
 
1128
     *   the stack so that the garbage collector will know it's referenced 
 
1129
     */
 
1130
    G_stk->push(retval);
 
1131
 
 
1132
    /*
 
1133
     *   Read a line of text from the file into our buffer.  Keep going
 
1134
     *   until we read an entire line; we might have to read the line in
 
1135
     *   chunks, since the line might end up being longer than our buffer.  
 
1136
     */
 
1137
    for (;;)
 
1138
    {
 
1139
        wchar_t found_nl;
 
1140
        char *start;
 
1141
        size_t new_len;
 
1142
        size_t nl_len;
 
1143
 
 
1144
        /* replenish the read buffer if it's empty */
 
1145
        if (readbuf->rem == 0
 
1146
            && !readbuf->refill(charmap, fp, is_res_file, get_ext()->res_end))
 
1147
            break;
 
1148
 
 
1149
        /* note where we started this chunk */
 
1150
        start = readbuf->ptr.getptr();
 
1151
 
 
1152
        /* scan for and remove any trailing newline */
 
1153
        for (found_nl = '\0' ; readbuf->rem != 0 ;
 
1154
             readbuf->ptr.inc(&readbuf->rem))
 
1155
        {
 
1156
            wchar_t cur;
 
1157
            
 
1158
            /* get the current character */
 
1159
            cur = readbuf->ptr.getch();
 
1160
 
 
1161
            /* 
 
1162
             *   check for a newline (note that 0x2028 is the unicode line
 
1163
             *   separator character) 
 
1164
             */
 
1165
            if (cur == '\n' || cur == '\r' || cur == 0x2028)
 
1166
            {
 
1167
                /* note the newline */
 
1168
                found_nl = cur;
 
1169
                
 
1170
                /* no need to look any further */
 
1171
                break;
 
1172
            }
 
1173
        }
 
1174
        
 
1175
        /* note the length of the current segment */
 
1176
        new_len = readbuf->ptr.getptr() - start;
 
1177
        
 
1178
        /* 
 
1179
         *   if there's a newline character, include an extra byte for the
 
1180
         *   '\n' we'll include in the result 
 
1181
         */
 
1182
        nl_len = (found_nl != '\0');
 
1183
        
 
1184
        /* 
 
1185
         *   If this is our first segment, construct a new string from this
 
1186
         *   chunk; otherwise, add to the existing string.
 
1187
         *   
 
1188
         *   Note that in either case, if we found a newline in the buffer,
 
1189
         *   we will NOT add the actual newline we found to the result
 
1190
         *   string.  Rather, we'll add a '\n' character to the result
 
1191
         *   string, no matter what kind of newline we found.  This ensures
 
1192
         *   that the data read uses a consistent format, regardless of the
 
1193
         *   local system convention where the file was created.  
 
1194
         */
 
1195
        if (str == 0)
 
1196
        {
 
1197
            /* create our first segment's string */
 
1198
            retval->set_obj(CVmObjString::
 
1199
                            create(vmg_ FALSE, new_len + nl_len));
 
1200
            str = (CVmObjString *)vm_objp(vmg_ retval->val.obj);
 
1201
            
 
1202
            /* copy the segment into the string object */
 
1203
            memcpy(str->cons_get_buf(), start, new_len);
 
1204
 
 
1205
            /* add a '\n' if we found a newline */
 
1206
            if (found_nl != '\0')
 
1207
                *(str->cons_get_buf() + new_len) = '\n';
 
1208
            
 
1209
            /* this is the length of the string so far */
 
1210
            str_len = new_len + nl_len;
 
1211
            
 
1212
            /* 
 
1213
             *   replace the stack placeholder with our string, so the
 
1214
             *   garbage collector will know it's still in use 
 
1215
             */
 
1216
            G_stk->discard();
 
1217
            G_stk->push(retval);
 
1218
        }
 
1219
        else
 
1220
        {
 
1221
            CVmObjString *new_str;
 
1222
            
 
1223
            /* 
 
1224
             *   create a new string to hold the contents of the old string
 
1225
             *   plus the new buffer 
 
1226
             */
 
1227
            retval->set_obj(CVmObjString::create(vmg_ FALSE,
 
1228
                str_len + new_len + nl_len));
 
1229
            new_str = (CVmObjString *)vm_objp(vmg_ retval->val.obj);
 
1230
            
 
1231
            /* copy the old string into the new string */
 
1232
            memcpy(new_str->cons_get_buf(),
 
1233
                   str->get_as_string(vmg0_) + VMB_LEN, str_len);
 
1234
 
 
1235
            /* add the new chunk after the copy of the old string */
 
1236
            memcpy(new_str->cons_get_buf() + str_len, start, new_len);
 
1237
 
 
1238
            /* add the newline if necessary */
 
1239
            if (found_nl != '\0')
 
1240
                *(new_str->cons_get_buf() + str_len + new_len) = '\n';
 
1241
            
 
1242
            /* the new string now replaces the old string */
 
1243
            str = new_str;
 
1244
            str_len += new_len + nl_len;
 
1245
            
 
1246
            /* 
 
1247
             *   replace our old intermediate value on the stack with the
 
1248
             *   new string - the old string isn't needed any more, so we
 
1249
             *   can leave it unreferenced, but we are still using the new
 
1250
             *   string 
 
1251
             */
 
1252
            G_stk->discard();
 
1253
            G_stk->push(retval);
 
1254
        }
 
1255
        
 
1256
        /* if we found a newline in this segment, we're done */
 
1257
        if (found_nl != '\0')
 
1258
        {
 
1259
            /* skip the newline in the input */
 
1260
            readbuf->ptr.inc(&readbuf->rem);
 
1261
 
 
1262
            /* replenish the read buffer if it's empty */
 
1263
            if (readbuf->rem == 0)
 
1264
                readbuf->refill(charmap, fp, is_res_file, get_ext()->res_end);
 
1265
 
 
1266
            /* 
 
1267
             *   check for a complementary newline character, for systems
 
1268
             *   that use \n\r or \r\n pairs 
 
1269
             */
 
1270
            if (readbuf->rem != 0)
 
1271
            {
 
1272
                wchar_t nxt;
 
1273
                
 
1274
                /* get the next character */
 
1275
                nxt = readbuf->ptr.getch();
 
1276
                
 
1277
                /* check for a complementary character */
 
1278
                if ((found_nl == '\n' && nxt == '\r')
 
1279
                    || (found_nl == '\r' && nxt == '\n'))
 
1280
                {
 
1281
                    /* 
 
1282
                     *   we have a pair sequence - skip the second character
 
1283
                     *   of the sequence 
 
1284
                     */
 
1285
                    readbuf->ptr.inc(&readbuf->rem);
 
1286
                }
 
1287
            }
 
1288
            
 
1289
            /* we've found the newline, so we're done with the string */
 
1290
            break;
 
1291
        }
 
1292
    }
 
1293
 
 
1294
    /* 
 
1295
     *   we now can discard the string we've been keeping on the stack to
 
1296
     *   for garbage collection protection 
 
1297
     */
 
1298
    G_stk->discard();
 
1299
}
 
1300
 
 
1301
/*
 
1302
 *   Read a value in 'data' mode 
 
1303
 */
 
1304
void CVmObjFile::read_data_mode(VMG_ vm_val_t *retval)
 
1305
{
 
1306
    char buf[32];
 
1307
    CVmObjString *str_obj;
 
1308
    vm_obj_id_t str_id;
 
1309
    osfildef *fp = get_ext()->fp;
 
1310
 
 
1311
    /* read the type flag */
 
1312
    if (osfrb(fp, buf, 1))
 
1313
    {
 
1314
        /* end of file - return nil */
 
1315
        retval->set_nil();
 
1316
        return;
 
1317
    }
 
1318
 
 
1319
    /* see what we have */
 
1320
    switch((vm_datatype_t)buf[0])
 
1321
    {
 
1322
    case VMOBJFILE_TAG_INT:
 
1323
        /* read the INT4 value */
 
1324
        if (osfrb(fp, buf, 4))
 
1325
            goto io_error;
 
1326
 
 
1327
        /* set the integer value from the buffer */
 
1328
        retval->set_int(osrp4(buf));
 
1329
        break;
 
1330
 
 
1331
    case VMOBJFILE_TAG_ENUM:
 
1332
        /* read the UINT4 value */
 
1333
        if (osfrb(fp, buf, 4))
 
1334
            goto io_error;
 
1335
 
 
1336
        /* set the 'enum' value */
 
1337
        retval->set_enum(t3rp4u(buf));
 
1338
        break;
 
1339
 
 
1340
    case VMOBJFILE_TAG_STRING:
 
1341
        /* 
 
1342
         *   read the string's length - note that this length is two
 
1343
         *   higher than the actual length of the string, because it
 
1344
         *   includes the length prefix bytes 
 
1345
         */
 
1346
        if (osfrb(fp, buf, 2))
 
1347
            goto io_error;
 
1348
 
 
1349
        /* 
 
1350
         *   allocate a new string of the required size (deducting two
 
1351
         *   bytes from the indicated size, since the string allocator
 
1352
         *   only wants to know about the bytes of the string we want to
 
1353
         *   store, not the length prefix part) 
 
1354
         */
 
1355
        str_id = CVmObjString::create(vmg_ FALSE, osrp2(buf) - 2);
 
1356
        str_obj = (CVmObjString *)vm_objp(vmg_ str_id);
 
1357
 
 
1358
        /* read the bytes of the string into the object's buffer */
 
1359
        if (osfrb(fp, str_obj->cons_get_buf(), osrp2(buf) - 2))
 
1360
            goto io_error;
 
1361
 
 
1362
        /* success - set the string return value, and we're done */
 
1363
        retval->set_obj(str_id);
 
1364
        break;
 
1365
 
 
1366
    case VMOBJFILE_TAG_TRUE:
 
1367
        /* it's a simple 'true' value */
 
1368
        retval->set_true();
 
1369
        break;
 
1370
 
 
1371
    case VMOBJFILE_TAG_BIGNUM:
 
1372
        /* read the BigNumber value and return a new BigNumber object */
 
1373
        if (CVmObjBigNum::read_from_data_file(vmg_ retval, fp))
 
1374
            goto io_error;
 
1375
        break;
 
1376
 
 
1377
    case VMOBJFILE_TAG_BYTEARRAY:
 
1378
        /* read the ByteArray value and return a new ByteArray object */
 
1379
        if (CVmObjByteArray::read_from_data_file(vmg_ retval, fp))
 
1380
            goto io_error;
 
1381
        break;
 
1382
 
 
1383
    default:
 
1384
        /* invalid data - throw an error */
 
1385
        G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
 
1386
                                       0, "file I/O error");
 
1387
    }
 
1388
 
 
1389
    /* done */
 
1390
    return;
 
1391
 
 
1392
io_error:
 
1393
    /* 
 
1394
     *   we'll come here if we read the type tag correctly but encounter
 
1395
     *   an I/O error reading the value - this indicates a corrupted input
 
1396
     *   stream, so throw an I/O error 
 
1397
     */
 
1398
    G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
 
1399
                                   0, "file I/O error");
 
1400
}
 
1401
 
 
1402
/* ------------------------------------------------------------------------ */
 
1403
/*
 
1404
 *   Property evaluator - write to the file
 
1405
 */
 
1406
int CVmObjFile::getp_write_file(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1407
                                uint *argc)
 
1408
{
 
1409
    static CVmNativeCodeDesc desc(1);
 
1410
    const vm_val_t *argval;
 
1411
 
 
1412
    /* check arguments */
 
1413
    if (get_prop_check_argc(retval, argc, &desc))
 
1414
        return TRUE;
 
1415
 
 
1416
    /* 
 
1417
     *   get a pointer to the argument value, but leave it on the stack
 
1418
     *   for now to protect against losing it in garbage collection 
 
1419
     */
 
1420
    argval = G_stk->get(0);
 
1421
 
 
1422
    /* push a self-reference for gc protection */
 
1423
    G_stk->push()->set_obj(self);
 
1424
 
 
1425
    /* make sure we are allowed to perform operations on the file */
 
1426
    check_valid_file(vmg0_);
 
1427
 
 
1428
    /* check that we have write access */
 
1429
    check_write_access(vmg0_);
 
1430
 
 
1431
    /* deal with stdio buffering if we're changing modes */
 
1432
    switch_read_write_mode(TRUE);
 
1433
 
 
1434
    /* read according to our mode */
 
1435
    switch(get_ext()->mode)
 
1436
    {
 
1437
    case VMOBJFILE_MODE_TEXT:
 
1438
        /* read a line of text */
 
1439
        write_text_mode(vmg_ argval);
 
1440
        break;
 
1441
 
 
1442
    case VMOBJFILE_MODE_DATA:
 
1443
        /* read in data mode */
 
1444
        write_data_mode(vmg_ argval);
 
1445
        break;
 
1446
 
 
1447
    case VMOBJFILE_MODE_RAW:
 
1448
        /* can't use this call on this type of file */
 
1449
        G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc,
 
1450
                                       0, "wrong file mode");
 
1451
        break;
 
1452
    }
 
1453
 
 
1454
    /* discard our gc protection and argument */
 
1455
    G_stk->discard(2);
 
1456
 
 
1457
    /* no return value - return nil by default */
 
1458
    retval->set_nil();
 
1459
    
 
1460
    /* handled */
 
1461
    return TRUE;
 
1462
}
 
1463
 
 
1464
/*
 
1465
 *   Write a value in text mode
 
1466
 */
 
1467
void CVmObjFile::write_text_mode(VMG_ const vm_val_t *val)
 
1468
{
 
1469
    char conv_buf[128];
 
1470
    vm_val_t new_str;
 
1471
    CCharmapToLocal *charmap;
 
1472
    const char *constp;
 
1473
    const char *p, *startp;
 
1474
    size_t rem;
 
1475
 
 
1476
    /* get our character mapper */
 
1477
    charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset))
 
1478
              ->get_to_local(vmg0_);
 
1479
    
 
1480
    /* convert the value to a string */
 
1481
    constp = CVmObjString::cvt_to_str(vmg_ &new_str,
 
1482
                                      conv_buf, sizeof(conv_buf),
 
1483
                                      val, 10);
 
1484
 
 
1485
    /* scan for newlines - we need to write newline sequences specially */
 
1486
    for (startp = constp + VMB_LEN, rem = vmb_get_len(constp) ;
 
1487
         rem != 0 ; )
 
1488
    {
 
1489
        /* scan to the next newline */
 
1490
        for (p = startp ; rem != 0 && *p != '\n' ; ++p, --rem) ;
 
1491
 
 
1492
        /* write this chunk through the character mapper */
 
1493
        if (p != startp
 
1494
            && charmap->write_file(get_ext()->fp, startp, p - startp))
 
1495
        {
 
1496
            /* the write failed - throw an I/O exception */
 
1497
            G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
 
1498
                                           0, "file I/O error");
 
1499
        }
 
1500
 
 
1501
        /* write the newline, if applicable */
 
1502
        if (rem != 0
 
1503
            && charmap->write_file(get_ext()->fp, OS_NEWLINE_SEQ,
 
1504
                                   strlen(OS_NEWLINE_SEQ)))
 
1505
        {
 
1506
            /* the write failed - throw an I/O exception */
 
1507
            G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
 
1508
                                           0, "file I/O error");
 
1509
        }
 
1510
 
 
1511
        /* if we're at a newline, skip it */
 
1512
        if (rem != 0)
 
1513
            ++p, --rem;
 
1514
    }
 
1515
}
 
1516
 
 
1517
/*
 
1518
 *   Write a value in 'data' mode 
 
1519
 */
 
1520
void CVmObjFile::write_data_mode(VMG_ const vm_val_t *val)
 
1521
{
 
1522
    char buf[32];
 
1523
    osfildef *fp = get_ext()->fp;
 
1524
    vm_val_t new_str;
 
1525
    const char *constp;
 
1526
 
 
1527
    /* see what type of data we want to put */
 
1528
    switch(val->typ)
 
1529
    {
 
1530
    case VM_INT:
 
1531
        /* put the type in the buffer */
 
1532
        buf[0] = VMOBJFILE_TAG_INT;
 
1533
        
 
1534
        /* add the value in INT4 format */
 
1535
        oswp4(buf + 1, val->val.intval);
 
1536
        
 
1537
        /* write out the type prefix plus the value */
 
1538
        if (osfwb(fp, buf, 5))
 
1539
            goto io_error;
 
1540
 
 
1541
        /* done */
 
1542
        break;
 
1543
 
 
1544
    case VM_ENUM:
 
1545
        /* put the type in the buffer */
 
1546
        buf[0] = VMOBJFILE_TAG_ENUM;
 
1547
 
 
1548
        /* add the value in INT4 format */
 
1549
        oswp4(buf + 1, val->val.enumval);
 
1550
 
 
1551
        /* write out the type prefix plus the value */
 
1552
        if (osfwb(fp, buf, 5))
 
1553
            goto io_error;
 
1554
 
 
1555
        /* done */
 
1556
        break;
 
1557
 
 
1558
    case VM_SSTRING:
 
1559
        /* get the string value pointer */
 
1560
        constp = val->get_as_string(vmg0_);
 
1561
 
 
1562
    write_binary_string:
 
1563
        /* write the type prefix byte */
 
1564
        buf[0] = VMOBJFILE_TAG_STRING;
 
1565
        if (osfwb(fp, buf, 1))
 
1566
            goto io_error;
 
1567
 
 
1568
        /* 
 
1569
         *   write the length prefix - for TADS 2 compatibility, include
 
1570
         *   the bytes of the prefix itself in the length count 
 
1571
         */
 
1572
        oswp2(buf, vmb_get_len(constp) + 2);
 
1573
        if (osfwb(fp, buf, 2))
 
1574
            goto io_error;
 
1575
 
 
1576
        /* write the string's bytes */
 
1577
        if (osfwb(fp, constp + VMB_LEN, vmb_get_len(constp)))
 
1578
            goto io_error;
 
1579
 
 
1580
        /* done */
 
1581
        break;
 
1582
 
 
1583
    case VM_OBJ:
 
1584
        /*
 
1585
         *   Write BigNumber and ByteArray types in special formats.  For
 
1586
         *   other types, try converting to a string. 
 
1587
         */
 
1588
        if (CVmObjBigNum::is_bignum_obj(vmg_ val->val.obj))
 
1589
        {
 
1590
            CVmObjBigNum *bignum;
 
1591
            
 
1592
            /* we know it's a BigNumber - cast it properly */
 
1593
            bignum = (CVmObjBigNum *)vm_objp(vmg_ val->val.obj);
 
1594
 
 
1595
            /* write the type tag */
 
1596
            buf[0] = VMOBJFILE_TAG_BIGNUM;
 
1597
            if (osfwb(fp, buf, 1))
 
1598
                goto io_error;
 
1599
 
 
1600
            /* write it out */
 
1601
            if (bignum->write_to_data_file(fp))
 
1602
                goto io_error;
 
1603
        }
 
1604
        else if (CVmObjByteArray::is_byte_array(vmg_ val->val.obj))
 
1605
        {
 
1606
            CVmObjByteArray *bytarr;
 
1607
 
 
1608
            /* we know it's a ByteArray - cast it properly */
 
1609
            bytarr = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj);
 
1610
 
 
1611
            /* write the type tag */
 
1612
            buf[0] = VMOBJFILE_TAG_BYTEARRAY;
 
1613
            if (osfwb(fp, buf, 1))
 
1614
                goto io_error;
 
1615
            
 
1616
            /* write the array */
 
1617
            if (bytarr->write_to_data_file(fp))
 
1618
                goto io_error;
 
1619
        }
 
1620
        else
 
1621
        {
 
1622
            /* 
 
1623
             *   Cast it to a string value and write that out.  Note that
 
1624
             *   we can ignore garbage collection for any new string we've
 
1625
             *   created, since we're just calling the OS-level file
 
1626
             *   writer, which will never invoke garbage collection.  
 
1627
             */
 
1628
            constp = vm_objp(vmg_ val->val.obj)
 
1629
                     ->cast_to_string(vmg_ val->val.obj, &new_str);
 
1630
            goto write_binary_string;
 
1631
        }
 
1632
        break;
 
1633
 
 
1634
    case VM_TRUE:
 
1635
        /* 
 
1636
         *   All we need for this is the type tag.  Note that we can't
 
1637
         *   write nil because we'd have no way of reading it back in - a
 
1638
         *   nil return from file_read indicates that we've reached the
 
1639
         *   end of the file.  So there's no point in writing nil to a
 
1640
         *   file.  
 
1641
         */
 
1642
        buf[0] = VMOBJFILE_TAG_TRUE;
 
1643
        if (osfwb(fp, buf, 1))
 
1644
            goto io_error;
 
1645
        
 
1646
        /* done */
 
1647
        break;
 
1648
        
 
1649
    default:
 
1650
        /* other types are not acceptable */
 
1651
        err_throw(VMERR_BAD_TYPE_BIF);
 
1652
    }
 
1653
 
 
1654
    /* done */
 
1655
    return;
 
1656
 
 
1657
io_error:
 
1658
    /* the write failed - throw an i/o exception */
 
1659
    G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
 
1660
                                   0, "file I/O error");
 
1661
}
 
1662
 
 
1663
 
 
1664
/* ------------------------------------------------------------------------ */
 
1665
/*
 
1666
 *   Property evaluator - read raw bytes from the file 
 
1667
 */
 
1668
int CVmObjFile::getp_read_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1669
                                uint *in_argc)
 
1670
{
 
1671
    static CVmNativeCodeDesc desc(1, 2);
 
1672
    uint argc = (in_argc != 0 ? *in_argc : 0);
 
1673
    vm_val_t arr_val;
 
1674
    CVmObjByteArray *arr;
 
1675
    unsigned long idx;
 
1676
    unsigned long len;
 
1677
    int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
 
1678
 
 
1679
    /* check arguments */
 
1680
    if (get_prop_check_argc(retval, in_argc, &desc))
 
1681
        return TRUE;
 
1682
 
 
1683
    /* make sure we are allowed to perform operations on the file */
 
1684
    check_valid_file(vmg0_);
 
1685
 
 
1686
    /* check that we have read access */
 
1687
    check_read_access(vmg0_);
 
1688
 
 
1689
    /* we can only use this call on 'raw' files */
 
1690
    if (get_ext()->mode != VMOBJFILE_MODE_RAW)
 
1691
        G_interpreter->throw_new_class(vmg_ G_predef->file_mode_exc,
 
1692
                                       0, "wrong file mode");
 
1693
 
 
1694
    /* retrieve the ByteArray destination */
 
1695
    G_stk->pop(&arr_val);
 
1696
 
 
1697
    /* make sure it's really a ByteArray object */
 
1698
    if (arr_val.typ != VM_OBJ
 
1699
        || !CVmObjByteArray::is_byte_array(vmg_ arr_val.val.obj))
 
1700
        err_throw(VMERR_BAD_TYPE_BIF);
 
1701
    
 
1702
    /* we know it's a byte array object, so cast it */
 
1703
    arr = (CVmObjByteArray *)vm_objp(vmg_ arr_val.val.obj);
 
1704
 
 
1705
    /* presume we'll try to fill the entire array */
 
1706
    idx = 1;
 
1707
    len = arr->get_element_count();
 
1708
    
 
1709
    /* if we have a starting index argument, retrieve it */
 
1710
    if (argc >= 2)
 
1711
        idx = (unsigned long)CVmBif::pop_int_val(vmg0_);
 
1712
 
 
1713
    /* if we have a length argument, retrieve it */
 
1714
    if (argc >= 3)
 
1715
        len = (unsigned long)CVmBif::pop_int_val(vmg0_);
 
1716
    
 
1717
    /* push a self-reference for gc protection */
 
1718
    G_stk->push()->set_obj(self);
 
1719
 
 
1720
    /* note the implicit seeking */
 
1721
    note_file_seek(vmg_ self, FALSE);
 
1722
 
 
1723
    /* deal with stdio buffering issues if necessary */
 
1724
    switch_read_write_mode(FALSE);
 
1725
 
 
1726
    /* 
 
1727
     *   limit the reading to the remaining data in the file, if it's a
 
1728
     *   resource file 
 
1729
     */
 
1730
    if (is_res_file)
 
1731
    {
 
1732
        unsigned long cur_seek_pos;
 
1733
 
 
1734
        /* check to see where we are relative to the end of the resource */
 
1735
        cur_seek_pos = osfpos(get_ext()->fp);
 
1736
        if (cur_seek_pos >= get_ext()->res_end)
 
1737
        {
 
1738
            /* we're already past the end - there's nothing left */
 
1739
            len = 0;
 
1740
        }
 
1741
        else
 
1742
        {
 
1743
            unsigned long limit;
 
1744
 
 
1745
            /* calculate the limit */
 
1746
            limit = get_ext()->res_end - cur_seek_pos;
 
1747
 
 
1748
            /* apply the limit if the request exceeds it */
 
1749
            if (len > limit)
 
1750
                len = limit;
 
1751
        }
 
1752
    }
 
1753
 
 
1754
    /* 
 
1755
     *   read the data into the array, and return the number of bytes we
 
1756
     *   actually manage to read 
 
1757
     */
 
1758
    retval->set_int(arr->read_from_file(get_ext()->fp, idx, len));
 
1759
 
 
1760
    /* discard our gc protection */
 
1761
    G_stk->discard();
 
1762
 
 
1763
    /* handled */
 
1764
    return TRUE;
 
1765
}
 
1766
 
 
1767
/* ------------------------------------------------------------------------ */
 
1768
/*
 
1769
 *   Property evaluator - write raw bytes to the file 
 
1770
 */
 
1771
int CVmObjFile::getp_write_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1772
                                 uint *in_argc)
 
1773
{
 
1774
    static CVmNativeCodeDesc desc(1, 2);
 
1775
    uint argc = (in_argc != 0 ? *in_argc : 0);
 
1776
    vm_val_t arr_val;
 
1777
    CVmObjByteArray *arr;
 
1778
    unsigned long idx;
 
1779
    unsigned long len;
 
1780
 
 
1781
    /* check arguments */
 
1782
    if (get_prop_check_argc(retval, in_argc, &desc))
 
1783
        return TRUE;
 
1784
 
 
1785
    /* make sure we are allowed to perform operations on the file */
 
1786
    check_valid_file(vmg0_);
 
1787
 
 
1788
    /* check that we have write access */
 
1789
    check_write_access(vmg0_);
 
1790
 
 
1791
    /* make sure the byte array argument is really a byte array */
 
1792
    G_stk->pop(&arr_val);
 
1793
    if (arr_val.typ != VM_OBJ
 
1794
        || !CVmObjByteArray::is_byte_array(vmg_ arr_val.val.obj))
 
1795
        err_throw(VMERR_BAD_TYPE_BIF);
 
1796
    
 
1797
    /* we know it's a byte array, so we can simply cast it */
 
1798
    arr = (CVmObjByteArray *)vm_objp(vmg_ arr_val.val.obj);
 
1799
 
 
1800
    /* assume we'll write the entire byte array */
 
1801
    idx = 1;
 
1802
    len = arr->get_element_count();
 
1803
 
 
1804
    /* if we have a starting index, retrieve it */
 
1805
    if (argc >= 2)
 
1806
        idx = (unsigned long)CVmBif::pop_int_val(vmg0_);
 
1807
 
 
1808
    /* if we have a length, retrieve it */
 
1809
    if (argc >= 3)
 
1810
        len = (unsigned long)CVmBif::pop_int_val(vmg0_);
 
1811
 
 
1812
    /* push a self-reference for gc protection */
 
1813
    G_stk->push()->set_obj(self);
 
1814
 
 
1815
    /* flush stdio buffers as needed and note the read operation */
 
1816
    switch_read_write_mode(TRUE);
 
1817
 
 
1818
    /* 
 
1819
     *   write the bytes to the file - on success (zero write_to_file
 
1820
     *   return), return nil, on failure (non-zero write_to_file return),
 
1821
     *   return true 
 
1822
     */
 
1823
    if (arr->write_to_file(get_ext()->fp, idx, len))
 
1824
    {
 
1825
        /* we failed to write the bytes - throw an I/O exception */
 
1826
        G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
 
1827
                                       0, "file I/O error");
 
1828
    }
 
1829
 
 
1830
    /* discard our gc protection */
 
1831
    G_stk->discard();
 
1832
 
 
1833
    /* no return value - return nil by default */
 
1834
    retval->set_nil();
 
1835
 
 
1836
    /* handled */
 
1837
    return TRUE;
 
1838
}
 
1839
 
 
1840
/* ------------------------------------------------------------------------ */
 
1841
/*
 
1842
 *   Property evaluator - get the seek position
 
1843
 */
 
1844
int CVmObjFile::getp_get_pos(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1845
                             uint *argc)
 
1846
{
 
1847
    static CVmNativeCodeDesc desc(0);
 
1848
    unsigned long cur_pos;
 
1849
 
 
1850
    /* check arguments */
 
1851
    if (get_prop_check_argc(retval, argc, &desc))
 
1852
        return TRUE;
 
1853
 
 
1854
    /* make sure we are allowed to perform operations on the file */
 
1855
    check_valid_file(vmg0_);
 
1856
 
 
1857
    /* get the current seek position */
 
1858
    cur_pos = osfpos(get_ext()->fp);
 
1859
 
 
1860
    /* if this is a resource file, adjust for the base offset */
 
1861
    cur_pos -= get_ext()->res_start;
 
1862
 
 
1863
    /* return the seek position */
 
1864
    retval->set_int(cur_pos);
 
1865
 
 
1866
    /* handled */
 
1867
    return TRUE;
 
1868
}
 
1869
 
 
1870
/* ------------------------------------------------------------------------ */
 
1871
/*
 
1872
 *   Property evaluator - set the seek position
 
1873
 */
 
1874
int CVmObjFile::getp_set_pos(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1875
                             uint *argc)
 
1876
{
 
1877
    static CVmNativeCodeDesc desc(1);
 
1878
    int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
 
1879
    unsigned long pos;
 
1880
 
 
1881
    /* check arguments */
 
1882
    if (get_prop_check_argc(retval, argc, &desc))
 
1883
        return TRUE;
 
1884
 
 
1885
    /* make sure we are allowed to perform operations on the file */
 
1886
    check_valid_file(vmg0_);
 
1887
 
 
1888
    /* note the seeking operation */
 
1889
    note_file_seek(vmg_ self, TRUE);
 
1890
 
 
1891
    /* retrieve the target seek position */
 
1892
    pos = CVmBif::pop_long_val(vmg0_);
 
1893
 
 
1894
    /* adjust for the resource base offset */
 
1895
    pos += get_ext()->res_start;
 
1896
 
 
1897
    /* 
 
1898
     *   if this is a resource file, move the position at most to the first
 
1899
     *   byte after the end of the resource 
 
1900
     */
 
1901
    if (is_res_file && pos > get_ext()->res_end)
 
1902
        pos = get_ext()->res_end;
 
1903
 
 
1904
    /* seek to the new position */
 
1905
    osfseek(get_ext()->fp, pos, OSFSK_SET);
 
1906
 
 
1907
    /* no return value */
 
1908
    retval->set_nil();
 
1909
 
 
1910
    /* handled */
 
1911
    return TRUE;
 
1912
}
 
1913
 
 
1914
/* ------------------------------------------------------------------------ */
 
1915
/*
 
1916
 *   Property evaluator - set position to end of file
 
1917
 */
 
1918
int CVmObjFile::getp_set_pos_end(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1919
                                 uint *argc)
 
1920
{
 
1921
    static CVmNativeCodeDesc desc(0);
 
1922
    int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
 
1923
 
 
1924
    /* check arguments */
 
1925
    if (get_prop_check_argc(retval, argc, &desc))
 
1926
        return TRUE;
 
1927
 
 
1928
    /* make sure we are allowed to perform operations on the file */
 
1929
    check_valid_file(vmg0_);
 
1930
 
 
1931
    /* note the seeking operation */
 
1932
    note_file_seek(vmg_ self, TRUE);
 
1933
 
 
1934
    /* handle according to whether it's a resource or not */
 
1935
    if (is_res_file)
 
1936
    {
 
1937
        /* resource - seek to the first byte after the resource data */
 
1938
        osfseek(get_ext()->fp, get_ext()->res_end, OSFSK_SET);
 
1939
    }
 
1940
    else
 
1941
    {
 
1942
        /* normal file - simply seek to the end of the file */
 
1943
        osfseek(get_ext()->fp, 0, OSFSK_END);
 
1944
    }
 
1945
 
 
1946
    /* no return value */
 
1947
    retval->set_nil();
 
1948
 
 
1949
    /* handled */
 
1950
    return TRUE;
 
1951
}
 
1952
 
 
1953
/* ------------------------------------------------------------------------ */
 
1954
/*
 
1955
 *   Property evaluator - get size 
 
1956
 */
 
1957
int CVmObjFile::getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *retval,
 
1958
                              uint *argc)
 
1959
{
 
1960
    static CVmNativeCodeDesc desc(0);
 
1961
    int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
 
1962
 
 
1963
    /* check arguments */
 
1964
    if (get_prop_check_argc(retval, argc, &desc))
 
1965
        return TRUE;
 
1966
 
 
1967
    /* make sure we are allowed to perform operations on the file */
 
1968
    check_valid_file(vmg0_);
 
1969
 
 
1970
    /* note the seeking operation */
 
1971
    note_file_seek(vmg_ self, TRUE);
 
1972
 
 
1973
    /* handle according to whether it's a resource or not */
 
1974
    if (is_res_file)
 
1975
    {
 
1976
        /* resource - we know the size from the resource descriptor */
 
1977
        retval->set_int(get_ext()->res_end - get_ext()->res_start + 1);
 
1978
    }
 
1979
    else
 
1980
    {
 
1981
        osfildef *fp = get_ext()->fp;
 
1982
        unsigned long cur_pos;
 
1983
        
 
1984
        /* 
 
1985
         *   It's a normal file.  Remember the current seek position, then
 
1986
         *   seek to the end of the file.  
 
1987
         */
 
1988
        cur_pos = osfpos(fp);
 
1989
        osfseek(fp, 0, OSFSK_END);
 
1990
 
 
1991
        /* the current position gives us the length of the file */
 
1992
        retval->set_int(osfpos(fp));
 
1993
 
 
1994
        /* seek back to where we started */
 
1995
        osfseek(fp, cur_pos, OSFSK_SET);
 
1996
    }
 
1997
 
 
1998
    /* handled */
 
1999
    return TRUE;
 
2000
}
 
2001
 
 
2002
/* ------------------------------------------------------------------------ */
 
2003
/*
 
2004
 *   Refill the read buffer.  Returns true if the buffer contains any data
 
2005
 *   on return, false if we're at end of file.  
 
2006
 */
 
2007
int vmobjfile_readbuf_t::refill(CCharmapToUni *charmap,
 
2008
                                osfildef *fp, int is_res_file,
 
2009
                                unsigned long res_seek_end)
 
2010
{
 
2011
    unsigned long read_limit;
 
2012
    
 
2013
    /* if the buffer isn't empty, ignore the request */
 
2014
    if (rem != 0)
 
2015
        return TRUE;
 
2016
 
 
2017
    /* presume there's no read limit */
 
2018
    read_limit = 0;
 
2019
 
 
2020
    /* if it's a resource file, limit the size */
 
2021
    if (is_res_file)
 
2022
    {
 
2023
        unsigned long cur_seek_ofs;
 
2024
 
 
2025
        /* make sure we're not already past the end */
 
2026
        cur_seek_ofs = osfpos(fp);
 
2027
        if (cur_seek_ofs >= res_seek_end)
 
2028
            return FALSE;
 
2029
 
 
2030
        /* calculate the amount of data remaining in the resource */
 
2031
        read_limit = res_seek_end - cur_seek_ofs;
 
2032
    }
 
2033
    
 
2034
    /* read the text */
 
2035
    rem = charmap->read_file(fp, buf, sizeof(buf), read_limit);
 
2036
 
 
2037
    /* read from the start of the buffer */
 
2038
    ptr.set(buf);
 
2039
 
 
2040
    /* indicate that we have more data to read */
 
2041
    return (rem != 0);
 
2042
}
 
2043
 
 
2044