| /* Copyright (C) 1989, 1990 Aladdin Enterprises. All rights reserved. |
| Distributed by Free Software Foundation, Inc. |
| |
| This file is part of Ghostscript. |
| |
| Ghostscript is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY. No author or distributor accepts responsibility |
| to anyone for the consequences of using it or for whether it serves any |
| particular purpose or works at all, unless he says so in writing. Refer |
| to the Ghostscript General Public License for full details. |
| |
| Everyone is granted permission to copy, modify and redistribute |
| Ghostscript, but only under the conditions described in the Ghostscript |
| General Public License. A copy of this license is supposed to have been |
| given to you along with Ghostscript so you can know your rights and |
| responsibilities. It should be in a file named COPYING. Among other |
| things, the copyright notice and this notice must be preserved on all |
| copies. */ |
| |
| /* zfile.c */ |
| /* File operators for GhostScript */ |
| #include <limits.h> |
| #include "memory_.h" |
| #include "string_.h" |
| #include "ghost.h" |
| #include "errors.h" |
| #include "oper.h" |
| #include "alloc.h" |
| #include "stream.h" |
| #include "store.h" |
| #include "gsmatrix.h" /* for gxdevice.h */ |
| #include "gxdevice.h" |
| #include "gxdevmem.h" |
| |
| #ifdef __XS1B__ |
| #define PATH_MAX 4096 |
| #endif |
| |
| /* Forward references */ |
| int zreadline_from(P4(byte *, uint, uint *, stream *)); |
| int lib_file_open(P3(byte *, uint, ref *)); |
| private int open_std_file(P3(ref *, char *, ref *)); |
| private ref *get_current_file(); |
| private int write_string(P2(ref *, stream *)); |
| |
| /* Imported from gs.c */ |
| extern char **gs_lib_paths; /* search path list, */ |
| /* terminated by a null pointer */ |
| |
| /* Imported from gp_*.c. */ |
| extern char gp_file_name_list_separator; |
| extern int gp_file_name_is_absolute(P2(char *, uint)); |
| extern char *gp_file_name_concat_string(P4(char *, uint, char *, uint)); |
| |
| /* Imported from iutil.c */ |
| extern char *ref_to_string(P2(ref *, char *)); |
| |
| /* Import the execution stack for currentfile */ |
| extern ref estack[]; |
| extern ref *esp; |
| |
| /* File objects store a pointer to a file entry in value.pfile. */ |
| /* A file entry is valid if its stream pointer is non-zero. */ |
| #define fptr(pref) (pref)->value.pfile |
| #define make_file(pref,a,pfe)\ |
| make_tav(pref,t_file,a,pfile,pfe) |
| typedef struct file_entry_s file_entry; |
| struct file_entry_s { |
| stream *s; |
| int can_close; /* 0 for stdin/out/err, */ |
| /* -1 for line/statementedit, */ |
| /* 1 for other files */ |
| ref file_name; /* t_string */ |
| }; |
| |
| /* File buffer sizes. For real files, this is arbitrary, */ |
| /* since the C library does its own buffering in addition. */ |
| /* stdout and stderr use smaller buffers, */ |
| /* on the assumption that they are usually not real files. */ |
| /* The buffer size for type 1 encrypted files is NOT arbitrary: */ |
| /* it must be at most 512. */ |
| #define buffer_size 512 |
| |
| /* Standard file objects: */ |
| /* 0 is stdin, 1 is stdout, 2 is stderr, 3 is lineedit, 4 is statementedit */ |
| #define num_std_files 5 |
| private file_entry invalid_file_entry = { (stream *)0 }; |
| private byte |
| #define stdin_buf_size 1 |
| stdin_buf[stdin_buf_size], |
| #define stdout_buf_size 128 |
| stdout_buf[stdout_buf_size], |
| #define stderr_buf_size 128 |
| stderr_buf[stderr_buf_size], |
| #define lineedit_buf_size 160 |
| lineedit_buf[lineedit_buf_size]; |
| /* statementedit is equivalent to lineedit for now */ |
| private stream std_file_streams[num_std_files]; |
| private file_entry std_files[num_std_files] = |
| { { &std_file_streams[0], 0 }, |
| { &std_file_streams[1], 0 }, |
| { &std_file_streams[2], 0 }, |
| { &std_file_streams[3], -1 }, |
| { &std_file_streams[4], -1 } |
| }; |
| private char *std_file_names[num_std_files] = |
| { "%stdin", |
| "%stdout", |
| "%stderr", |
| "%lineedit", |
| "%statementedit" |
| }; |
| private int std_file_attrs[num_std_files] = |
| { a_read+a_execute, |
| a_write+a_execute, |
| a_write+a_execute, |
| a_read+a_execute, |
| a_read+a_execute |
| }; |
| |
| /* Macros for checking file validity */ |
| #define check_file_access(svar,op,acc)\ |
| { file_entry *fe = fptr(op);\ |
| svar = fe->s; /* do first, acc may refer to it */\ |
| if ( svar == 0 || !(acc) )\ |
| return e_invalidaccess;\ |
| } |
| #define check_file_ref(svar,op,acc)\ |
| { if ( r_type(op) != t_file ) return e_typecheck;\ |
| check_file_access(svar,op,acc);\ |
| } |
| #define check_file(svar,op) check_file_ref(svar,op,1) |
| #define check_read_file(svar,op) check_file_ref(svar,op,!svar->writing) |
| #define check_write_file(svar,op) check_file_ref(svar,op,svar->writing) |
| |
| /* Initialize the file table */ |
| void |
| zfile_init() |
| { /* Create files for stdin, stdout, and stderr. */ |
| int i; |
| /****** stdin IS NOT IMPLEMENTED PROPERLY ******/ |
| sread_file(std_files[0].s, stdin, stdin_buf, stdin_buf_size); |
| swrite_file(std_files[1].s, stdout, stdout_buf, stdout_buf_size); |
| swrite_file(std_files[2].s, stderr, stderr_buf, stderr_buf_size); |
| for ( i = 0; i < num_std_files; i++ ) |
| { ref *pfn = &std_files[i].file_name; |
| make_tv(pfn, t_null, intval, 0); /* pre-clear */ |
| if ( string_to_ref(std_file_names[i], pfn, "zfile_init") < 0 ) |
| { dprintf("alloc failed in zfile_init!\n"); |
| gs_exit(1); |
| } |
| } |
| } |
| |
| /* file */ |
| int |
| zfile(register ref *op) |
| { char *file_access; |
| ref fname; |
| int code; |
| fname = op[-1]; |
| check_type(fname, t_string); |
| check_type(*op, t_string); |
| if ( op->size != 1 ) return e_invalidfileaccess; |
| switch ( *op->value.bytes ) |
| { |
| case 'r': file_access = "r"; break; |
| case 'w': file_access = "w"; break; |
| default: return e_invalidfileaccess; |
| } |
| code = open_std_file(op - 1, file_access, op - 1); |
| switch ( code ) |
| { |
| case 0: /* successful open */ |
| pop(1); |
| default: /* unsuccessful open */ |
| return code; |
| case e_undefinedfilename: /* not a %file */ |
| ; |
| } |
| code = file_open(fname.value.bytes, fname.size, file_access, op - 1); |
| if ( code >= 0 ) pop(1); |
| return code; |
| } |
| |
| /* closefile */ |
| int |
| zclosefile(register ref *op) |
| { stream *s; |
| int code; |
| check_file(s, op); |
| if ( (code = file_close(op, s)) >= 0 ) |
| { /* If we just closed the file from which the interpreter */ |
| /* is reading, zap it on the exec stack. */ |
| ref *fp = get_current_file(); |
| if ( fp != 0 && fptr(fp) == fptr(op) ) |
| /* A null would confuse the estack parser.... */ |
| make_tasv(fp, t_array, a_executable+a_execute, 0, refs, (ref *)0); |
| pop(1); |
| } |
| return code; |
| } |
| |
| /* read */ |
| int |
| zread(register ref *op) |
| { stream *s; |
| int ch; |
| check_read_file(s, op); |
| check_read(*op); |
| ch = sgetc(s); |
| if ( ch == EOFC ) |
| make_bool(op, 0); |
| else |
| { make_int(op, ch); |
| push(1); |
| make_bool(op, 1); |
| } |
| return 0; |
| } |
| |
| /* unread */ |
| int |
| zunread(register ref *op) |
| { stream *s; |
| ulong ch; |
| check_read_file(s, op - 1); |
| check_type(*op, t_integer); |
| ch = op->value.intval; |
| if ( ch > 0xff ) return e_rangecheck; |
| if ( sungetc(s, (byte)ch) < 0 ) return e_ioerror; |
| pop(2); |
| return 0; |
| } |
| |
| /* write */ |
| int |
| zwrite(register ref *op) |
| { stream *s; |
| ulong ch; |
| check_write_file(s, op - 1); |
| check_write(op[-1]); |
| check_type(*op, t_integer); |
| ch = op->value.intval; |
| if ( ch > 0xff ) return e_rangecheck; |
| sputc(s, (byte)ch); |
| pop(2); |
| return 0; |
| } |
| |
| /* readhexstring */ |
| int |
| zreadhexstring(register ref *op) |
| { stream st; |
| stream *s; |
| ref *op1 = op - 1; |
| int odd = -1; |
| int code; |
| uint nread; |
| switch ( r_type(op1) ) |
| { |
| default: return e_typecheck; |
| case t_file: |
| check_read_file(s, op1); |
| check_read(*op1); |
| break; |
| case t_string: |
| s = &st; |
| sread_string(s, op1->value.bytes, op1->size); |
| } |
| check_type(*op, t_string); |
| check_write(*op); |
| code = sreadhex(s, op->value.bytes, op->size, &nread, &odd); |
| switch ( code ) |
| { |
| case 1: |
| /* Reached end-of-file before filling the string. */ |
| /* Return an appropriate substring. */ |
| op->size = nread; |
| r_set_attrs(op, a_subrange); |
| break; |
| case 0: |
| /* Filled the string. */ |
| break; |
| default: /* Error */ |
| return e_ioerror; |
| } |
| if ( s == &st ) |
| { /* Reading from a string, return remainder */ |
| uint pos = stell(&st); |
| op1->size -= pos, op1->value.bytes += pos; |
| r_set_attrs(op1, a_subrange); |
| push(1); |
| } |
| else |
| { /* Reading from a file */ |
| *op1 = *op; |
| } |
| make_bool(op, 1 - code); |
| return 0; |
| } |
| |
| /* writehexstring */ |
| int |
| zwritehexstring(register ref *op) |
| { stream *s; |
| byte *p; |
| uint len; |
| static char *hex_digits = "0123456789abcdef"; |
| check_write_file(s, op - 1); |
| check_write(op[-1]); |
| check_read_type(*op, t_string); |
| p = op->value.bytes; |
| len = op->size; |
| while ( len-- ) |
| { byte ch = *p++; |
| sputc(s, hex_digits[ch >> 4]); |
| sputc(s, hex_digits[ch & 0xf]); |
| } |
| pop(2); |
| return 0; |
| } |
| |
| /* readstring */ |
| int |
| zreadstring(register ref *op) |
| { stream *s; |
| uint len, rlen; |
| check_read_file(s, op - 1); |
| check_read(op[-1]); |
| check_write_type(*op, t_string); |
| len = op->size; |
| rlen = sgets(s, op->value.bytes, len); |
| op->size = rlen; |
| r_set_attrs(op, a_subrange); |
| op[-1] = *op; |
| make_bool(op, (rlen == len ? 1 : 0)); |
| return 0; |
| } |
| |
| /* writestring */ |
| int |
| zwritestring(register ref *op) |
| { stream *s; |
| int code; |
| check_write_file(s, op - 1); |
| check_write(op[-1]); |
| code = write_string(op, s); |
| if ( code >= 0 ) pop(2); |
| return code; |
| } |
| |
| /* readline */ |
| int |
| zreadline(register ref *op) |
| { stream *s; |
| uint count; |
| int code; |
| check_read_file(s, op - 1); |
| check_read(op[-1]); |
| check_write_type(*op, t_string); |
| code = zreadline_from(op->value.bytes, op->size, &count, s); |
| if ( code < 0 ) return code; |
| op->size = count; |
| r_set_attrs(op, a_subrange); |
| op[-1] = *op; |
| make_bool(op, code); |
| return 0; |
| } |
| |
| /* Read a line from stdin. This is called from gs.c. */ |
| int |
| zreadline_stdin(byte *ptr, uint size, uint *pcount) |
| { return zreadline_from(ptr, size, pcount, std_files[0].s); |
| } |
| |
| /* Internal readline routine. */ |
| /* Returns 1 if OK, 0 if end of file, or an error code. */ |
| int |
| zreadline_from(byte *ptr, uint size, uint *pcount, stream *s) |
| { uint count = 0; |
| int ch; |
| while ( count < size ) |
| { switch ( ch = sgetc(s) ) |
| { |
| case '\r': |
| ch = sgetc(s); |
| if ( ch != '\n' && ch != EOFC ) sputback(s); |
| /* falls through */ |
| case '\n': |
| *pcount = count; |
| return 1; |
| case EOFC: |
| *pcount = count; |
| return 0; |
| } |
| *ptr++ = ch; |
| count++; |
| } |
| return e_rangecheck; /* filled the string */ |
| } |
| |
| /* token - this is called from zstring.c */ |
| int |
| ztoken_file(register ref *op) |
| { stream *s; |
| ref token; |
| int code; |
| check_read_file(s, op); |
| check_read(*op); |
| switch ( code = scan_token(s, 0, &token) ) |
| { |
| case 0: /* read a token */ |
| *op = token; |
| push(1); |
| make_bool(op, 1); |
| return 0; |
| case 1: /* no tokens */ |
| make_bool(op, 0); |
| return 0; |
| default: /* error */ |
| return code; |
| } |
| } |
| |
| /* bytesavailable */ |
| int |
| zbytesavailable(register ref *op) |
| { stream *s; |
| long avail; |
| check_read_file(s, op); |
| if ( savailable(s, &avail) < 0 ) return e_ioerror; |
| make_int(op, avail); |
| return 0; |
| } |
| |
| /* flush */ |
| int |
| zflush(register ref *op) |
| { sflush(std_files[1].s); |
| return 0; |
| } |
| |
| /* flushfile */ |
| int |
| zflushfile(register ref *op) |
| { stream *s; |
| check_file(s, op); |
| sflush(s); |
| if ( !s->writing ) |
| fseek(s->file, 0L, 2); /* set to end */ |
| pop(1); |
| return 0; |
| } |
| |
| /* resetfile */ |
| int |
| zresetfile(register ref *op) |
| { NYI("resetfile"); |
| pop(1); |
| return 0; |
| } |
| |
| /* status */ |
| int |
| zstatus(register ref *op) |
| { check_type(*op, t_file); |
| make_bool(op, (fptr(op)->s != 0 ? 1 : 0)); |
| return 0; |
| } |
| |
| /* run */ |
| int |
| zrun(register ref *op) |
| { NYI("run"); |
| pop(1); |
| return 0; |
| } |
| |
| /* currentfile */ |
| int |
| zcurrentfile(register ref *op) |
| { ref *fp; |
| push(1); |
| if ( (fp = get_current_file()) == 0 ) |
| { /* Return an invalid file object. */ |
| /* This doesn't make a lot of sense to me, */ |
| /* but it's what the PostScript manual specifies. */ |
| make_file(op, 0, &invalid_file_entry); |
| } |
| else |
| *op = *fp; |
| /* Make sure the returned value is literal. */ |
| r_clear_attrs(op, a_executable); |
| return 0; |
| } |
| |
| /* print */ |
| int |
| zprint(register ref *op) |
| { int code = write_string(op, std_files[1].s); |
| if ( code >= 0 ) pop(1); |
| return code; |
| } |
| |
| /* echo */ |
| int |
| zecho(register ref *op) |
| { check_type(*op, t_boolean); |
| /****** NOT IMPLEMENTED YET ******/ |
| pop(1); |
| return 0; |
| } |
| |
| /* ------ Level 2 extensions ------ */ |
| |
| /* setfileposition */ |
| int |
| zsetfileposition(register ref *op) |
| { stream *s; |
| check_file(s, op - 1); |
| check_type(*op, t_integer); |
| if ( sseek(s, op->value.intval) < 0 ) return e_ioerror; |
| pop(2); |
| return 0; |
| } |
| |
| /* fileposition */ |
| int |
| zfileposition(register ref *op) |
| { stream *s; |
| check_file(s, op); |
| if ( !sseekable(s) ) return e_ioerror; |
| make_int(op, stell(s)); |
| return 0; |
| } |
| |
| /* deletefile */ |
| int |
| zdeletefile(register ref *op) |
| { char *str; |
| int stat; |
| check_read_type(*op, t_string); |
| str = ref_to_string(op, "deletefile"); |
| if ( str == 0 ) return e_VMerror; |
| stat = unlink(str); |
| alloc_free(str, op->size + 1, 1, "deletefile"); |
| if ( stat != 0 ) return e_ioerror; |
| pop(1); |
| return 0; |
| } |
| |
| /* renamefile */ |
| int |
| zrenamefile(register ref *op) |
| { char *str1 = 0, *str2 = 0; |
| check_read_type(*op, t_string); |
| check_read_type(op[-1], t_string); |
| str1 = ref_to_string(op - 1, "renamefile(from)"); |
| str2 = ref_to_string(op, "renamefile(to)"); |
| if ( str1 != 0 && str2 != 0 && rename(str1, str2) == 0 ) |
| { pop(2); |
| } |
| if ( str1 != 0 ) |
| alloc_free(str1, op[-1].size + 1, 1, "renamefile(from)"); |
| if ( str2 != 0 ) |
| alloc_free(str2, op->size + 1, 1, "renamefile(to)"); |
| return 0; |
| } |
| |
| /* ------ Ghostscript extensions ------ */ |
| |
| /* filename */ |
| int |
| zfilename(register ref *op) |
| { stream *s; |
| check_file(s, op); |
| *op = op->value.pfile->file_name; |
| return 0; |
| } |
| |
| /* findlibfile */ |
| int |
| zfindlibfile(register ref *op) |
| { int code; |
| check_type(*op, t_string); |
| code = open_std_file(op, "r", op); |
| switch ( code ) |
| { |
| case 0: /* successful open */ |
| push(1); |
| make_bool(op, 1); |
| default: /* unsuccessful open */ |
| return code; |
| case e_undefinedfilename: /* not a %file */ |
| ; |
| } |
| code = lib_file_open(op->value.bytes, op->size, op); |
| push(1); |
| make_bool(op, code >= 0); |
| return 0; |
| } |
| |
| /* writeppmfile */ |
| int |
| zwriteppmfile(register ref *op) |
| { stream *s; |
| int code; |
| check_write_file(s, op - 1); |
| check_write(op[-1]); |
| check_type(*op, t_device); |
| if ( !gs_device_is_memory(op->value.pdevice) ) return e_typecheck; |
| sflush(s); |
| code = gs_writeppmfile((gx_device_memory *)(op->value.pdevice), s->file); |
| if ( code >= 0 ) pop(2); |
| return code; |
| } |
| |
| /* type1decryptfile */ |
| int |
| ztype1decryptfile(register ref *op) |
| { stream *s; |
| ushort state; |
| ref dec_file; |
| int code; |
| stream *es; |
| check_type(op[-1], t_integer); |
| state = op[-1].value.intval; |
| if ( op[-1].value.intval != state ) |
| return e_rangecheck; /* state value was truncated */ |
| check_read_file(s, op); |
| code = file_open((byte *)0, 0, "r", &dec_file); |
| if ( code < 0 ) return code; |
| es = fptr(&dec_file)->s; |
| sread_decrypt(es, fptr(op)->s, es->cbuf, es->bsize, state); |
| op[-1] = dec_file; |
| pop(1); |
| return 0; |
| } |
| |
| /* ------ Initialization procedure ------ */ |
| |
| void |
| zfile_op_init() |
| { static op_def my_defs[] = { |
| {"1bytesavailable", zbytesavailable}, |
| {"1closefile", zclosefile}, |
| {"0currentfile", zcurrentfile}, |
| {"1deletefile", zdeletefile}, |
| {"1echo", zecho}, |
| {"2file", zfile}, |
| {"1filename", zfilename}, |
| {"1fileposition", zfileposition}, |
| {"1findlibfile", zfindlibfile}, |
| {"0flush", zflush}, |
| {"1flushfile", zflushfile}, |
| {"1print", zprint}, |
| {"1read", zread}, |
| {"2readhexstring", zreadhexstring}, |
| {"2readline", zreadline}, |
| {"2readstring", zreadstring}, |
| {"2renamefile", zrenamefile}, |
| {"1resetfile", zresetfile}, |
| {"1run", zrun}, |
| {"2setfileposition", zsetfileposition}, |
| {"2type1decryptfile", ztype1decryptfile}, |
| {"2unread", zunread}, |
| {"1status", zstatus}, |
| {"2write", zwrite}, |
| {"2writehexstring", zwritehexstring}, |
| {"2writeppmfile", zwriteppmfile}, |
| {"2writestring", zwritestring}, |
| op_def_end |
| }; |
| z_op_init(my_defs); |
| } |
| |
| /* ------ Non-operator routines ------ */ |
| |
| /* Open a file, using the search paths if necessary. */ |
| /* The startup code calls this to open the initialization file ghost.ps, */ |
| /* and any other files specified on the command line. */ |
| int |
| lib_file_open(byte *fname, uint len, ref *pfile) |
| { int code; |
| char **ppath; |
| char cname[PATH_MAX]; |
| code = file_open(fname, len, "r", pfile); |
| if ( code >= 0 ) return code; |
| if ( gp_file_name_is_absolute((char *)fname, len) ) |
| return e_undefinedfilename; |
| /* Go through the list of search paths */ |
| for ( ppath = gs_lib_paths; *ppath != 0; ppath++ ) |
| { char *path = *ppath; |
| for ( ; ; ) |
| { /* Find the end of the next path */ |
| char *npath = path; |
| uint plen; |
| char *cstr; |
| int clen; |
| while ( *npath != 0 && *npath != gp_file_name_list_separator ) |
| npath++; |
| plen = npath - path; |
| cstr = gp_file_name_concat_string(path, plen, |
| (char *)fname, len); |
| /* Concatenate the prefix, combiner, and file name. */ |
| clen = plen + strlen(cstr) + len; |
| if ( clen <= PATH_MAX ) /* otherwise punt */ |
| { memcpy(cname, path, plen); |
| strcpy(cname + plen, cstr); |
| memcpy(cname + clen - len, fname, len); |
| code = file_open(cname, clen, "r", pfile); |
| if ( code >= 0 ) return code; |
| } |
| /****** NYI ******/ |
| if ( !*npath ) break; |
| path = npath + 1; |
| } |
| } |
| return code; |
| } |
| |
| /* Open a file and create a file object. */ |
| /* Return 0 if successful, error code if not. */ |
| /* If fname==0, set up the file entry, stream, and buffer, */ |
| /* but don't open an OS file or initialize the stream. */ |
| int |
| file_open(byte *fname, uint len, char *file_access, ref *pfile) |
| { byte *buffer; |
| stream *s; |
| file_entry *fe; |
| int code; |
| if ( len >= buffer_size ) |
| return e_limitcheck; /* we copy the file name into the buffer */ |
| /* Allocate the file entry first, since it persists */ |
| /* even after the file has been closed. */ |
| fe = (file_entry *)alloc(1, sizeof(file_entry), "file_open(file_entry)"); |
| if ( fe == 0 ) return e_VMerror; |
| /* Allocate the buffer and stream. */ |
| buffer = (byte *)alloc(buffer_size, 1, "file_open(buffer)"); |
| if ( buffer == 0 ) |
| { alloc_free((char *)fe, 1, sizeof(file_entry), "file_open(file_entry)"); |
| return e_VMerror; |
| } |
| s = (stream *)alloc(1, sizeof(stream), "file_open(stream)"); |
| if ( s == 0 ) |
| { alloc_free((char *)buffer, buffer_size, 1, "file_open(buffer)"); |
| alloc_free((char *)fe, 1, sizeof(file_entry), "file_open(file_entry)"); |
| return e_VMerror; |
| } |
| if ( fname != 0 ) |
| { /* Copy the name (so we can terminate it with a zero byte.) */ |
| char *file_name = (char *)buffer; |
| FILE *file; |
| memcpy(file_name, fname, len); |
| file_name[len] = 0; /* terminate string */ |
| /* Open the file. */ |
| file = fopen(file_name, file_access); |
| code = e_undefinedfilename; |
| if ( file == 0 || (code = string_to_ref(file_name, &fe->file_name, "file_open(file_name)")) < 0 ) |
| { alloc_free((char *)s, 1, sizeof(stream), "file_open(stream)"); |
| alloc_free((char *)buffer, buffer_size, 1, "file_open(buffer)"); |
| alloc_free((char *)fe, 1, sizeof(file_entry), "file_open(file_entry)"); |
| return code; |
| } |
| /* Set up the stream. */ |
| if ( *file_access == 'r' ) /* reading */ |
| sread_file(s, file, buffer, buffer_size); |
| else |
| swrite_file(s, file, buffer, buffer_size); |
| } |
| else /* save the buffer and size */ |
| { s->cbuf = buffer; |
| s->bsize = buffer_size; |
| } |
| fe->s = s; |
| fe->can_close = 1; |
| make_file(pfile, |
| (*file_access == 'r' ? a_read+a_execute : a_write+a_execute), |
| fe); |
| return 0; |
| } |
| |
| /* Check a file for reading. */ |
| /* The interpreter calls this to check an executable file. */ |
| int |
| file_check_read(ref *op, stream **ps) |
| { stream *s; |
| check_file_access(s, op, !s->writing); |
| *ps = s; |
| return 0; |
| } |
| |
| /* Get the current file from which the interpreter is reading. */ |
| private ref * |
| get_current_file() |
| { ref *ep = esp; |
| while ( ep >= estack ) |
| { if ( r_type(ep) == t_file && r_has_attrs(ep, a_executable) ) |
| return ep; |
| ep--; |
| } |
| return (ref *)0; |
| } |
| |
| /* Close a file. The interpreter calls this when */ |
| /* it reaches the end of an executable file. */ |
| int |
| file_close(ref *fp /* t_file */, stream *s) |
| { file_entry *fe = fptr(fp); |
| byte *buffer = s->cbuf; |
| switch ( fe->can_close ) |
| { |
| case 0: /* can't close std files */ |
| return e_invalidaccess; |
| case -1: /* ignore on statement/lineedit */ |
| sclose(s); |
| break; |
| default: /* ordinary file */ |
| if ( sclose(s) ) return e_ioerror; |
| /* Free the stream and buffer in the reverse of the order */ |
| /* in which they were created, and hope for LIFO storage behavior. */ |
| alloc_free((char *)s, 1, sizeof(stream), "file_close(stream)"); |
| alloc_free((char *)buffer, buffer_size, 1, "file_close(buffer)"); |
| } |
| fe->s = 0; |
| return 0; |
| } |
| |
| /* ------ Internal routines ------ */ |
| |
| /* If a file name refers to one of the standard %files, */ |
| /* 'open' the file and return 0 or an error code, otherwise */ |
| /* return e_undefinedfilename. */ |
| private int |
| open_std_file(ref *pfname, char *file_access, ref *pfile) |
| { int i; |
| for ( i = 0; i < num_std_files; i++ ) |
| if ( !bytes_compare(pfname->value.bytes, pfname->size, |
| std_file_names[i], strlen(std_file_names[i])) |
| ) |
| { /* This is a standard file */ |
| int attrs = (*file_access == 'r' ? a_read+a_execute : a_write+a_execute); |
| file_entry *fe = &std_files[i]; |
| if ( attrs != std_file_attrs[i] ) |
| return e_invalidaccess; |
| make_file(pfile, attrs, fe); |
| /* If this is %lineedit or %statementedit, */ |
| /* read a line now. */ |
| switch ( i ) |
| { |
| case 3: case 4: |
| { uint count; |
| int code = zreadline_stdin(lineedit_buf, |
| lineedit_buf_size, &count); |
| if ( code < 0 ) return code; |
| fe->s = &std_file_streams[i]; |
| sread_string(fe->s, lineedit_buf, count); |
| return 0; |
| } |
| } |
| return 0; |
| } |
| return e_undefinedfilename; |
| } |
| |
| /* Write a string on a file. The file has been checked for validity, */ |
| /* but not the string. */ |
| private int |
| write_string(ref *op, stream *s) |
| { uint len; |
| check_read_type(*op, t_string); |
| len = op->size; |
| if ( sputs(s, op->value.bytes, len) != len ) return e_ioerror; |
| return 0; |
| } |