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