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