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