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
20static 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
32static 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) < 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
90static void ficlOpenFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
91{
92    ficlFopen(pVM, "a");
93}
94
95
96static void ficlCreateFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
97{
98    ficlFopen(pVM, "w");
99}
100
101
102static int closeFiclFILE(ficlFILE *ff) /* ( fileid -- ior ) */
103{
104    FILE *f = ff->f;
105    free(ff);
106    return !fclose(f);
107}
108
109static void ficlCloseFile(FICL_VM *pVM) /* ( fileid -- ior ) */
110{
111    ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
112    pushIor(pVM, closeFiclFILE(ff));
113}
114
115static 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
127static 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
150static 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
179static 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
189static 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
200static 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
211static 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
282static 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
298static 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
339static 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
352static 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
366static 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
376static 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
386static 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
400void 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