1 
2 /*---------------------------------------------------------------------
3   File Handling
4   ---------------------------------------------------------------------*/
5 
6 
7 /*---------------------------------------------------------------------
8   I keep an array of file handles. RETRO will use the index number as
9   its representation of the file.
10   ---------------------------------------------------------------------*/
11 
12 FILE *OpenFileHandles[MAX_OPEN_FILES];
13 
14 /*---------------------------------------------------------------------
15   `files_get_handle()` returns a file handle, or 0 if there are no
16   available handle slots in the array.
17   ---------------------------------------------------------------------*/
18 
files_get_handle()19 CELL files_get_handle() {
20   CELL i;
21   for(i = 1; i < MAX_OPEN_FILES; i++)
22     if (OpenFileHandles[i] == 0)
23       return i;
24   return 0;
25 }
26 
27 
28 /*---------------------------------------------------------------------
29   `file_open()` opens a file. This pulls from the RETRO data stack:
30 
31   - mode     (number, TOS)
32   - filename (string, NOS)
33 
34   Modes are:
35 
36   | Mode | Corresponds To | Description          |
37   | ---- | -------------- | -------------------- |
38   |  0   | rb             | Open for reading     |
39   |  1   | w              | Open for writing     |
40   |  2   | a              | Open for append      |
41   |  3   | rb+            | Open for read/update |
42 
43   The file name should be a NULL terminated string. This will attempt
44   to open the requested file and will return a handle (index number
45   into the `OpenFileHandles` array).
46   ---------------------------------------------------------------------*/
47 
file_open()48 void file_open() {
49   CELL slot, mode, name;
50   char *request;
51   slot = files_get_handle();
52   mode = stack_pop();
53   name = stack_pop();
54   request = string_extract(name);
55   if (slot > 0) {
56     if (mode == 0)  OpenFileHandles[slot] = fopen(request, "rb");
57     if (mode == 1)  OpenFileHandles[slot] = fopen(request, "w");
58     if (mode == 2)  OpenFileHandles[slot] = fopen(request, "a");
59     if (mode == 3)  OpenFileHandles[slot] = fopen(request, "rb+");
60   }
61   if (OpenFileHandles[slot] == NULL) {
62     OpenFileHandles[slot] = 0;
63     slot = 0;
64   }
65   stack_push(slot);
66 }
67 
68 
69 /*---------------------------------------------------------------------
70   `file_read()` reads a byte from a file. This takes a file pointer
71   from the stack and pushes the character that was read to the stack.
72   ---------------------------------------------------------------------*/
73 
file_read()74 void file_read() {
75   CELL c;
76   CELL slot = stack_pop();
77 #ifndef NOCHECKS
78   if (slot <= 0 || slot > MAX_OPEN_FILES || OpenFileHandles[slot] == 0) {
79     printf("\nERROR (nga/file_read): Invalid file handle\n");
80     exit(1);
81   }
82 #endif
83   c = fgetc(OpenFileHandles[slot]);
84   stack_push(feof(OpenFileHandles[slot]) ? 0 : c);
85 }
86 
87 
88 /*---------------------------------------------------------------------
89   `file_write()` writes a byte to a file. This takes a file pointer
90   (TOS) and a byte (NOS) from the stack. It does not return any values
91   on the stack.
92   ---------------------------------------------------------------------*/
93 
file_write()94 void file_write() {
95   CELL slot, c, r;
96   slot = stack_pop();
97 #ifndef NOCHECKS
98   if (slot <= 0 || slot > MAX_OPEN_FILES || OpenFileHandles[slot] == 0) {
99     printf("\nERROR (nga/file_write): Invalid file handle\n");
100     exit(1);
101   }
102 #endif
103   c = stack_pop();
104   r = fputc(c, OpenFileHandles[slot]);
105 }
106 
107 
108 /*---------------------------------------------------------------------
109   `file_close()` closes a file. This takes a file handle from the
110   stack and does not return anything on the stack.
111   ---------------------------------------------------------------------*/
112 
file_close()113 void file_close() {
114   CELL slot = stack_pop();
115 #ifndef NOCHECKS
116   if (slot <= 0 || slot > MAX_OPEN_FILES || OpenFileHandles[slot] == 0) {
117     printf("\nERROR (nga/file_close): Invalid file handle\n");
118     exit(1);
119   }
120 #endif
121   fclose(OpenFileHandles[slot]);
122   OpenFileHandles[slot] = 0;
123 }
124 
125 
126 /*---------------------------------------------------------------------
127   `file_get_position()` provides the current index into a file. This
128   takes the file handle from the stack and returns the offset.
129   ---------------------------------------------------------------------*/
130 
file_get_position()131 void file_get_position() {
132   CELL slot = stack_pop();
133 #ifndef NOCHECKS
134   if (slot <= 0 || slot > MAX_OPEN_FILES || OpenFileHandles[slot] == 0) {
135     printf("\nERROR (nga/file_get_position): Invalid file handle\n");
136     exit(1);
137   }
138 #endif
139   stack_push((CELL) ftell(OpenFileHandles[slot]));
140 }
141 
142 
143 /*---------------------------------------------------------------------
144   `file_set_position()` changes the current index into a file to the
145   specified one. This takes a file handle (TOS) and new offset (NOS)
146   from the stack.
147   ---------------------------------------------------------------------*/
148 
file_set_position()149 void file_set_position() {
150   CELL slot, pos;
151   slot = stack_pop();
152   pos  = stack_pop();
153 #ifndef NOCHECKS
154   if (slot <= 0 || slot > MAX_OPEN_FILES || OpenFileHandles[slot] == 0) {
155     printf("\nERROR (nga/file_set_position): Invalid file handle\n");
156     exit(1);
157   }
158 #endif
159   fseek(OpenFileHandles[slot], pos, SEEK_SET);
160 }
161 
162 
163 /*---------------------------------------------------------------------
164   `file_get_size()` returns the size of a file, or 0 if empty. If the
165   file is a directory, it returns -1. It takes a file handle from the
166   stack.
167   ---------------------------------------------------------------------*/
168 
file_get_size()169 void file_get_size() {
170   CELL slot, current, r, size;
171   struct stat buffer;
172   slot = stack_pop();
173 #ifndef NOCHECKS
174   if (slot <= 0 || slot > MAX_OPEN_FILES || OpenFileHandles[slot] == 0) {
175     printf("\nERROR (nga/file_get_size): Invalid file handle\n");
176     exit(1);
177   }
178 #endif
179   fstat(fileno(OpenFileHandles[slot]), &buffer);
180   if (!S_ISDIR(buffer.st_mode)) {
181     current = ftell(OpenFileHandles[slot]);
182     r = fseek(OpenFileHandles[slot], 0, SEEK_END);
183     size = ftell(OpenFileHandles[slot]);
184     fseek(OpenFileHandles[slot], current, SEEK_SET);
185   } else {
186     r = -1;
187     size = 0;
188   }
189   stack_push((r == 0) ? size : 0);
190 }
191 
192 
193 /*---------------------------------------------------------------------
194   `file_delete()` removes a file. This takes a file name (as a string)
195   from the stack.
196   ---------------------------------------------------------------------*/
197 
file_delete()198 void file_delete() {
199   char *request;
200   CELL name = stack_pop();
201   request = string_extract(name);
202   unlink(request);
203 }
204 
205 
206 /*---------------------------------------------------------------------
207   `file_flush()` flushes any pending writes to disk. This takes a
208   file handle from the stack.
209   ---------------------------------------------------------------------*/
210 
file_flush()211 void file_flush() {
212   CELL slot;
213   slot = stack_pop();
214 #ifndef NOCHECKS
215   if (slot <= 0 || slot > MAX_OPEN_FILES || OpenFileHandles[slot] == 0) {
216     printf("\nERROR (nga/file_flush): Invalid file handle\n");
217     exit(1);
218   }
219 #endif
220   fflush(OpenFileHandles[slot]);
221 }
222 
223 
224 Handler FileActions[10] = {
225   file_open,
226   file_close,
227   file_read,
228   file_write,
229   file_get_position,
230   file_set_position,
231   file_get_size,
232   file_delete,
233   file_flush
234 };
235 
io_filesystem_query()236 void io_filesystem_query() {
237   stack_push(0);
238   stack_push(4);
239 }
240 
io_filesystem_handler()241 void io_filesystem_handler() {
242   FileActions[stack_pop()]();
243 }
244