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