xref: /plan9-contrib/sys/src/cmd/gs/src/ziodev.c (revision d46c239f8612929b7dbade67d0d071633df3a15d)
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