xref: /freebsd/stand/ficl/fileaccess.c (revision 783d3ff6d7fae619db8a7990b8a6387de0c677b5)
1 
2 #include <errno.h>
3 #include <stdlib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <ctype.h>
7 #include <sys/stat.h>
8 #include "ficl.h"
9 
10 #if FICL_WANT_FILE
11 /*
12 **
13 ** fileaccess.c
14 **
15 ** Implements all of the File Access word set that can be implemented in portable C.
16 **
17 */
18 
19 static void pushIor(FICL_VM *pVM, int success)
20 {
21     int ior;
22     if (success)
23         ior = 0;
24     else
25         ior = errno;
26     stackPushINT(pVM->pStack, ior);
27 }
28 
29 
30 
31 static void ficlFopen(FICL_VM *pVM, char *writeMode) /* ( c-addr u fam -- fileid ior ) */
32 {
33     int fam = stackPopINT(pVM->pStack);
34     int length = stackPopINT(pVM->pStack);
35     void *address = (void *)stackPopPtr(pVM->pStack);
36     char mode[4];
37     FILE *f;
38 
39     char *filename = (char *)alloca(length + 1);
40     memcpy(filename, address, length);
41     filename[length] = 0;
42 
43     *mode = 0;
44 
45     switch (FICL_FAM_OPEN_MODE(fam))
46         {
47         case 0:
48             stackPushPtr(pVM->pStack, NULL);
49             stackPushINT(pVM->pStack, EINVAL);
50             return;
51         case FICL_FAM_READ:
52             strcat(mode, "r");
53             break;
54         case FICL_FAM_WRITE:
55             strcat(mode, writeMode);
56             break;
57         case FICL_FAM_READ | FICL_FAM_WRITE:
58             strcat(mode, writeMode);
59             strcat(mode, "+");
60             break;
61         }
62 
63     strcat(mode, (fam & FICL_FAM_BINARY) ? "b" : "t");
64 
65     f = fopen(filename, mode);
66     if (f == NULL)
67         stackPushPtr(pVM->pStack, NULL);
68     else
69 #ifdef LOADER_VERIEXEC
70 	if (*mode == 'r' &&
71 	    verify_file(fileno(f), filename, 0, VE_GUESS, __func__) < 0) {
72 	    fclose(f);
73 	    stackPushPtr(pVM->pStack, NULL);
74 	} else
75 #endif
76         {
77 	    ficlFILE *ff = (ficlFILE *)malloc(sizeof(ficlFILE));
78 	    strcpy(ff->filename, filename);
79 	    ff->f = f;
80 	    stackPushPtr(pVM->pStack, ff);
81 
82 	    fseek(f, 0, SEEK_SET);
83 	}
84     pushIor(pVM, f != NULL);
85 }
86 
87 
88 
89 static void ficlOpenFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
90 {
91     ficlFopen(pVM, "a");
92 }
93 
94 
95 static void ficlCreateFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
96 {
97     ficlFopen(pVM, "w");
98 }
99 
100 
101 static int closeFiclFILE(ficlFILE *ff) /* ( fileid -- ior ) */
102 {
103     FILE *f = ff->f;
104     free(ff);
105     return !fclose(f);
106 }
107 
108 static void ficlCloseFile(FICL_VM *pVM) /* ( fileid -- ior ) */
109 {
110     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
111     pushIor(pVM, closeFiclFILE(ff));
112 }
113 
114 static void ficlDeleteFile(FICL_VM *pVM) /* ( c-addr u -- ior ) */
115 {
116     int length = stackPopINT(pVM->pStack);
117     void *address = (void *)stackPopPtr(pVM->pStack);
118 
119     char *filename = (char *)alloca(length + 1);
120     memcpy(filename, address, length);
121     filename[length] = 0;
122 
123     pushIor(pVM, !unlink(filename));
124 }
125 
126 static void ficlRenameFile(FICL_VM *pVM) /* ( c-addr1 u1 c-addr2 u2 -- ior ) */
127 {
128     int length;
129     void *address;
130     char *from;
131     char *to;
132 
133     length = stackPopINT(pVM->pStack);
134     address = (void *)stackPopPtr(pVM->pStack);
135     to = (char *)alloca(length + 1);
136     memcpy(to, address, length);
137     to[length] = 0;
138 
139     length = stackPopINT(pVM->pStack);
140     address = (void *)stackPopPtr(pVM->pStack);
141 
142     from = (char *)alloca(length + 1);
143     memcpy(from, address, length);
144     from[length] = 0;
145 
146     pushIor(pVM, !rename(from, to));
147 }
148 
149 static void ficlFileStatus(FICL_VM *pVM) /* ( c-addr u -- x ior ) */
150 {
151     struct stat statbuf;
152 
153     int length = stackPopINT(pVM->pStack);
154     void *address = (void *)stackPopPtr(pVM->pStack);
155 
156     char *filename = (char *)alloca(length + 1);
157     memcpy(filename, address, length);
158     filename[length] = 0;
159 
160     if (stat(filename, &statbuf) == 0)
161     {
162         /*
163         ** the "x" left on the stack is implementation-defined.
164         ** I push the file's access mode (readable, writeable, is directory, etc)
165         ** as defined by ANSI C.
166         */
167         stackPushINT(pVM->pStack, statbuf.st_mode);
168         stackPushINT(pVM->pStack, 0);
169     }
170     else
171     {
172         stackPushINT(pVM->pStack, -1);
173         stackPushINT(pVM->pStack, ENOENT);
174     }
175 }
176 
177 
178 static void ficlFilePosition(FICL_VM *pVM) /* ( fileid -- ud ior ) */
179 {
180     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
181     long ud = ftell(ff->f);
182     stackPushINT(pVM->pStack, ud);
183     pushIor(pVM, ud != -1);
184 }
185 
186 
187 
188 static long fileSize(FILE *f)
189 {
190     struct stat statbuf;
191     statbuf.st_size = -1;
192     if (fstat(fileno(f), &statbuf) != 0)
193         return -1;
194     return statbuf.st_size;
195 }
196 
197 
198 
199 static void ficlFileSize(FICL_VM *pVM) /* ( fileid -- ud ior ) */
200 {
201     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
202     long ud = fileSize(ff->f);
203     stackPushINT(pVM->pStack, ud);
204     pushIor(pVM, ud != -1);
205 }
206 
207 
208 
209 #define nLINEBUF 256
210 static void ficlIncludeFile(FICL_VM *pVM) /* ( i*x fileid -- j*x ) */
211 {
212     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
213     CELL id = pVM->sourceID;
214     int     result = VM_OUTOFTEXT;
215     long currentPosition, totalSize;
216     long size;
217     pVM->sourceID.p = (void *)ff;
218 
219     currentPosition = ftell(ff->f);
220     totalSize = fileSize(ff->f);
221     size = totalSize - currentPosition;
222 
223     if ((totalSize != -1) && (currentPosition != -1) && (size > 0))
224         {
225         char *buffer = (char *)malloc(size);
226         long got = fread(buffer, 1, size, ff->f);
227         if (got == size)
228             result = ficlExecC(pVM, buffer, size);
229         }
230 
231 #if 0
232     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
233     CELL id = pVM->sourceID;
234     char    cp[nLINEBUF];
235     int     nLine = 0;
236     int     keepGoing;
237     int     result;
238     pVM->sourceID.p = (void *)ff;
239 
240     /* feed each line to ficlExec */
241     keepGoing = TRUE;
242     while (keepGoing && fgets(cp, nLINEBUF, ff->f))
243     {
244         int len = strlen(cp) - 1;
245 
246         nLine++;
247         if (len <= 0)
248             continue;
249 
250         if (cp[len] == '\n')
251             cp[len] = '\0';
252 
253         result = ficlExec(pVM, cp);
254 
255         switch (result)
256         {
257             case VM_OUTOFTEXT:
258             case VM_USEREXIT:
259                 break;
260 
261             default:
262                 pVM->sourceID = id;
263                 keepGoing = FALSE;
264                 break;
265         }
266     }
267 #endif /* 0 */
268     /*
269     ** Pass an empty line with SOURCE-ID == -1 to flush
270     ** any pending REFILLs (as required by FILE wordset)
271     */
272     pVM->sourceID.i = -1;
273     ficlExec(pVM, "");
274 
275     pVM->sourceID = id;
276     closeFiclFILE(ff);
277 }
278 
279 
280 
281 static void ficlReadFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 ior ) */
282 {
283     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
284     int length = stackPopINT(pVM->pStack);
285     void *address = (void *)stackPopPtr(pVM->pStack);
286     int result;
287 
288     clearerr(ff->f);
289     result = fread(address, 1, length, ff->f);
290 
291     stackPushINT(pVM->pStack, result);
292     pushIor(pVM, ferror(ff->f) == 0);
293 }
294 
295 
296 
297 static void ficlReadLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 flag ior ) */
298 {
299     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
300     int length = stackPopINT(pVM->pStack);
301     char *address = (char *)stackPopPtr(pVM->pStack);
302     int error;
303     int flag;
304 
305     if (feof(ff->f))
306         {
307         stackPushINT(pVM->pStack, -1);
308         stackPushINT(pVM->pStack, 0);
309         stackPushINT(pVM->pStack, 0);
310         return;
311         }
312 
313     clearerr(ff->f);
314     *address = 0;
315     fgets(address, length, ff->f);
316 
317     error = ferror(ff->f);
318     if (error != 0)
319         {
320         stackPushINT(pVM->pStack, -1);
321         stackPushINT(pVM->pStack, 0);
322         stackPushINT(pVM->pStack, error);
323         return;
324         }
325 
326     length = strlen(address);
327     flag = (length > 0);
328     if (length && ((address[length - 1] == '\r') || (address[length - 1] == '\n')))
329         length--;
330 
331     stackPushINT(pVM->pStack, length);
332     stackPushINT(pVM->pStack, flag);
333     stackPushINT(pVM->pStack, 0); /* ior */
334 }
335 
336 
337 
338 static void ficlWriteFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
339 {
340     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
341     int length = stackPopINT(pVM->pStack);
342     void *address = (void *)stackPopPtr(pVM->pStack);
343 
344     clearerr(ff->f);
345     fwrite(address, 1, length, ff->f);
346     pushIor(pVM, ferror(ff->f) == 0);
347 }
348 
349 
350 
351 static void ficlWriteLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
352 {
353     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
354     size_t length = (size_t)stackPopINT(pVM->pStack);
355     void *address = (void *)stackPopPtr(pVM->pStack);
356 
357     clearerr(ff->f);
358     if (fwrite(address, 1, length, ff->f) == length)
359         fwrite("\n", 1, 1, ff->f);
360     pushIor(pVM, ferror(ff->f) == 0);
361 }
362 
363 
364 
365 static void ficlRepositionFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
366 {
367     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
368     size_t ud = (size_t)stackPopINT(pVM->pStack);
369 
370     pushIor(pVM, fseek(ff->f, ud, SEEK_SET) == 0);
371 }
372 
373 
374 
375 static void ficlFlushFile(FICL_VM *pVM) /* ( fileid -- ior ) */
376 {
377     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
378     pushIor(pVM, fflush(ff->f) == 0);
379 }
380 
381 
382 
383 #if FICL_HAVE_FTRUNCATE
384 
385 static void ficlResizeFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
386 {
387     ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
388     size_t ud = (size_t)stackPopINT(pVM->pStack);
389 
390     pushIor(pVM, ftruncate(fileno(ff->f), ud) == 0);
391 }
392 
393 #endif /* FICL_HAVE_FTRUNCATE */
394 
395 #endif /* FICL_WANT_FILE */
396 
397 
398 
399 void ficlCompileFile(FICL_SYSTEM *pSys)
400 {
401 #if FICL_WANT_FILE
402     FICL_DICT *dp = pSys->dp;
403     assert(dp);
404 
405     dictAppendWord(dp, "create-file", ficlCreateFile,  FW_DEFAULT);
406     dictAppendWord(dp, "open-file", ficlOpenFile,  FW_DEFAULT);
407     dictAppendWord(dp, "close-file", ficlCloseFile,  FW_DEFAULT);
408     dictAppendWord(dp, "include-file", ficlIncludeFile,  FW_DEFAULT);
409     dictAppendWord(dp, "read-file", ficlReadFile,  FW_DEFAULT);
410     dictAppendWord(dp, "read-line", ficlReadLine,  FW_DEFAULT);
411     dictAppendWord(dp, "write-file", ficlWriteFile,  FW_DEFAULT);
412     dictAppendWord(dp, "write-line", ficlWriteLine,  FW_DEFAULT);
413     dictAppendWord(dp, "file-position", ficlFilePosition,  FW_DEFAULT);
414     dictAppendWord(dp, "file-size", ficlFileSize,  FW_DEFAULT);
415     dictAppendWord(dp, "reposition-file", ficlRepositionFile,  FW_DEFAULT);
416     dictAppendWord(dp, "file-status", ficlFileStatus,  FW_DEFAULT);
417     dictAppendWord(dp, "flush-file", ficlFlushFile,  FW_DEFAULT);
418 
419     dictAppendWord(dp, "delete-file", ficlDeleteFile,  FW_DEFAULT);
420     dictAppendWord(dp, "rename-file", ficlRenameFile,  FW_DEFAULT);
421 
422 #ifdef FICL_HAVE_FTRUNCATE
423     dictAppendWord(dp, "resize-file", ficlResizeFile,  FW_DEFAULT);
424 
425     ficlSetEnv(pSys, "file", FICL_TRUE);
426     ficlSetEnv(pSys, "file-ext", FICL_TRUE);
427 #endif /* FICL_HAVE_FTRUNCATE */
428 #else
429     (void)pSys;
430 #endif /* FICL_WANT_FILE */
431 }
432