2
* Copyright (c) 2001, 2002 Michael J. Roberts. All Rights Reserved.
4
* Please see the accompanying license file, LICENSE.TXT, for information
5
* on using and copying this software.
9
vmfilobj.h - File object metaclass
11
Implements an intrinsic class interface to operating system file I/O.
15
06/28/01 MJRoberts - Creation
41
/* ------------------------------------------------------------------------ */
46
/* metaclass registration object */
47
static CVmMetaclassFile metaclass_reg_obj;
48
CVmMetaclass *CVmObjFile::metaclass_reg_ = &metaclass_reg_obj;
52
*CVmObjFile::func_table_[])(VMG_ vm_obj_id_t self,
53
vm_val_t *retval, uint *argc) =
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
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().
78
enum vmobjfil_meta_fnset
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
87
/* ------------------------------------------------------------------------ */
89
* Special filename designators
92
/* library defaults file */
93
#define SFID_LIB_DEFAULTS 0x0001
95
/* ------------------------------------------------------------------------ */
99
vm_obj_id_t CVmObjFile::create_from_stack(VMG_ const uchar **pc_ptr,
103
* we can't be created with 'new' - we can only be created via our
104
* static creator methods (openTextFile, openDataFile, openRawFile)
106
err_throw(VMERR_BAD_DYNAMIC_NEW);
108
/* not reached, but the compiler might not know that */
109
AFTER_ERR_THROW(return VM_INVALID_OBJ;)
112
/* ------------------------------------------------------------------------ */
114
* Create with no contents
116
vm_obj_id_t CVmObjFile::create(VMG_ int in_root_set)
118
vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
120
/* instantiate the object */
121
new (vmg_ id) CVmObjFile();
123
/* files are always transient */
124
G_obj_table->set_obj_transient(id);
126
/* return the new ID */
131
* Create with the given character set object and file handle.
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,
137
unsigned long res_start, unsigned long res_end)
139
vm_obj_id_t id = vm_new_id(vmg_ in_root_set, TRUE, FALSE);
141
/* instantiate the object */
142
new (vmg_ id) CVmObjFile(vmg_ charset, fp, flags, mode, access,
143
create_readbuf, res_start, res_end);
145
/* files are always transient */
146
G_obj_table->set_obj_transient(id);
152
/* ------------------------------------------------------------------------ */
156
CVmObjFile::CVmObjFile(VMG_ vm_obj_id_t charset, osfildef *fp,
157
unsigned long flags, int mode, int access,
159
unsigned long res_start, unsigned long res_end)
161
/* allocate and initialize our extension */
163
alloc_ext(vmg_ charset, fp, flags, mode, access, create_readbuf,
168
* Allocate and initialize our extension
170
void CVmObjFile::alloc_ext(VMG_ vm_obj_id_t charset, osfildef *fp,
171
unsigned long flags, int mode, int access,
173
unsigned long res_start, unsigned long res_end)
178
* if we already have an extension, delete it (and release our
179
* underlying system file, if any)
181
notify_delete(vmg_ FALSE);
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.
187
siz = sizeof(vmobjfile_ext_t);
189
siz += sizeof(vmobjfile_readbuf_t);
191
/* allocate space for our extension structure */
192
ext_ = (char *)G_mem->get_var_heap()->alloc_mem(siz, this);
194
/* store the data we received from the caller */
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;
204
* point to our read buffer, for which we allocated space contiguously
205
* with and immediately following our extension, if we have one
209
/* point to our read buffer object */
210
get_ext()->readbuf = (vmobjfile_readbuf_t *)(get_ext() + 1);
212
/* initialize the read buffer with no initial data */
213
get_ext()->readbuf->rem = 0;
214
get_ext()->readbuf->ptr.set(0);
218
/* there's no read buffer at all */
219
get_ext()->readbuf = 0;
223
/* ------------------------------------------------------------------------ */
227
void CVmObjFile::notify_delete(VMG_ int /*in_root_set*/)
229
/* if we have an extension, clean it up */
232
/* close our file if we have one */
233
if (get_ext()->fp != 0)
234
osfcls(get_ext()->fp);
236
/* free our extension */
237
G_mem->get_var_heap()->free_mem(ext_);
241
/* ------------------------------------------------------------------------ */
245
void CVmObjFile::set_prop(VMG_ class CVmUndo *,
246
vm_obj_id_t, vm_prop_id_t,
249
err_throw(VMERR_INVALID_SETPROP);
252
/* ------------------------------------------------------------------------ */
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,
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);
266
/* call the appropriate function */
267
if ((this->*func_table_[func_idx])(vmg_ self, retval, argc))
269
*source_obj = metaclass_reg_->get_class_obj(vmg0_);
273
/* inherit default handling */
274
return CVmObject::get_prop(vmg_ prop, retval, self, source_obj, argc);
278
* call a static property
280
int CVmObjFile::call_stat_prop(VMG_ vm_val_t *result,
281
const uchar **pc_ptr, uint *argc,
284
/* translate the property into a function vector index */
286
->prop_to_vector_idx(metaclass_reg_->get_reg_idx(), prop))
288
case VMOBJFILE_OPEN_TEXT:
289
return s_getp_open_text(vmg_ result, argc, FALSE);
291
case VMOBJFILE_OPEN_DATA:
292
return s_getp_open_data(vmg_ result, argc);
294
case VMOBJFILE_OPEN_RAW:
295
return s_getp_open_raw(vmg_ result, argc, FALSE);
297
case VMOBJFILE_OPEN_RES_TEXT:
298
return s_getp_open_text(vmg_ result, argc, TRUE);
300
case VMOBJFILE_OPEN_RES_RAW:
301
return s_getp_open_raw(vmg_ result, argc, TRUE);
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);
309
/* ------------------------------------------------------------------------ */
311
* load from an image file
313
void CVmObjFile::load_from_image(VMG_ vm_obj_id_t self,
314
const char *ptr, size_t siz)
316
/* load from the image data */
317
load_image_data(vmg_ ptr, siz);
320
* save our image data pointer in the object table, so that we can
321
* access it (without storing it ourselves) during a reload
323
G_obj_table->save_image_pointer(self, ptr, siz);
327
* reload the object from image data
329
void CVmObjFile::reload_from_image(VMG_ vm_obj_id_t /*self*/,
330
const char *ptr, size_t siz)
332
/* load the image data */
333
load_image_data(vmg_ ptr, siz);
337
* load or re-load image data
339
void CVmObjFile::load_image_data(VMG_ const char *ptr, size_t siz)
346
/* read our character set */
347
charset = vmb_get_objid(ptr);
348
ptr += VMB_OBJECT_ID;
350
/* get the mode and access values */
351
mode = (unsigned char)*ptr++;
352
access = (unsigned char)*ptr++;
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
362
flags |= VMOBJFILE_OUT_OF_SYNC;
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
371
alloc_ext(vmg_ charset, 0, flags, mode, access, FALSE, 0, 0);
374
/* ------------------------------------------------------------------------ */
378
void CVmObjFile::save_to_file(VMG_ class CVmFile *fp)
380
/* files are always transient, so should never be saved */
385
* restore from a file
387
void CVmObjFile::restore_from_file(VMG_ vm_obj_id_t self,
388
CVmFile *fp, CVmObjFixup *)
390
/* files are always transient, so should never be savd */
393
/* ------------------------------------------------------------------------ */
395
* Mark as referenced all of the objects to which we refer
397
void CVmObjFile::mark_refs(VMG_ uint state)
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);
404
/* ------------------------------------------------------------------------ */
406
* Note that we're seeking within the file.
408
void CVmObjFile::note_file_seek(VMG_ vm_obj_id_t self, int is_explicit)
411
* if it's an explicit seek, invalidate our internal read buffer and
412
* note that the stdio buffers have been invalidated
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;
421
* mark the last operation as clearing stdio buffering - when we
422
* explicitly seek, stdio automatically invalidates its internal
425
get_ext()->flags &= ~VMOBJFILE_STDIO_BUF_DIRTY;
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.
436
void CVmObjFile::switch_read_write_mode(int writing)
438
/* if we're writing, invalidate the read buffer */
439
if (writing && get_ext()->readbuf != 0)
440
get_ext()->readbuf->rem = 0;
443
* if we just performed a read or write operation, we must seek if
444
* we're performing the opposite type of operation now
446
if ((get_ext()->flags & VMOBJFILE_STDIO_BUF_DIRTY) != 0)
450
/* check what type of operation we did last */
451
was_writing = ((get_ext()->flags & VMOBJFILE_LAST_OP_WRITE) != 0);
454
* if we're switching operations, explicitly seek to the current
455
* location to flush the stdio buffers
457
if ((writing && !was_writing) || (!writing && was_writing))
458
osfseek(get_ext()->fp, osfpos(get_ext()->fp), OSFSK_SET);
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
466
get_ext()->flags |= VMOBJFILE_STDIO_BUF_DIRTY;
468
/* remember which type of operation we're performing */
470
get_ext()->flags |= VMOBJFILE_LAST_OP_WRITE;
472
get_ext()->flags &= ~VMOBJFILE_LAST_OP_WRITE;
475
/* ------------------------------------------------------------------------ */
477
* Retrieve the filename and access mode arguments.
479
void CVmObjFile::get_filename_and_access(VMG_ char *fname, size_t fname_siz,
480
int *access, int is_resource_file)
482
int is_special_file = FALSE;
485
* check to see if we have an explicit filename string, or an integer
486
* giving a special system file ID
488
if (G_stk->get(0)->typ == VM_INT)
492
/* start with no path, in case we have trouble retrieving it */
495
/* we have an integer, which is a special file designator */
496
switch (CVmBif::pop_int_val(vmg0_))
498
case SFID_LIB_DEFAULTS:
499
/* get the system application data path */
500
G_host_ifc->get_special_file_path(path, sizeof(path),
503
/* add the filename */
504
os_build_full_path(fname, fname_siz, path, "settings.txt");
508
/* invalid filename value */
509
err_throw(VMERR_BAD_VAL_BIF);
512
/* note that we have a special file, for file safety purposes */
513
is_special_file = TRUE;
517
/* we must have an explicit filename string - pop it */
518
CVmBif::pop_str_val_fname(vmg_ fname, fname_siz);
522
* retrieve the access mode; if it's a resource file, the mode is
525
if (is_resource_file)
526
*access = VMOBJFILE_ACCESS_READ;
528
*access = CVmBif::pop_int_val(vmg0_);
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.
539
if (!is_resource_file && !is_special_file)
540
check_safety_for_open(vmg_ fname, *access);
543
/* ------------------------------------------------------------------------ */
545
* Static property evaluator - open a text file
547
int CVmObjFile::s_getp_open_text(VMG_ vm_val_t *retval, uint *in_argc,
548
int is_resource_file)
550
uint argc = (in_argc != 0 ? *in_argc : 0);
551
static CVmNativeCodeDesc desc_file(2, 1);
552
static CVmNativeCodeDesc desc_res(1, 1);
555
vm_obj_id_t cset_obj;
558
unsigned long res_start;
559
unsigned long res_end;
562
/* check arguments */
563
if (get_prop_check_argc(retval, in_argc,
564
is_resource_file ? &desc_res : &desc_file))
567
/* initialize the flags to indicate a text-mode file */
570
/* add the resource-file flag if appropriate */
571
if (is_resource_file)
572
flags |= VMOBJFILE_IS_RESOURCE;
574
/* presume we can use the entire file */
578
/* retrieve the filename */
579
get_filename_and_access(vmg_ fname, sizeof(fname),
580
&access, is_resource_file);
582
/* presume we won't need a read buffer */
583
create_readbuf = FALSE;
585
/* if there's a character set name or object, retrieve it */
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
592
if (G_stk->get(0)->typ == VM_OBJ
593
&& CVmObjCharSet::is_charset(vmg_ G_stk->get(0)->val.obj))
595
/* retrieve the CharacterSet reference */
596
cset_obj = CVmBif::pop_obj_val(vmg0_);
603
/* it's not a CharacterSet, so it must be a character set name */
604
str = G_stk->get(0)->get_as_string(vmg0_);
606
err_throw(VMERR_BAD_TYPE_BIF);
608
/* get the length and skip the length prefix */
609
len = vmb_get_len(str);
612
/* create a mapper for the given name */
613
cset_obj = CVmObjCharSet::create(vmg_ FALSE, str, len);
618
/* no character set is specified - use US-ASCII by default */
619
cset_obj = CVmObjCharSet::create(vmg_ FALSE, "us-ascii", 8);
622
/* push the character map object onto the stack for gc protection */
623
G_stk->push()->set_obj(cset_obj);
625
/* open the file for reading or writing, as appropriate */
628
case VMOBJFILE_ACCESS_READ:
629
/* open a resource file or file system file, as appropriate */
630
if (is_resource_file)
632
unsigned long res_len;
634
/* it's a resource - open it */
635
fp = G_host_ifc->find_resource(fname, strlen(fname), &res_len);
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
644
/* the file is initially at the start of the resource data */
645
res_start = osfpos(fp);
647
/* note the offset of the first byte after the resource */
648
res_end = res_start + res_len;
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
663
fp = osfoprb(fname, OSFTTEXT);
666
/* make sure we opened it successfully */
668
G_interpreter->throw_new_class(vmg_ G_predef->file_not_found_exc,
669
0, "file not found");
671
/* we need a read buffer */
672
create_readbuf = TRUE;
675
case VMOBJFILE_ACCESS_WRITE:
676
/* open for writing */
677
fp = osfopwb(fname, OSFTTEXT);
679
/* make sure we created it successfully */
681
G_interpreter->throw_new_class(vmg_ G_predef->file_creation_exc,
682
0, "error creating file");
685
case VMOBJFILE_ACCESS_RW_KEEP:
686
/* open for read/write, keeping existing contents */
687
fp = osfoprwb(fname, OSFTTEXT);
689
/* make sure we were able to find or create the file */
691
G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
692
0, "error opening file");
695
case VMOBJFILE_ACCESS_RW_TRUNC:
696
/* open for read/write, truncating existing contents */
697
fp = osfoprwtb(fname, OSFTTEXT);
699
/* make sure we were successful */
701
G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
702
0, "error opening file");
707
err_throw(VMERR_BAD_VAL_BIF);
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));
715
/* discard gc protection */
723
* Static property evaluator - open a data file
725
int CVmObjFile::s_getp_open_data(VMG_ vm_val_t *retval, uint *argc)
727
/* use the generic binary file opener in 'data' mode */
728
return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_DATA, FALSE);
732
* Static property evaluator - open a raw file
734
int CVmObjFile::s_getp_open_raw(VMG_ vm_val_t *retval, uint *argc,
735
int is_resource_file)
737
/* use the generic binary file opener in 'raw' mode */
738
return open_binary(vmg_ retval, argc, VMOBJFILE_MODE_RAW,
743
* Generic binary file opener - common to 'data' and 'raw' files
745
int CVmObjFile::open_binary(VMG_ vm_val_t *retval, uint *argc, int mode,
746
int is_resource_file)
748
static CVmNativeCodeDesc file_desc(2);
749
static CVmNativeCodeDesc res_desc(1);
753
unsigned long res_start;
754
unsigned long res_end;
757
/* check arguments */
758
if (get_prop_check_argc(retval, argc,
759
is_resource_file ? &res_desc : &file_desc))
762
/* initialize the flags */
765
/* set the resource-file flag, if appropriate */
766
if (is_resource_file)
767
flags |= VMOBJFILE_IS_RESOURCE;
769
/* presume we can use the entire file */
773
/* retrieve the filename and access mode */
774
get_filename_and_access(vmg_ fname, sizeof(fname),
775
&access, is_resource_file);
777
/* open the file in binary mode, with the desired access type */
780
case VMOBJFILE_ACCESS_READ:
781
/* open the resource or ordinary file, as appropriate */
782
if (is_resource_file)
784
unsigned long res_len;
786
/* it's a resource - open it */
787
fp = G_host_ifc->find_resource(fname, strlen(fname), &res_len);
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
796
/* the file is initially at the start of the resource data */
797
res_start = osfpos(fp);
799
/* note the offset of the first byte after the resource */
800
res_end = res_start + res_len;
805
/* open the ordinary file in binary mode for reading only */
806
fp = osfoprb(fname, OSFTBIN);
809
/* make sure we were able to find it and open it */
811
G_interpreter->throw_new_class(vmg_ G_predef->file_not_found_exc,
812
0, "file not found");
815
case VMOBJFILE_ACCESS_WRITE:
816
/* open in binary mode for writing only */
817
fp = osfopwb(fname, OSFTBIN);
819
/* make sure we were able to create the file successfully */
821
G_interpreter->throw_new_class(vmg_ G_predef->file_creation_exc,
822
0, "error creating file");
825
case VMOBJFILE_ACCESS_RW_KEEP:
826
/* open for read/write, keeping existing contents */
827
fp = osfoprwb(fname, OSFTBIN);
829
/* make sure we were able to find or create the file */
831
G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
832
0, "error opening file");
835
case VMOBJFILE_ACCESS_RW_TRUNC:
836
/* open for read/write, truncating existing contents */
837
fp = osfoprwtb(fname, OSFTBIN);
839
/* make sure we were successful */
841
G_interpreter->throw_new_class(vmg_ G_predef->file_open_exc,
842
0, "error opening file");
847
err_throw(VMERR_BAD_VAL_BIF);
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));
858
/* ------------------------------------------------------------------------ */
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.
863
void CVmObjFile::check_safety_for_open(VMG_ const char *fname, int access)
868
/* get the current file safety level from the host application */
869
safety = G_host_ifc->get_io_safety();
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
881
in_same_dir = (os_get_root_name((char *)fname) == fname);
883
/* check for conformance with the safety level setting */
886
case VMOBJFILE_ACCESS_READ:
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
893
if (safety > VM_IO_SAFETY_READ_CUR
894
|| (!in_same_dir && safety > VM_IO_SAFETY_READ_ANY_WRITE_CUR))
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");
902
case VMOBJFILE_ACCESS_WRITE:
903
case VMOBJFILE_ACCESS_RW_KEEP:
904
case VMOBJFILE_ACCESS_RW_TRUNC:
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
910
if (safety > VM_IO_SAFETY_READWRITE_CUR
911
|| (!in_same_dir && safety > VM_IO_SAFETY_MINIMUM))
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");
921
/* ------------------------------------------------------------------------ */
923
* Property evaluator - get the character set
925
int CVmObjFile::getp_get_charset(VMG_ vm_obj_id_t self, vm_val_t *retval,
928
static CVmNativeCodeDesc desc(0);
930
/* check arguments */
931
if (get_prop_check_argc(retval, argc, &desc))
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);
944
/* ------------------------------------------------------------------------ */
946
* Property evaluator - set the character set
948
int CVmObjFile::getp_set_charset(VMG_ vm_obj_id_t self, vm_val_t *retval,
951
static CVmNativeCodeDesc desc(1);
953
/* check arguments */
954
if (get_prop_check_argc(retval, argc, &desc))
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);
963
/* remember the new character set */
964
if (G_stk->get(0)->typ == VM_NIL)
965
get_ext()->charset = VM_INVALID_OBJ;
967
get_ext()->charset = G_stk->get(0)->val.obj;
969
/* discard the argument */
972
/* no return value */
979
/* ------------------------------------------------------------------------ */
981
* Check that we're in a valid state to perform file operations.
983
void CVmObjFile::check_valid_file(VMG0_)
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");
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");
997
* Check that we have read access
999
void CVmObjFile::check_read_access(VMG0_)
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,
1008
* Check that we have write access
1010
void CVmObjFile::check_write_access(VMG0_)
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,
1019
/* ------------------------------------------------------------------------ */
1021
* Property evaluator - close the file
1023
int CVmObjFile::getp_close_file(VMG_ vm_obj_id_t self, vm_val_t *retval,
1026
static CVmNativeCodeDesc desc(0);
1028
/* check arguments */
1029
if (get_prop_check_argc(retval, argc, &desc))
1032
/* make sure we are allowed to perform operations on the file */
1033
check_valid_file(vmg0_);
1035
/* close the underlying system file */
1036
osfcls(get_ext()->fp);
1038
/* forget the underlying system file, since it's no longer valid */
1041
/* no return value */
1048
/* ------------------------------------------------------------------------ */
1050
* Property evaluator - read from the file
1052
int CVmObjFile::getp_read_file(VMG_ vm_obj_id_t self, vm_val_t *retval,
1055
static CVmNativeCodeDesc desc(0);
1057
/* check arguments */
1058
if (get_prop_check_argc(retval, argc, &desc))
1061
/* push a self-reference for gc protection */
1062
G_stk->push()->set_obj(self);
1064
/* make sure we are allowed to perform operations on the file */
1065
check_valid_file(vmg0_);
1067
/* check that we have read access */
1068
check_read_access(vmg0_);
1070
/* note the implicit seeking */
1071
note_file_seek(vmg_ self, FALSE);
1073
/* flush stdio buffers as needed and note the read operation */
1074
switch_read_write_mode(FALSE);
1076
/* read according to our mode */
1077
switch(get_ext()->mode)
1079
case VMOBJFILE_MODE_TEXT:
1080
/* read a line of text */
1081
read_text_mode(vmg_ retval);
1084
case VMOBJFILE_MODE_DATA:
1085
/* read in data mode */
1086
read_data_mode(vmg_ retval);
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");
1096
/* discard the gc protection */
1104
* Read a value in text mode
1106
void CVmObjFile::read_text_mode(VMG_ vm_val_t *retval)
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);
1115
/* we haven't yet constructed a string */
1119
/* get our character mapper */
1120
charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset))
1121
->get_to_uni(vmg0_);
1123
/* assume we'll fail to read anything, in which case we'll return nil */
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
1130
G_stk->push(retval);
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.
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))
1149
/* note where we started this chunk */
1150
start = readbuf->ptr.getptr();
1152
/* scan for and remove any trailing newline */
1153
for (found_nl = '\0' ; readbuf->rem != 0 ;
1154
readbuf->ptr.inc(&readbuf->rem))
1158
/* get the current character */
1159
cur = readbuf->ptr.getch();
1162
* check for a newline (note that 0x2028 is the unicode line
1163
* separator character)
1165
if (cur == '\n' || cur == '\r' || cur == 0x2028)
1167
/* note the newline */
1170
/* no need to look any further */
1175
/* note the length of the current segment */
1176
new_len = readbuf->ptr.getptr() - start;
1179
* if there's a newline character, include an extra byte for the
1180
* '\n' we'll include in the result
1182
nl_len = (found_nl != '\0');
1185
* If this is our first segment, construct a new string from this
1186
* chunk; otherwise, add to the existing string.
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.
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);
1202
/* copy the segment into the string object */
1203
memcpy(str->cons_get_buf(), start, new_len);
1205
/* add a '\n' if we found a newline */
1206
if (found_nl != '\0')
1207
*(str->cons_get_buf() + new_len) = '\n';
1209
/* this is the length of the string so far */
1210
str_len = new_len + nl_len;
1213
* replace the stack placeholder with our string, so the
1214
* garbage collector will know it's still in use
1217
G_stk->push(retval);
1221
CVmObjString *new_str;
1224
* create a new string to hold the contents of the old string
1225
* plus the new buffer
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);
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);
1235
/* add the new chunk after the copy of the old string */
1236
memcpy(new_str->cons_get_buf() + str_len, start, new_len);
1238
/* add the newline if necessary */
1239
if (found_nl != '\0')
1240
*(new_str->cons_get_buf() + str_len + new_len) = '\n';
1242
/* the new string now replaces the old string */
1244
str_len += new_len + nl_len;
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
1253
G_stk->push(retval);
1256
/* if we found a newline in this segment, we're done */
1257
if (found_nl != '\0')
1259
/* skip the newline in the input */
1260
readbuf->ptr.inc(&readbuf->rem);
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);
1267
* check for a complementary newline character, for systems
1268
* that use \n\r or \r\n pairs
1270
if (readbuf->rem != 0)
1274
/* get the next character */
1275
nxt = readbuf->ptr.getch();
1277
/* check for a complementary character */
1278
if ((found_nl == '\n' && nxt == '\r')
1279
|| (found_nl == '\r' && nxt == '\n'))
1282
* we have a pair sequence - skip the second character
1285
readbuf->ptr.inc(&readbuf->rem);
1289
/* we've found the newline, so we're done with the string */
1295
* we now can discard the string we've been keeping on the stack to
1296
* for garbage collection protection
1302
* Read a value in 'data' mode
1304
void CVmObjFile::read_data_mode(VMG_ vm_val_t *retval)
1307
CVmObjString *str_obj;
1309
osfildef *fp = get_ext()->fp;
1311
/* read the type flag */
1312
if (osfrb(fp, buf, 1))
1314
/* end of file - return nil */
1319
/* see what we have */
1320
switch((vm_datatype_t)buf[0])
1322
case VMOBJFILE_TAG_INT:
1323
/* read the INT4 value */
1324
if (osfrb(fp, buf, 4))
1327
/* set the integer value from the buffer */
1328
retval->set_int(osrp4(buf));
1331
case VMOBJFILE_TAG_ENUM:
1332
/* read the UINT4 value */
1333
if (osfrb(fp, buf, 4))
1336
/* set the 'enum' value */
1337
retval->set_enum(t3rp4u(buf));
1340
case VMOBJFILE_TAG_STRING:
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
1346
if (osfrb(fp, buf, 2))
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)
1355
str_id = CVmObjString::create(vmg_ FALSE, osrp2(buf) - 2);
1356
str_obj = (CVmObjString *)vm_objp(vmg_ str_id);
1358
/* read the bytes of the string into the object's buffer */
1359
if (osfrb(fp, str_obj->cons_get_buf(), osrp2(buf) - 2))
1362
/* success - set the string return value, and we're done */
1363
retval->set_obj(str_id);
1366
case VMOBJFILE_TAG_TRUE:
1367
/* it's a simple 'true' value */
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))
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))
1384
/* invalid data - throw an error */
1385
G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
1386
0, "file I/O error");
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
1398
G_interpreter->throw_new_class(vmg_ G_predef->file_io_exc,
1399
0, "file I/O error");
1402
/* ------------------------------------------------------------------------ */
1404
* Property evaluator - write to the file
1406
int CVmObjFile::getp_write_file(VMG_ vm_obj_id_t self, vm_val_t *retval,
1409
static CVmNativeCodeDesc desc(1);
1410
const vm_val_t *argval;
1412
/* check arguments */
1413
if (get_prop_check_argc(retval, argc, &desc))
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
1420
argval = G_stk->get(0);
1422
/* push a self-reference for gc protection */
1423
G_stk->push()->set_obj(self);
1425
/* make sure we are allowed to perform operations on the file */
1426
check_valid_file(vmg0_);
1428
/* check that we have write access */
1429
check_write_access(vmg0_);
1431
/* deal with stdio buffering if we're changing modes */
1432
switch_read_write_mode(TRUE);
1434
/* read according to our mode */
1435
switch(get_ext()->mode)
1437
case VMOBJFILE_MODE_TEXT:
1438
/* read a line of text */
1439
write_text_mode(vmg_ argval);
1442
case VMOBJFILE_MODE_DATA:
1443
/* read in data mode */
1444
write_data_mode(vmg_ argval);
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");
1454
/* discard our gc protection and argument */
1457
/* no return value - return nil by default */
1465
* Write a value in text mode
1467
void CVmObjFile::write_text_mode(VMG_ const vm_val_t *val)
1471
CCharmapToLocal *charmap;
1473
const char *p, *startp;
1476
/* get our character mapper */
1477
charmap = ((CVmObjCharSet *)vm_objp(vmg_ get_ext()->charset))
1478
->get_to_local(vmg0_);
1480
/* convert the value to a string */
1481
constp = CVmObjString::cvt_to_str(vmg_ &new_str,
1482
conv_buf, sizeof(conv_buf),
1485
/* scan for newlines - we need to write newline sequences specially */
1486
for (startp = constp + VMB_LEN, rem = vmb_get_len(constp) ;
1489
/* scan to the next newline */
1490
for (p = startp ; rem != 0 && *p != '\n' ; ++p, --rem) ;
1492
/* write this chunk through the character mapper */
1494
&& charmap->write_file(get_ext()->fp, startp, p - startp))
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");
1501
/* write the newline, if applicable */
1503
&& charmap->write_file(get_ext()->fp, OS_NEWLINE_SEQ,
1504
strlen(OS_NEWLINE_SEQ)))
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");
1511
/* if we're at a newline, skip it */
1518
* Write a value in 'data' mode
1520
void CVmObjFile::write_data_mode(VMG_ const vm_val_t *val)
1523
osfildef *fp = get_ext()->fp;
1527
/* see what type of data we want to put */
1531
/* put the type in the buffer */
1532
buf[0] = VMOBJFILE_TAG_INT;
1534
/* add the value in INT4 format */
1535
oswp4(buf + 1, val->val.intval);
1537
/* write out the type prefix plus the value */
1538
if (osfwb(fp, buf, 5))
1545
/* put the type in the buffer */
1546
buf[0] = VMOBJFILE_TAG_ENUM;
1548
/* add the value in INT4 format */
1549
oswp4(buf + 1, val->val.enumval);
1551
/* write out the type prefix plus the value */
1552
if (osfwb(fp, buf, 5))
1559
/* get the string value pointer */
1560
constp = val->get_as_string(vmg0_);
1562
write_binary_string:
1563
/* write the type prefix byte */
1564
buf[0] = VMOBJFILE_TAG_STRING;
1565
if (osfwb(fp, buf, 1))
1569
* write the length prefix - for TADS 2 compatibility, include
1570
* the bytes of the prefix itself in the length count
1572
oswp2(buf, vmb_get_len(constp) + 2);
1573
if (osfwb(fp, buf, 2))
1576
/* write the string's bytes */
1577
if (osfwb(fp, constp + VMB_LEN, vmb_get_len(constp)))
1585
* Write BigNumber and ByteArray types in special formats. For
1586
* other types, try converting to a string.
1588
if (CVmObjBigNum::is_bignum_obj(vmg_ val->val.obj))
1590
CVmObjBigNum *bignum;
1592
/* we know it's a BigNumber - cast it properly */
1593
bignum = (CVmObjBigNum *)vm_objp(vmg_ val->val.obj);
1595
/* write the type tag */
1596
buf[0] = VMOBJFILE_TAG_BIGNUM;
1597
if (osfwb(fp, buf, 1))
1601
if (bignum->write_to_data_file(fp))
1604
else if (CVmObjByteArray::is_byte_array(vmg_ val->val.obj))
1606
CVmObjByteArray *bytarr;
1608
/* we know it's a ByteArray - cast it properly */
1609
bytarr = (CVmObjByteArray *)vm_objp(vmg_ val->val.obj);
1611
/* write the type tag */
1612
buf[0] = VMOBJFILE_TAG_BYTEARRAY;
1613
if (osfwb(fp, buf, 1))
1616
/* write the array */
1617
if (bytarr->write_to_data_file(fp))
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.
1628
constp = vm_objp(vmg_ val->val.obj)
1629
->cast_to_string(vmg_ val->val.obj, &new_str);
1630
goto write_binary_string;
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
1642
buf[0] = VMOBJFILE_TAG_TRUE;
1643
if (osfwb(fp, buf, 1))
1650
/* other types are not acceptable */
1651
err_throw(VMERR_BAD_TYPE_BIF);
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");
1664
/* ------------------------------------------------------------------------ */
1666
* Property evaluator - read raw bytes from the file
1668
int CVmObjFile::getp_read_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval,
1671
static CVmNativeCodeDesc desc(1, 2);
1672
uint argc = (in_argc != 0 ? *in_argc : 0);
1674
CVmObjByteArray *arr;
1677
int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
1679
/* check arguments */
1680
if (get_prop_check_argc(retval, in_argc, &desc))
1683
/* make sure we are allowed to perform operations on the file */
1684
check_valid_file(vmg0_);
1686
/* check that we have read access */
1687
check_read_access(vmg0_);
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");
1694
/* retrieve the ByteArray destination */
1695
G_stk->pop(&arr_val);
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);
1702
/* we know it's a byte array object, so cast it */
1703
arr = (CVmObjByteArray *)vm_objp(vmg_ arr_val.val.obj);
1705
/* presume we'll try to fill the entire array */
1707
len = arr->get_element_count();
1709
/* if we have a starting index argument, retrieve it */
1711
idx = (unsigned long)CVmBif::pop_int_val(vmg0_);
1713
/* if we have a length argument, retrieve it */
1715
len = (unsigned long)CVmBif::pop_int_val(vmg0_);
1717
/* push a self-reference for gc protection */
1718
G_stk->push()->set_obj(self);
1720
/* note the implicit seeking */
1721
note_file_seek(vmg_ self, FALSE);
1723
/* deal with stdio buffering issues if necessary */
1724
switch_read_write_mode(FALSE);
1727
* limit the reading to the remaining data in the file, if it's a
1732
unsigned long cur_seek_pos;
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)
1738
/* we're already past the end - there's nothing left */
1743
unsigned long limit;
1745
/* calculate the limit */
1746
limit = get_ext()->res_end - cur_seek_pos;
1748
/* apply the limit if the request exceeds it */
1755
* read the data into the array, and return the number of bytes we
1756
* actually manage to read
1758
retval->set_int(arr->read_from_file(get_ext()->fp, idx, len));
1760
/* discard our gc protection */
1767
/* ------------------------------------------------------------------------ */
1769
* Property evaluator - write raw bytes to the file
1771
int CVmObjFile::getp_write_bytes(VMG_ vm_obj_id_t self, vm_val_t *retval,
1774
static CVmNativeCodeDesc desc(1, 2);
1775
uint argc = (in_argc != 0 ? *in_argc : 0);
1777
CVmObjByteArray *arr;
1781
/* check arguments */
1782
if (get_prop_check_argc(retval, in_argc, &desc))
1785
/* make sure we are allowed to perform operations on the file */
1786
check_valid_file(vmg0_);
1788
/* check that we have write access */
1789
check_write_access(vmg0_);
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);
1797
/* we know it's a byte array, so we can simply cast it */
1798
arr = (CVmObjByteArray *)vm_objp(vmg_ arr_val.val.obj);
1800
/* assume we'll write the entire byte array */
1802
len = arr->get_element_count();
1804
/* if we have a starting index, retrieve it */
1806
idx = (unsigned long)CVmBif::pop_int_val(vmg0_);
1808
/* if we have a length, retrieve it */
1810
len = (unsigned long)CVmBif::pop_int_val(vmg0_);
1812
/* push a self-reference for gc protection */
1813
G_stk->push()->set_obj(self);
1815
/* flush stdio buffers as needed and note the read operation */
1816
switch_read_write_mode(TRUE);
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),
1823
if (arr->write_to_file(get_ext()->fp, idx, len))
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");
1830
/* discard our gc protection */
1833
/* no return value - return nil by default */
1840
/* ------------------------------------------------------------------------ */
1842
* Property evaluator - get the seek position
1844
int CVmObjFile::getp_get_pos(VMG_ vm_obj_id_t self, vm_val_t *retval,
1847
static CVmNativeCodeDesc desc(0);
1848
unsigned long cur_pos;
1850
/* check arguments */
1851
if (get_prop_check_argc(retval, argc, &desc))
1854
/* make sure we are allowed to perform operations on the file */
1855
check_valid_file(vmg0_);
1857
/* get the current seek position */
1858
cur_pos = osfpos(get_ext()->fp);
1860
/* if this is a resource file, adjust for the base offset */
1861
cur_pos -= get_ext()->res_start;
1863
/* return the seek position */
1864
retval->set_int(cur_pos);
1870
/* ------------------------------------------------------------------------ */
1872
* Property evaluator - set the seek position
1874
int CVmObjFile::getp_set_pos(VMG_ vm_obj_id_t self, vm_val_t *retval,
1877
static CVmNativeCodeDesc desc(1);
1878
int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
1881
/* check arguments */
1882
if (get_prop_check_argc(retval, argc, &desc))
1885
/* make sure we are allowed to perform operations on the file */
1886
check_valid_file(vmg0_);
1888
/* note the seeking operation */
1889
note_file_seek(vmg_ self, TRUE);
1891
/* retrieve the target seek position */
1892
pos = CVmBif::pop_long_val(vmg0_);
1894
/* adjust for the resource base offset */
1895
pos += get_ext()->res_start;
1898
* if this is a resource file, move the position at most to the first
1899
* byte after the end of the resource
1901
if (is_res_file && pos > get_ext()->res_end)
1902
pos = get_ext()->res_end;
1904
/* seek to the new position */
1905
osfseek(get_ext()->fp, pos, OSFSK_SET);
1907
/* no return value */
1914
/* ------------------------------------------------------------------------ */
1916
* Property evaluator - set position to end of file
1918
int CVmObjFile::getp_set_pos_end(VMG_ vm_obj_id_t self, vm_val_t *retval,
1921
static CVmNativeCodeDesc desc(0);
1922
int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
1924
/* check arguments */
1925
if (get_prop_check_argc(retval, argc, &desc))
1928
/* make sure we are allowed to perform operations on the file */
1929
check_valid_file(vmg0_);
1931
/* note the seeking operation */
1932
note_file_seek(vmg_ self, TRUE);
1934
/* handle according to whether it's a resource or not */
1937
/* resource - seek to the first byte after the resource data */
1938
osfseek(get_ext()->fp, get_ext()->res_end, OSFSK_SET);
1942
/* normal file - simply seek to the end of the file */
1943
osfseek(get_ext()->fp, 0, OSFSK_END);
1946
/* no return value */
1953
/* ------------------------------------------------------------------------ */
1955
* Property evaluator - get size
1957
int CVmObjFile::getp_get_size(VMG_ vm_obj_id_t self, vm_val_t *retval,
1960
static CVmNativeCodeDesc desc(0);
1961
int is_res_file = ((get_ext()->flags & VMOBJFILE_IS_RESOURCE) != 0);
1963
/* check arguments */
1964
if (get_prop_check_argc(retval, argc, &desc))
1967
/* make sure we are allowed to perform operations on the file */
1968
check_valid_file(vmg0_);
1970
/* note the seeking operation */
1971
note_file_seek(vmg_ self, TRUE);
1973
/* handle according to whether it's a resource or not */
1976
/* resource - we know the size from the resource descriptor */
1977
retval->set_int(get_ext()->res_end - get_ext()->res_start + 1);
1981
osfildef *fp = get_ext()->fp;
1982
unsigned long cur_pos;
1985
* It's a normal file. Remember the current seek position, then
1986
* seek to the end of the file.
1988
cur_pos = osfpos(fp);
1989
osfseek(fp, 0, OSFSK_END);
1991
/* the current position gives us the length of the file */
1992
retval->set_int(osfpos(fp));
1994
/* seek back to where we started */
1995
osfseek(fp, cur_pos, OSFSK_SET);
2002
/* ------------------------------------------------------------------------ */
2004
* Refill the read buffer. Returns true if the buffer contains any data
2005
* on return, false if we're at end of file.
2007
int vmobjfile_readbuf_t::refill(CCharmapToUni *charmap,
2008
osfildef *fp, int is_res_file,
2009
unsigned long res_seek_end)
2011
unsigned long read_limit;
2013
/* if the buffer isn't empty, ignore the request */
2017
/* presume there's no read limit */
2020
/* if it's a resource file, limit the size */
2023
unsigned long cur_seek_ofs;
2025
/* make sure we're not already past the end */
2026
cur_seek_ofs = osfpos(fp);
2027
if (cur_seek_ofs >= res_seek_end)
2030
/* calculate the amount of data remaining in the resource */
2031
read_limit = res_seek_end - cur_seek_ofs;
2035
rem = charmap->read_file(fp, buf, sizeof(buf), read_limit);
2037
/* read from the start of the buffer */
2040
/* indicate that we have more data to read */