2 |
- |
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
|
|
|
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
|
|
|
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 |
};
|