1 /* Copyright (C) 1993, 1995, 1997, 1998, 1999, 2001 Aladdin Enterprises. All rights reserved. 2 3 This file is part of AFPL Ghostscript. 4 5 AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or 6 distributor accepts any responsibility for the consequences of using it, or 7 for whether it serves any particular purpose or works at all, unless he or 8 she says so in writing. Refer to the Aladdin Free Public License (the 9 "License") for full details. 10 11 Every copy of AFPL Ghostscript must include a copy of the License, normally 12 in a plain ASCII text file named PUBLIC. The License grants you the right 13 to copy, modify and redistribute AFPL Ghostscript, but only under certain 14 conditions described in the License. Among other things, the License 15 requires that the copyright notice and this notice be preserved on all 16 copies. 17 */ 18 19 /*$Id: ziodev.c,v 1.6 2001/09/22 07:26:46 ghostgum Exp $ */ 20 /* Standard IODevice implementation */ 21 #include "memory_.h" 22 #include "stdio_.h" 23 #include "string_.h" 24 #include "ghost.h" 25 #include "gp.h" 26 #include "gpcheck.h" 27 #include "oper.h" 28 #include "stream.h" 29 #include "istream.h" 30 #include "ialloc.h" 31 #include "iscan.h" 32 #include "ivmspace.h" 33 #include "gxiodev.h" /* must come after stream.h */ 34 /* and before files.h */ 35 #include "files.h" 36 #include "scanchar.h" /* for char_EOL */ 37 #include "store.h" 38 39 /* Import the dtype of the stdio IODevices. */ 40 extern const char iodev_dtype_stdio[]; 41 42 /* Define the special devices. */ 43 #define iodev_special(dname, init, open) {\ 44 dname, iodev_dtype_stdio,\ 45 { init, open, iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,\ 46 iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,\ 47 iodev_no_enumerate_files, NULL, NULL,\ 48 iodev_no_get_params, iodev_no_put_params\ 49 }\ 50 } 51 52 /* 53 * We need the current context pointer for accessing / opening the %std 54 * IODevices. However, this is not available to the open routine. 55 * Therefore, we use the hack of storing this pointer in the IODevice state 56 * pointer just before calling the open routines. We clear the pointer 57 * immediately afterwards so as not to wind up with dangling references. 58 */ 59 60 #define LINEEDIT_BUF_SIZE 20 /* initial size, not fixed size */ 61 private iodev_proc_open_device(lineedit_open); 62 const gx_io_device gs_iodev_lineedit = 63 iodev_special("%lineedit%", iodev_no_init, iodev_no_open_device); 64 65 #define STATEMENTEDIT_BUF_SIZE 50 /* initial size, not fixed size */ 66 private iodev_proc_open_device(statementedit_open); 67 const gx_io_device gs_iodev_statementedit = 68 iodev_special("%statementedit%", iodev_no_init, iodev_no_open_device); 69 70 /* ------ Operators ------ */ 71 72 /* <int> .getiodevice <string|null> */ 73 private int 74 zgetiodevice(i_ctx_t *i_ctx_p) 75 { 76 os_ptr op = osp; 77 gx_io_device *iodev; 78 const byte *dname; 79 80 check_type(*op, t_integer); 81 if (op->value.intval != (int)op->value.intval) 82 return_error(e_rangecheck); 83 iodev = gs_getiodevice((int)(op->value.intval)); 84 if (iodev == 0) /* index out of range */ 85 return_error(e_rangecheck); 86 dname = (const byte *)iodev->dname; 87 if (dname == 0) 88 make_null(op); 89 else 90 make_const_string(op, a_readonly | avm_foreign, 91 strlen((const char *)dname), dname); 92 return 0; 93 } 94 95 /* ------ %lineedit and %statementedit ------ */ 96 97 /* <file> <bool> <int> <string> .filelineedit <file> */ 98 /* This opens %statementedit% or %lineedit% and is also the 99 * continuation proc for callouts. 100 * Input: 101 * string is the statement/line buffer, 102 * int is the write index into string 103 * bool is true if %statementedit% 104 * file is stdin 105 * Output: 106 * file is a string based stream 107 * We store the line being read in a PostScript string. 108 * This limits the size to max_string_size (64k). 109 * This could be increased by storing the input line in something 110 * other than a PostScript string. 111 */ 112 int 113 zfilelineedit(i_ctx_t *i_ctx_p) 114 { 115 uint count = 0; 116 bool in_eol = false; 117 int code; 118 os_ptr op = osp; 119 bool statement; 120 stream *s; 121 stream *ins; 122 gs_string str; 123 uint initial_buf_size; 124 const char *filename; 125 /* 126 * buf exists only for stylistic parallelism: all occurrences of 127 * buf-> could just as well be str. . 128 */ 129 gs_string *const buf = &str; 130 131 check_type(*op, t_string); /* line assembled so far */ 132 buf->data = op->value.bytes; 133 buf->size = op->tas.rsize; 134 check_type(*(op-1), t_integer); /* index */ 135 count = (op-1)->value.intval; 136 check_type(*(op-2), t_boolean); /* statementedit/lineedit */ 137 statement = (op-2)->value.boolval; 138 check_read_file(ins, op - 3); /* %stdin */ 139 140 /* extend string */ 141 initial_buf_size = statement ? STATEMENTEDIT_BUF_SIZE : LINEEDIT_BUF_SIZE; 142 if (initial_buf_size > max_string_size) 143 return_error(e_limitcheck); 144 if (!buf->data || (buf->size < initial_buf_size)) { 145 count = 0; 146 buf->data = gs_alloc_string(imemory, initial_buf_size, 147 "zfilelineedit(buffer)"); 148 if (buf->data == 0) 149 return_error(e_VMerror); 150 op->value.bytes = buf->data; 151 op->tas.rsize = buf->size = initial_buf_size; 152 } 153 154 rd: 155 code = zreadline_from(ins, buf, imemory, &count, &in_eol); 156 if (buf->size > max_string_size) { 157 /* zreadline_from reallocated the buffer larger than 158 * is valid for a PostScript string. 159 * Return an error, but first realloc the buffer 160 * back to a legal size. 161 */ 162 byte *nbuf = gs_resize_string(imemory, buf->data, buf->size, 163 max_string_size, "zfilelineedit(shrink buffer)"); 164 if (nbuf == 0) 165 return_error(e_VMerror); 166 op->value.bytes = buf->data = nbuf; 167 op->tas.rsize = buf->size = max_string_size; 168 return_error(e_limitcheck); 169 } 170 171 op->value.bytes = buf->data; /* zreadline_from sometimes resizes the buffer. */ 172 op->tas.rsize = buf->size; 173 174 switch (code) { 175 case EOFC: 176 code = gs_note_error(e_undefinedfilename); 177 /* falls through */ 178 case 0: 179 break; 180 default: 181 code = gs_note_error(e_ioerror); 182 break; 183 case CALLC: 184 { 185 ref rfile; 186 (op-1)->value.intval = count; 187 /* callout is for stdin */ 188 make_file(&rfile, a_readonly | avm_system, ins->read_id, ins); 189 code = s_handle_read_exception(i_ctx_p, code, &rfile, 190 NULL, 0, zfilelineedit); 191 } 192 break; 193 case 1: /* filled buffer */ 194 { 195 uint nsize = buf->size; 196 byte *nbuf; 197 198 if (nsize >= max_string_size) { 199 code = gs_note_error(e_limitcheck); 200 break; 201 } 202 else if (nsize >= max_string_size / 2) 203 nsize= max_string_size; 204 else 205 nsize = buf->size * 2; 206 nbuf = gs_resize_string(imemory, buf->data, buf->size, nsize, 207 "zfilelineedit(grow buffer)"); 208 if (nbuf == 0) { 209 code = gs_note_error(e_VMerror); 210 break; 211 } 212 op->value.bytes = buf->data = nbuf; 213 op->tas.rsize = buf->size = nsize; 214 goto rd; 215 } 216 } 217 if (code != 0) 218 return code; 219 if (statement) { 220 /* If we don't have a complete token, keep going. */ 221 stream st; 222 stream *ts = &st; 223 scanner_state state; 224 ref ignore_value; 225 uint depth = ref_stack_count(&o_stack); 226 int code; 227 228 /* Add a terminating EOL. */ 229 if (count + 1 > buf->size) { 230 uint nsize; 231 byte *nbuf; 232 233 nsize = buf->size + 1; 234 if (nsize > max_string_size) { 235 return_error(gs_note_error(e_limitcheck)); 236 } 237 else { 238 nbuf = gs_resize_string(imemory, buf->data, buf->size, nsize, 239 "zfilelineedit(grow buffer)"); 240 if (nbuf == 0) { 241 code = gs_note_error(e_VMerror); 242 return_error(code); 243 } 244 op->value.bytes = buf->data = nbuf; 245 op->tas.rsize = buf->size = nsize; 246 } 247 } 248 buf->data[count++] = char_EOL; 249 sread_string(ts, buf->data, count); 250 sc: 251 scanner_state_init_check(&state, false, true); 252 code = scan_token(i_ctx_p, ts, &ignore_value, &state); 253 ref_stack_pop_to(&o_stack, depth); 254 switch (code) { 255 case 0: /* read a token */ 256 case scan_BOS: 257 goto sc; /* keep going until we run out of data */ 258 case scan_Refill: 259 goto rd; 260 case scan_EOF: 261 break; 262 default: /* error */ 263 return code; 264 } 265 } 266 buf->data = gs_resize_string(imemory, buf->data, buf->size, count, 267 "zfilelineedit(resize buffer)"); 268 if (buf->data == 0) 269 return_error(e_VMerror); 270 op->value.bytes = buf->data; 271 op->tas.rsize = buf->size; 272 273 s = file_alloc_stream(imemory, "zfilelineedit(stream)"); 274 if (s == 0) 275 return_error(e_VMerror); 276 277 sread_string(s, buf->data, count); 278 s->save_close = s->procs.close; 279 s->procs.close = file_close_disable; 280 281 filename = statement ? gs_iodev_statementedit.dname 282 : gs_iodev_lineedit.dname; 283 code = ssetfilename(s, (const byte *)filename, strlen(filename)+1); 284 if (code < 0) { 285 sclose(s); 286 return_error(e_VMerror); 287 } 288 289 pop(3); 290 make_stream_file(osp, s, "r"); 291 292 return code; 293 } 294 295 /* ------ Initialization procedure ------ */ 296 297 const op_def ziodev_op_defs[] = 298 { 299 {"1.getiodevice", zgetiodevice}, 300 op_def_end(0) 301 }; 302