/* $FreeBSD$ */
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/stat.h>
#include "ficl.h"
#if FICL_WANT_FILE
/*
**
** fileaccess.c
**
** Implements all of the File Access word set that can be implemented in portable C.
**
*/
static void pushIor(FICL_VM *pVM, int success)
{
int ior;
if (success)
ior = 0;
else
ior = errno;
stackPushINT(pVM->pStack, ior);
}
static void ficlFopen(FICL_VM *pVM, char *writeMode) /* ( c-addr u fam -- fileid ior ) */
{
int fam = stackPopINT(pVM->pStack);
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
char mode[4];
FILE *f;
char *filename = (char *)alloca(length + 1);
memcpy(filename, address, length);
filename[length] = 0;
*mode = 0;
switch (FICL_FAM_OPEN_MODE(fam))
{
case 0:
stackPushPtr(pVM->pStack, NULL);
stackPushINT(pVM->pStack, EINVAL);
return;
case FICL_FAM_READ:
strcat(mode, "r");
break;
case FICL_FAM_WRITE:
strcat(mode, writeMode);
break;
case FICL_FAM_READ | FICL_FAM_WRITE:
strcat(mode, writeMode);
strcat(mode, "+");
break;
}
strcat(mode, (fam & FICL_FAM_BINARY) ? "b" : "t");
f = fopen(filename, mode);
if (f == NULL)
stackPushPtr(pVM->pStack, NULL);
else
{
ficlFILE *ff = (ficlFILE *)malloc(sizeof(ficlFILE));
strcpy(ff->filename, filename);
ff->f = f;
stackPushPtr(pVM->pStack, ff);
fseek(f, 0, SEEK_SET);
}
pushIor(pVM, f != NULL);
}
static void ficlOpenFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
{
ficlFopen(pVM, "a");
}
static void ficlCreateFile(FICL_VM *pVM) /* ( c-addr u fam -- fileid ior ) */
{
ficlFopen(pVM, "w");
}
static int closeFiclFILE(ficlFILE *ff) /* ( fileid -- ior ) */
{
FILE *f = ff->f;
free(ff);
return !fclose(f);
}
static void ficlCloseFile(FICL_VM *pVM) /* ( fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
pushIor(pVM, closeFiclFILE(ff));
}
static void ficlDeleteFile(FICL_VM *pVM) /* ( c-addr u -- ior ) */
{
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
char *filename = (char *)alloca(length + 1);
memcpy(filename, address, length);
filename[length] = 0;
pushIor(pVM, !unlink(filename));
}
static void ficlRenameFile(FICL_VM *pVM) /* ( c-addr1 u1 c-addr2 u2 -- ior ) */
{
int length;
void *address;
char *from;
char *to;
length = stackPopINT(pVM->pStack);
address = (void *)stackPopPtr(pVM->pStack);
to = (char *)alloca(length + 1);
memcpy(to, address, length);
to[length] = 0;
length = stackPopINT(pVM->pStack);
address = (void *)stackPopPtr(pVM->pStack);
from = (char *)alloca(length + 1);
memcpy(from, address, length);
from[length] = 0;
pushIor(pVM, !rename(from, to));
}
static void ficlFileStatus(FICL_VM *pVM) /* ( c-addr u -- x ior ) */
{
struct stat statbuf;
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
char *filename = (char *)alloca(length + 1);
memcpy(filename, address, length);
filename[length] = 0;
if (stat(filename, &statbuf) == 0)
{
/*
** the "x" left on the stack is implementation-defined.
** I push the file's access mode (readable, writeable, is directory, etc)
** as defined by ANSI C.
*/
stackPushINT(pVM->pStack, statbuf.st_mode);
stackPushINT(pVM->pStack, 0);
}
else
{
stackPushINT(pVM->pStack, -1);
stackPushINT(pVM->pStack, ENOENT);
}
}
static void ficlFilePosition(FICL_VM *pVM) /* ( fileid -- ud ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
long ud = ftell(ff->f);
stackPushINT(pVM->pStack, ud);
pushIor(pVM, ud != -1);
}
static long fileSize(FILE *f)
{
struct stat statbuf;
statbuf.st_size = -1;
if (fstat(fileno(f), &statbuf) != 0)
return -1;
return statbuf.st_size;
}
static void ficlFileSize(FICL_VM *pVM) /* ( fileid -- ud ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
long ud = fileSize(ff->f);
stackPushINT(pVM->pStack, ud);
pushIor(pVM, ud != -1);
}
#define nLINEBUF 256
static void ficlIncludeFile(FICL_VM *pVM) /* ( i*x fileid -- j*x ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
CELL id = pVM->sourceID;
int result = VM_OUTOFTEXT;
long currentPosition, totalSize;
long size;
pVM->sourceID.p = (void *)ff;
currentPosition = ftell(ff->f);
totalSize = fileSize(ff->f);
size = totalSize - currentPosition;
if ((totalSize != -1) && (currentPosition != -1) && (size > 0))
{
char *buffer = (char *)malloc(size);
long got = fread(buffer, 1, size, ff->f);
if (got == size)
result = ficlExecC(pVM, buffer, size);
}
#if 0
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
CELL id = pVM->sourceID;
char cp[nLINEBUF];
int nLine = 0;
int keepGoing;
int result;
pVM->sourceID.p = (void *)ff;
/* feed each line to ficlExec */
keepGoing = TRUE;
while (keepGoing && fgets(cp, nLINEBUF, ff->f))
{
int len = strlen(cp) - 1;
nLine++;
if (len <= 0)
continue;
if (cp[len] == '\n')
cp[len] = '\0';
result = ficlExec(pVM, cp);
switch (result)
{
case VM_OUTOFTEXT:
case VM_USEREXIT:
break;
default:
pVM->sourceID = id;
keepGoing = FALSE;
break;
}
}
#endif /* 0 */
/*
** Pass an empty line with SOURCE-ID == -1 to flush
** any pending REFILLs (as required by FILE wordset)
*/
pVM->sourceID.i = -1;
ficlExec(pVM, "");
pVM->sourceID = id;
closeFiclFILE(ff);
}
static void ficlReadFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
int result;
clearerr(ff->f);
result = fread(address, 1, length, ff->f);
stackPushINT(pVM->pStack, result);
pushIor(pVM, ferror(ff->f) == 0);
}
static void ficlReadLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- u2 flag ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
int length = stackPopINT(pVM->pStack);
char *address = (char *)stackPopPtr(pVM->pStack);
int error;
int flag;
if (feof(ff->f))
{
stackPushINT(pVM->pStack, -1);
stackPushINT(pVM->pStack, 0);
stackPushINT(pVM->pStack, 0);
return;
}
clearerr(ff->f);
*address = 0;
fgets(address, length, ff->f);
error = ferror(ff->f);
if (error != 0)
{
stackPushINT(pVM->pStack, -1);
stackPushINT(pVM->pStack, 0);
stackPushINT(pVM->pStack, error);
return;
}
length = strlen(address);
flag = (length > 0);
if (length && ((address[length - 1] == '\r') || (address[length - 1] == '\n')))
length--;
stackPushINT(pVM->pStack, length);
stackPushINT(pVM->pStack, flag);
stackPushINT(pVM->pStack, 0); /* ior */
}
static void ficlWriteFile(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
int length = stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
clearerr(ff->f);
fwrite(address, 1, length, ff->f);
pushIor(pVM, ferror(ff->f) == 0);
}
static void ficlWriteLine(FICL_VM *pVM) /* ( c-addr u1 fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
size_t length = (size_t)stackPopINT(pVM->pStack);
void *address = (void *)stackPopPtr(pVM->pStack);
clearerr(ff->f);
if (fwrite(address, 1, length, ff->f) == length)
fwrite("\n", 1, 1, ff->f);
pushIor(pVM, ferror(ff->f) == 0);
}
static void ficlRepositionFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
size_t ud = (size_t)stackPopINT(pVM->pStack);
pushIor(pVM, fseek(ff->f, ud, SEEK_SET) == 0);
}
static void ficlFlushFile(FICL_VM *pVM) /* ( fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
pushIor(pVM, fflush(ff->f) == 0);
}
#if FICL_HAVE_FTRUNCATE
static void ficlResizeFile(FICL_VM *pVM) /* ( ud fileid -- ior ) */
{
ficlFILE *ff = (ficlFILE *)stackPopPtr(pVM->pStack);
size_t ud = (size_t)stackPopINT(pVM->pStack);
pushIor(pVM, ftruncate(fileno(ff->f), ud) == 0);
}
#endif /* FICL_HAVE_FTRUNCATE */
#endif /* FICL_WANT_FILE */
void ficlCompileFile(FICL_SYSTEM *pSys)
{
#if FICL_WANT_FILE
FICL_DICT *dp = pSys->dp;
assert(dp);
dictAppendWord(dp, "create-file", ficlCreateFile, FW_DEFAULT);
dictAppendWord(dp, "open-file", ficlOpenFile, FW_DEFAULT);
dictAppendWord(dp, "close-file", ficlCloseFile, FW_DEFAULT);
dictAppendWord(dp, "include-file", ficlIncludeFile, FW_DEFAULT);
dictAppendWord(dp, "read-file", ficlReadFile, FW_DEFAULT);
dictAppendWord(dp, "read-line", ficlReadLine, FW_DEFAULT);
dictAppendWord(dp, "write-file", ficlWriteFile, FW_DEFAULT);
dictAppendWord(dp, "write-line", ficlWriteLine, FW_DEFAULT);
dictAppendWord(dp, "file-position", ficlFilePosition, FW_DEFAULT);
dictAppendWord(dp, "file-size", ficlFileSize, FW_DEFAULT);
dictAppendWord(dp, "reposition-file", ficlRepositionFile, FW_DEFAULT);
dictAppendWord(dp, "file-status", ficlFileStatus, FW_DEFAULT);
dictAppendWord(dp, "flush-file", ficlFlushFile, FW_DEFAULT);
dictAppendWord(dp, "delete-file", ficlDeleteFile, FW_DEFAULT);
dictAppendWord(dp, "rename-file", ficlRenameFile, FW_DEFAULT);
#ifdef FICL_HAVE_FTRUNCATE
dictAppendWord(dp, "resize-file", ficlResizeFile, FW_DEFAULT);
ficlSetEnv(pSys, "file", FICL_TRUE);
ficlSetEnv(pSys, "file-ext", FICL_TRUE);
#endif /* FICL_HAVE_FTRUNCATE */
#else
(void)pSys;
#endif /* FICL_WANT_FILE */
}