Removing commented out methods from IoFile. Code was moved to IoObject.
[io/quag.git] / libs / iovm / source / IoFile.c
blob698b3702d5fdc922f76d87ba0d1aa95a6043939d
1 /*#io
2 File ioDoc(
3 docCopyright("Steve Dekorte", 2002)
4 docLicense("BSD revised")
5 docDescription("""Encapsulates file i/o. Here's an example of opening a file, and reversing it's lines:
7 <pre>
8 file = File clone openForUpdating("/tmp/test")
9 lines = file readLines reverse
10 file rewind
11 lines foreach(i, line, file write(line, "\n"))
12 file close</pre>
13 """)
14 docCategory("FileSystem")
17 #include "IoDate.h"
18 #include "IoFile.h"
19 #include "IoFile_stat.h"
20 #include "IoSeq.h"
21 #include "IoState.h"
22 #include "IoCFunction.h"
23 #include "IoObject.h"
24 #include "IoList.h"
25 #include "IoSeq.h"
26 #include "UArray.h"
27 #include "PortableTruncate.h"
28 #include <errno.h>
29 #include <stdio.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
34 #if !defined(_MSC_VER) && !defined(__SYMBIAN32__)
35 #include <unistd.h> /* ok, this isn't ANSI */
36 #endif
38 #if defined(_MSC_VER) && !defined(__SYMBIAN32__)
39 #include <direct.h>
40 #define getcwd _getcwd
41 #define popen(x,y) _popen(x,y)
42 #define pclose(x) _pclose(x)
43 #endif
45 #if defined(__SYMBIAN32__)
46 static int pclose(void* f) { return 0; }
47 static int popen(void* f, int m) { return 0; }
48 static int rename(void* a, void* b) { return 0; }
49 static char* getcwd(char* buf, int size) { return 0; }
50 #endif
52 #define DATA(self) ((IoFileData *)IoObject_dataPointer(self))
54 IoTag *IoFile_newTag(void *state)
56 IoTag *tag = IoTag_newWithName_("File");
57 IoTag_state_(tag, state);
58 IoTag_cloneFunc_(tag, (IoTagCloneFunc *)IoFile_rawClone);
59 IoTag_markFunc_(tag, (IoTagMarkFunc *)IoFile_mark);
60 IoTag_freeFunc_(tag, (IoTagFreeFunc *)IoFile_free);
61 IoTag_writeToStreamFunc_(tag, (IoTagWriteToStreamFunc *)IoFile_writeToStream_);
62 IoTag_readFromStreamFunc_(tag, (IoTagReadFromStreamFunc *)IoFile_readFromStream_);
63 return tag;
66 IoFile *IoFile_proto(void *state)
68 IoMethodTable methodTable[] = {
69 {"descriptor", IoFile_descriptor},
70 // standard I/O
71 {"standardInput", IoFile_standardInput},
72 {"standardOutput", IoFile_standardOutput},
73 {"standardError", IoFile_standardError},
75 // path
76 {"setPath", IoFile_setPath},
77 {"path", IoFile_path},
78 {"name", IoFile_lastPathComponent},
79 {"temporaryFile", IoFile_temporaryFile},
81 // info
82 {"exists", IoFile_exists},
83 {"size", IoFile_size},
85 // open and close
86 {"openForReading", IoFile_openForReading},
87 {"openForUpdating", IoFile_openForUpdating},
88 {"openForAppending", IoFile_openForAppending},
89 {"mode", IoFile_mode},
91 {"open", IoFile_open},
92 {"popen", IoFile_popen},
93 {"close", IoFile_close},
95 {"isOpen", IoFile_isOpen},
97 // reading
98 {"contents", IoFile_contents},
99 {"asBuffer", IoFile_asBuffer},
100 {"readLine", IoFile_readLine},
101 {"readLines", IoFile_readLines},
102 {"readStringOfLength", IoFile_readStringOfLength_},
103 {"readBufferOfLength", IoFile_readBufferOfLength_},
104 {"readToBufferLength", IoFile_readToBufferLength},
105 {"at", IoFile_at},
106 {"foreach", IoFile_foreach},
108 // writing
109 {"write", IoFile_write},
110 {"atPut", IoFile_atPut},
111 {"flush", IoFile_flush},
113 // positioning
114 {"rewind", IoFile_rewind},
115 {"setPosition", IoFile_position_},
116 {"position", IoFile_position},
117 {"isAtEnd", IoFile_isAtEnd},
119 // other
120 {"remove", IoFile_remove},
121 {"moveTo", IoFile_moveTo_},
122 {"truncateToSize", IoFile_truncateToSize},
124 {NULL, NULL},
127 IoObject *self = IoObject_new(state);
128 IoObject_tag_(self, IoFile_newTag(state));
130 IoObject_setDataPointer_(self, io_calloc(1, sizeof(IoFileData)));
131 DATA(self)->path = IOSYMBOL("");
132 DATA(self)->mode = IOSYMBOL("r+");
133 DATA(self)->flags = IOFILE_FLAGS_NONE;
134 IoState_registerProtoWithFunc_((IoState *)state, self, IoFile_proto);
136 IoObject_addMethodTable_(self, methodTable);
137 IoFile_statInit(self);
138 return self;
141 IoFile *IoFile_rawClone(IoFile *proto)
143 IoObject *self = IoObject_rawClonePrimitive(proto);
144 IoObject_setDataPointer_(self, cpalloc(IoObject_dataPointer(proto), sizeof(IoFileData)));
145 DATA(self)->info = NULL;
146 return self;
149 IoFile *IoFile_new(void *state)
151 IoObject *proto = IoState_protoWithInitFunction_((IoState *)state, IoFile_proto);
152 return IOCLONE(proto);
155 IoFile *IoFile_newWithPath_(void *state, IoSymbol *path)
157 IoFile *self = IoFile_new(state);
158 DATA(self)->path = IOREF(path);
159 return self;
163 IoFile *IoFile_newWithStream_(void *state, FILE *stream)
165 IoFile *self = IoFile_new(state);
166 DATA(self)->stream = stream;
167 return self;
170 IoFile *IoFile_cloneWithPath_(IoFile *self, IoSymbol *path)
172 IoFile *f = IOCLONE(self);
173 DATA(f)->path = IOREF(path);
174 return f;
177 void IoFile_mark(IoFile *self)
179 IoObject_shouldMarkIfNonNull(DATA(self)->path);
180 IoObject_shouldMarkIfNonNull(DATA(self)->mode);
183 void IoFile_free(IoFile *self)
185 if (NULL == IoObject_dataPointer(self))
187 return;
190 IoFile_justClose(self);
192 if (DATA(self)->info)
194 io_free(DATA(self)->info);
197 io_free(IoObject_dataPointer(self));
200 void IoFile_writeToStream_(IoFile *self, BStream *stream)
202 BStream_writeTaggedUArray_(stream, IoSeq_rawUArray(DATA(self)->path));
203 BStream_writeTaggedUArray_(stream, IoSeq_rawUArray(DATA(self)->mode));
206 void *IoFile_readFromStream_(IoFile *self, BStream *stream)
208 IoSymbol *mode;
209 IoSymbol *path = IoState_symbolWithUArray_copy_(IOSTATE, BStream_readTaggedUArray(stream), 1);
210 DATA(self)->path = IOREF(path);
211 mode = IoState_symbolWithUArray_copy_(IOSTATE, BStream_readTaggedUArray(stream), 1);
212 DATA(self)->mode = IOREF(mode);
213 return self;
216 void IoFile_justClose(IoFile *self)
218 FILE *stream = DATA(self)->stream;
220 if (stream)
222 if (stream != stdout && stream != stdin)
224 if (DATA(self)->flags == IOFILE_FLAGS_PIPE)
226 pclose(stream);
228 else
230 fclose(stream);
231 DATA(self)->flags = IOFILE_FLAGS_NONE;
235 DATA(self)->stream = (FILE *)NULL;
239 int IoFile_justExists(IoFile *self)
241 struct stat statInfo;
242 return stat(CSTRING(DATA(self)->path), &statInfo) == 0;
245 int IoFile_create(IoFile *self)
247 FILE *fp = fopen(CSTRING(DATA(self)->path), "w");
249 if (fp)
251 fclose(fp);
252 return 1;
255 return 0;
258 /* ----------------------------------------------------------- */
260 IoObject *IoFile_descriptor(IoFile *self, IoObject *locals, IoMessage *m)
262 /*#io
263 docSlot("escriptor", "Returns the file's descriptor as a number.")
266 return IONUMBER(fileno(DATA(self)->stream));
270 IoObject *IoFile_standardInput(IoFile *self, IoObject *locals, IoMessage *m)
272 /*#io
273 docSlot("standardInput",
274 "Returns a new File whose stream is set to the standard input stream.")
277 IoFile *newFile = IoFile_new(IOSTATE);
278 DATA(newFile)->path = IOREF(IOSYMBOL("<standard input>"));
279 DATA(newFile)->mode = IOREF(IOSYMBOL("r"));
280 DATA(newFile)->stream = stdin;
281 DATA(newFile)->flags = IOFILE_FLAGS_NONE;
282 return newFile;
285 IoObject *IoFile_standardOutput(IoFile *self, IoObject *locals, IoMessage *m)
287 /*#io
288 docSlot("standardOutput",
289 "Returns a new File whose stream is set to the standard output stream.")
292 IoFile *newFile = IoFile_new(IOSTATE);
293 DATA(newFile)->path = IOREF(IOSYMBOL("<standard output>"));
294 DATA(newFile)->mode = IOREF(IOSYMBOL("w"));
295 DATA(newFile)->stream = stdout;
296 DATA(newFile)->flags = IOFILE_FLAGS_NONE;
297 return newFile;
300 IoObject *IoFile_standardError(IoFile *self, IoObject *locals, IoMessage *m)
302 /*#io
303 docSlot("standardError",
304 "Returns a new File whose stream is set to the standard error stream.")
307 IoFile *newFile = IoFile_new(IOSTATE);
308 DATA(newFile)->path = IOREF(IOSYMBOL("<standard error>"));
309 DATA(newFile)->mode = IOREF(IOSYMBOL("w"));
310 DATA(newFile)->stream = stderr;
311 DATA(newFile)->flags = IOFILE_FLAGS_NONE;
312 return newFile;
316 IoObject *IoFile_setPath(IoFile *self, IoObject *locals, IoMessage *m)
318 /*#io
319 docSlot("setPath(aString)",
320 "Sets the file path of the receiver to pathString.
321 The default path is an empty string. Returns self.")
324 DATA(self)->path = IOREF(IoMessage_locals_symbolArgAt_(m, locals, 0));
325 return self;
328 IoObject *IoFile_path(IoFile *self, IoObject *locals, IoMessage *m)
330 /*#io
331 docSlot("path",
332 "Returns the file path of the receiver.")
335 return DATA(self)->path;
338 IoObject *IoFile_lastPathComponent(IoFile *self, IoObject *locals, IoMessage *m)
340 /*#io
341 docSlot("name",
342 "Returns the last path component of the file path.")
345 return IoSeq_lastPathComponent(DATA(self)->path, locals, m);
348 IoObject *IoFile_mode(IoFile *self, IoObject *locals, IoMessage *m)
350 /*#io
351 docSlot("mode",
352 "Returns the open mode of the file(either read, update or append).")
355 char *mode = IoSeq_asCString(DATA(self)->mode);
357 if (!strcmp(mode, "r")) { return IOSYMBOL("read"); }
358 if (!strcmp(mode, "r+")) { return IOSYMBOL("update"); }
359 if (!strcmp(mode, "a+")) { return IOSYMBOL("append"); }
361 return IONIL(self);
364 IoObject *IoFile_temporaryFile(IoFile *self, IoObject *locals, IoMessage *m)
366 /*#io
367 docSlot("temporaryFile",
368 "Returns a new File object with an open temporary file. The file is
369 automatically deleted when the returned File object is closed or garbage collected. ")
372 IoFile *newFile = IoFile_new(IOSTATE);
373 DATA(newFile)->stream = tmpfile();
374 return newFile;
377 IoObject *IoFile_openForReading(IoFile *self, IoObject *locals, IoMessage *m)
379 /*#io
380 docSlot("openForReading(optionalPathString)",
381 "Sets the file mode to read (reading only) and calls open(optionalPathString). ")
383 DATA(self)->mode = IOREF(IOSYMBOL("r"));
384 return IoFile_open(self, locals, m);
387 IoObject *IoFile_openForUpdating(IoFile *self, IoObject *locals, IoMessage *m)
389 /*#io
390 docSlot("openForUpdating(optionalPathString)",
391 "Sets the file mode to update (reading and writing) and calls
392 open(optionalPathString). This will not delete the file if it already exists.
393 Use the remove method first if you need to delete an existing file before opening a new one.")
396 DATA(self)->mode = IOREF(IOSYMBOL("r+"));
397 return IoFile_open(self, locals, m);
400 IoObject *IoFile_openForAppending(IoFile *self, IoObject *locals, IoMessage *m)
402 /*#io
403 docSlot("openForAppending(optionalPathString)",
404 "Sets the file mode to append (writing to the end of the file)
405 and calls open(optionalPathString).")
408 DATA(self)->mode = IOREF(IOSYMBOL("a+"));
409 return IoFile_open(self, locals, m);
412 IoObject *IoFile_open(IoFile *self, IoObject *locals, IoMessage *m)
414 /*#io
415 docSlot("open(optionalPathString)",
416 "Opens the file. Creates one if it does not exist.
417 If the optionalPathString argument is provided, the path is set to it before
418 opening. Returns self or raises an File exception on error.")
421 char *mode = CSTRING(DATA(self)->mode);
423 DATA(self)->flags = IOFILE_FLAGS_NONE;
425 if (IoMessage_argCount(m) > 0)
427 DATA(self)->path = IOREF(IoMessage_locals_symbolArgAt_(m, locals, 0));
430 if (!DATA(self)->stream)
432 if (!IoFile_justExists(self) && strcmp(mode, "r") != 0 )
434 IoFile_create(self);
436 if(!IoFile_justExists(self))
438 IoState_error_(IOSTATE, m, "unable to create file '%s'", CSTRING(DATA(self)->path));
442 DATA(self)->stream = fopen(CSTRING(DATA(self)->path), mode);
445 if (DATA(self)->stream == NULL)
447 IoState_error_(IOSTATE, m, "unable to open file path '%s'", CSTRING(DATA(self)->path));
450 return self;
453 IoObject *IoFile_popen(IoFile *self, IoObject *locals, IoMessage *m)
455 /*#io
456 docSlot("popen",
457 "open as a pipe")
460 DATA(self)->flags = IOFILE_FLAGS_PIPE;
462 if (IoMessage_argCount(m) > 0)
464 DATA(self)->path = IOREF(IoMessage_locals_symbolArgAt_(m, locals, 0));
467 if (DATA(self)->stream)
469 IoFile_justClose(self);
472 #if defined(SANE_POPEN)
473 DATA(self)->mode = IOREF(IOSYMBOL("a+"));
474 DATA(self)->stream = popen(CSTRING(DATA(self)->path), "r+");
475 #elif defined(__SYMBIAN32__)
476 /* Symbian does not implement popen.
477 * (There is popen3() but it is "internal and not intended for use.")
479 DATA(self)->mode = IOREF(IOSYMBOL("r"));
480 DATA(self)->stream = NULL;
481 #else
482 DATA(self)->mode = IOREF(IOSYMBOL("r"));
483 DATA(self)->stream = popen(CSTRING(DATA(self)->path), "r");
484 #endif
485 if (DATA(self)->stream == NULL)
487 IoState_error_(IOSTATE, m, "error executing file path '%s'", CSTRING(DATA(self)->path));
490 return self;
494 IoObject *IoFile_close(IoFile *self, IoObject *locals, IoMessage *m)
496 /*#io
497 docSlot("close",
498 "Closes the receiver if open, otherwise does nothing. Returns self.")
501 IoFile_justClose(self);
502 return self;
505 IoObject *IoFile_flush(IoFile *self, IoObject *locals, IoMessage *m)
507 /*#io
508 docSlot("flush",
509 "Forces any buffered data to be written to disk. Returns self.")
512 fflush(DATA(self)->stream);
513 return self;
516 IoObject *IoFile_rawAsString(IoFile *self)
518 UArray *ba = UArray_new();
520 if (UArray_readFromFilePath_(ba, IoSeq_rawUArray(DATA(self)->path)) == 1)
522 return IoState_symbolWithUArray_copy_(IOSTATE, ba, 0);
524 else
526 UArray_free(ba);
527 IoState_error_(IOSTATE, NULL, "unable to read file '%s'", CSTRING(DATA(self)->path));
530 return IONIL(self);
533 IoObject *IoFile_contents(IoFile *self, IoObject *locals, IoMessage *m)
535 /*#io
536 docSlot("contents", "Returns contents of the file as a mutable Sequence of bytes.")
539 UArray *ba = UArray_new();
540 int result = -1;
542 if (DATA(self)->stream == stdin)
544 result = UArray_readFromCStream_(ba, DATA(self)->stream);
546 else
548 result = UArray_readFromFilePath_(ba, IoSeq_rawUArray(DATA(self)->path));
551 if (result != -1)
553 return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
555 else
557 UArray_free(ba);
558 IoState_error_(IOSTATE, m, "unable to read file '%s'", CSTRING(DATA(self)->path));
561 return IONIL(self);
564 IoObject *IoFile_asBuffer(IoFile *self, IoObject *locals, IoMessage *m)
566 /*#io
567 docSlot("asBuffer", "Opens the receiver in read only mode, reads the whole
568 contents of the file into a buffer object, closes the file and returns the buffer.")
571 UArray *ba = UArray_new();
573 if (UArray_readFromFilePath_(ba, IoSeq_rawUArray(DATA(self)->path)) == 1)
575 return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
577 else
579 UArray_free(ba);
580 IoState_error_(IOSTATE, m, "unable to read file '%s'", CSTRING(DATA(self)->path));
583 return IONIL(self);
586 IoObject *IoFile_exists(IoFile *self, IoObject *locals, IoMessage *m)
588 /*#io
589 docSlot("exists", "Returns true if the file specified by the receiver's
590 path exists, false otherwise.")
593 return IOBOOL(self, IoFile_justExists(self));
596 IoObject *IoFile_remove(IoFile *self, IoObject *locals, IoMessage *m)
598 /*#io
599 docSlot("remove",
600 "Removes the file specified by the receiver's path.
601 Raises an error if the file exists but is not removed. Returns self.")
604 int error;
606 #if defined(__SYMBIAN32__)
607 error = -1;
608 #else
609 error = remove(CSTRING(DATA(self)->path));
610 #endif
612 if (error && IoFile_justExists(self))
614 IoState_error_(IOSTATE, m, "error removing file '%s'", CSTRING(DATA(self)->path));
616 return self;
619 IoObject *IoFile_truncateToSize(IoFile *self, IoObject *locals, IoMessage *m)
621 /*#io
622 docSlot("truncateToSize(numberOfBytes)",
623 "Trunctates the file's size to the numberOfBytes. Returns self.")
626 long newSize = IoMessage_locals_longArgAt_(m, locals, 0);
627 truncate(CSTRING(DATA(self)->path), newSize);
628 return self;
631 IoObject *IoFile_moveTo_(IoFile *self, IoObject *locals, IoMessage *m)
633 /*#io
634 docSlot("moveTo(pathString)",
635 """Moves the file specified by the receiver's path to the
636 new path pathString. Raises an File doesNotExist exception if the
637 file does not exist or a File nameConflict exception if the file
638 nameString already exists.""")
641 IoSymbol *newPath = IoMessage_locals_symbolArgAt_(m, locals, 0);
642 int error = rename(CSTRING(DATA(self)->path), CSTRING(newPath));
644 if (error)
646 IoState_error_(IOSTATE, m, "error moving file '%s' to '%s'", CSTRING(DATA(self)->path), CSTRING(newPath));
648 return self;
651 IoObject *IoFile_write(IoFile *self, IoObject *locals, IoMessage *m)
653 /*#io
654 docSlot("write(aSequence1, aSequence2, ...)",
655 "Writes the arguments to the receiver file. Returns self.")
658 int i;
660 IoFile_assertOpen(self, locals, m);
661 IoFile_assertWrite(self, locals, m);
663 for (i = 0; i < IoMessage_argCount(m); i ++)
665 IoSymbol *string = IoMessage_locals_seqArgAt_(m, locals, i);
666 UArray_writeToCStream_(IoSeq_rawUArray(string), DATA(self)->stream);
668 if (ferror(DATA(self)->stream) != 0)
670 IoState_error_(IOSTATE, m, "error writing to file '%s'",
671 CSTRING(DATA(self)->path));
675 return self;
678 IoObject *IoFile_readLines(IoFile *self, IoObject *locals, IoMessage *m)
680 /*#io
681 docSlot("readLines",
682 "Returns list containing all lines in the file.")
685 IoState *state = IOSTATE;
687 if (!DATA(self)->stream)
689 IoFile_openForReading(self, locals, m);
692 IoFile_assertOpen(self, locals, m);
695 IoList *lines = IoList_new(state);
696 IoObject *newLine;
698 IoState_pushRetainPool(state);
700 for (;;)
702 IoState_clearTopPool(state);
703 newLine = IoFile_readLine(self, locals, m);
705 if (ISNIL(newLine))
707 break;
710 IoList_rawAppend_(lines, newLine);
712 IoState_popRetainPool(state);
714 return lines;
718 IoObject *IoFile_readLine(IoFile *self, IoObject *locals, IoMessage *m)
720 /*#io
721 docSlot("readLine", "Reads the next line of the file and returns it as a
722 string without the return character. Returns Nil if the end of the file has been reached.")
724 //char *path = CSTRING(DATA(self)->path); // tmp for debugging
726 IoFile_assertOpen(self, locals, m);
728 if (feof(DATA(self)->stream) != 0)
730 clearerr(DATA(self)->stream);
731 return IONIL(self);
733 else
735 UArray *ba = UArray_new();
736 int error;
737 unsigned char didRead = UArray_readLineFromCStream_(ba, DATA(self)->stream);
739 if (!didRead)
741 UArray_free(ba);
742 return IONIL(self);
745 error = ferror(DATA(self)->stream);
747 if (error != 0)
749 UArray_free(ba);
750 clearerr(DATA(self)->stream);
751 IoState_error_(IOSTATE, m, "error reading from file '%s'", CSTRING(DATA(self)->path));
752 return IONIL(self);
755 return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
756 /*return IoState_symbolWithUArray_copy_(IOSTATE, ba, 0);*/
760 UArray *IoFile_readUArrayOfLength_(IoFile *self, IoObject *locals, IoMessage *m)
762 size_t length = IoMessage_locals_sizetArgAt_(m, locals, 0);
763 UArray *ba = UArray_new();
764 IoFile_assertOpen(self, locals, m);
766 UArray_readNumberOfItems_fromCStream_(ba, length, DATA(self)->stream);
768 if (ferror(DATA(self)->stream) != 0)
770 clearerr(DATA(self)->stream);
771 UArray_free(ba);
772 IoState_error_(IOSTATE, m, "error reading file '%s'", CSTRING(DATA(self)->path));
775 if (!UArray_size(ba))
777 UArray_free(ba);
778 return NULL;
781 return ba;
784 IoObject *IoFile_readToBufferLength(IoFile *self, IoObject *locals, IoMessage *m)
786 /*#io
787 docSlot("readToBufferOfLength(aBuffer, aNumber)",
788 "Reads at most aNumber number of items and appends them to aBuffer.
789 Returns number of items read.")
792 IoSeq *buffer = IoMessage_locals_mutableSeqArgAt_(m, locals, 0);
793 size_t length = IoMessage_locals_longArgAt_(m, locals, 1);
794 UArray *ba = IoSeq_rawUArray(buffer);
795 size_t itemsRead = UArray_readNumberOfItems_fromCStream_(ba, length, DATA(self)->stream);
796 return IONUMBER(itemsRead);
799 IoObject *IoFile_readBufferOfLength_(IoFile *self, IoObject *locals, IoMessage *m)
801 /*#io
802 docSlot("readBufferOfLength(aNumber)",
803 "Reads a Buffer of the specified length and returns it.
804 Returns Nil if the end of the file has been reached.")
807 UArray *ba = IoFile_readUArrayOfLength_(self, locals, m);
809 if (!ba)
811 return IONIL(self);
814 return IoSeq_newWithUArray_copy_(IOSTATE, ba, 0);
817 IoObject *IoFile_readStringOfLength_(IoFile *self, IoObject *locals, IoMessage *m)
819 /*#io
820 docSlot("readStringOfLength(aNumber)",
821 "Reads a String of the specified length and returns it.
822 Returns Nil if the end of the file has been reached.")
825 UArray *ba = IoFile_readUArrayOfLength_(self, locals, m);
827 if (!ba)
829 return IONIL(self);
832 return IoState_symbolWithUArray_copy_(IOSTATE, ba, 0);
835 IoObject *IoFile_rewind(IoFile *self, IoObject *locals, IoMessage *m)
837 /*#io
838 docSlot("rewind",
839 "Sets the file position pointer to the beginning of the file.")
841 IoFile_assertOpen(self, locals, m);
843 if (DATA(self)->stream)
845 rewind(DATA(self)->stream);
848 return self;
851 IoObject *IoFile_position_(IoFile *self, IoObject *locals, IoMessage *m)
853 /*#io
854 docSlot("setPosition(aNumber)",
855 "Sets the file position pointer to the byte specified by aNumber. Returns self.")
858 long pos = IoMessage_locals_longArgAt_(m, locals, 0);
859 IoFile_assertOpen(self, locals, m);
861 if (fseek(DATA(self)->stream, pos, 0) != 0)
863 IoState_error_(IOSTATE, m, "unable to set position %i file path '%s'",
864 (int)pos, CSTRING(DATA(self)->path));
867 return self;
870 IoObject *IoFile_position(IoFile *self, IoObject *locals, IoMessage *m)
872 /*#io
873 docSlot("position",
874 "Returns the current file pointer byte position as a Number.")
877 IoFile_assertOpen(self, locals, m);
878 return IONUMBER(ftell(DATA(self)->stream));
881 IoObject *IoFile_isAtEnd(IoFile *self, IoObject *locals, IoMessage *m)
883 /*#io
884 docSlot("isAtEnd",
885 "Returns true if the file is at it's end. Otherwise returns false.")
888 IoFile_assertOpen(self, locals, m);
889 return IOBOOL(self, feof(DATA(self)->stream) != 0);
892 IoObject *IoFile_size(IoFile *self, IoObject *locals, IoMessage *m)
894 /*#io
895 docSlot("size",
896 "Returns the file size in bytes.")
899 FILE *fp = fopen(CSTRING(DATA(self)->path), "r");
901 if (fp)
903 int fileSize;
904 fseek(fp, 0, SEEK_END);
905 fileSize = ftell(fp);
906 fclose(fp);
907 return IONUMBER(fileSize);
909 else
911 IoState_error_(IOSTATE, m, "unable to open file '%s'", CSTRING(DATA(self)->path));
914 return IONIL(self);
917 IoObject *IoFile_isOpen(IoFile *self, IoObject *locals, IoMessage *m)
919 /*#io
920 docSlot("isOpen",
921 "Returns self if the file is open. Otherwise returns Nil.")
924 return IOBOOL(self, DATA(self)->stream != 0);
927 IoObject *IoFile_assertOpen(IoFile *self, IoObject *locals, IoMessage *m)
929 if (!DATA(self)->stream)
931 IoState_error_(IOSTATE, m, "file '%s' not yet open", CSTRING(DATA(self)->path));
933 return self;
936 IoObject *IoFile_assertWrite(IoFile *self, IoObject *locals, IoMessage *m)
938 char *mode = IoSeq_asCString(DATA(self)->mode);
940 if ((strcmp(mode, "r+")) && (strcmp(mode, "a+")) && (strcmp(mode, "w")))
942 IoState_error_(IOSTATE, m, "file '%s' not open for write", CSTRING(DATA(self)->path));
945 return self;
948 IoObject *IoFile_at(IoFile *self, IoObject *locals, IoMessage *m)
950 /*#io
951 docSlot("at(aNumber)",
952 "Returns a Number containing the byte at the specified
953 byte index or Nil if the index is out of bounds.")
956 int byte;
958 IoFile_assertOpen(self, locals, m);
959 IoFile_position_(self, locals, m); /* works since first arg is the same */
960 byte = fgetc(DATA(self)->stream);
962 if (byte == EOF)
964 return IONIL(self);
967 return IONUMBER(byte);
970 IoObject *IoFile_atPut(IoFile *self, IoObject *locals, IoMessage *m)
972 /*#io
973 docSlot("atPut(positionNumber, byteNumber)",
974 "Writes the byte value of byteNumber to the file position
975 positionNumber. Returns self.")
978 int c = IoMessage_locals_intArgAt_(m, locals, 1);
980 IoFile_assertOpen(self, locals, m);
981 IoFile_assertWrite(self, locals, m);
982 IoFile_position_(self, locals, m); // works since first arg is the same
984 if (fputc(c, DATA(self)->stream) == EOF)
986 int pos = IoMessage_locals_intArgAt_(m, locals, 0); // BUG - this may not be the same when evaled
987 IoState_error_(IOSTATE, m, "error writing to position %i in file '%s'", pos, CSTRING(DATA(self)->path));
990 return self;
993 IoObject *IoFile_foreach(IoFile *self, IoObject *locals, IoMessage *m)
995 /*#io
996 docSlot("foreach(optionalIndex, value, message)",
997 """For each byte, set index to the index of the byte
998 and value to the number containing the byte value and execute aMessage.
999 Example usage:
1000 <pre>
1001 aFile foreach(i, v, writeln("byte at ", i, " is ", v))
1002 aFile foreach(v, writeln("byte ", v))
1003 </pre>
1004 """)
1007 IoObject *result = IONIL(self);
1009 IoSymbol *indexSlotName, *characterSlotName;
1010 IoMessage *doMessage;
1011 int i = 0;
1013 IoMessage_foreachArgs(m, self, &indexSlotName, &characterSlotName, &doMessage);
1015 for (;;)
1017 int c = getc(DATA(self)->stream);
1019 if (c == EOF)
1021 break;
1024 if (indexSlotName)
1026 IoObject_setSlot_to_(locals, indexSlotName, IONUMBER(i));
1029 IoObject_setSlot_to_(locals, characterSlotName, IONUMBER(c));
1030 result = IoMessage_locals_performOn_(doMessage, locals, locals);
1032 if (IoState_handleStatus(IOSTATE))
1034 break;
1037 i ++;
1039 return result;