/src/ghostpdl/psi/zfileio.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2021 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 1305 Grant Avenue - Suite 200, Novato, |
13 | | CA 94945, U.S.A., +1(415)492-9861, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* File I/O operators */ |
18 | | #include "memory_.h" |
19 | | #include "ghost.h" |
20 | | #include "gp.h" |
21 | | #include "oper.h" |
22 | | #include "stream.h" |
23 | | #include "files.h" |
24 | | #include "store.h" |
25 | | #include "strimpl.h" /* for ifilter.h */ |
26 | | #include "ifilter.h" /* for procedure streams */ |
27 | | #include "interp.h" /* for gs_errorinfo_put_string */ |
28 | | #include "gsmatrix.h" /* for gxdevice.h */ |
29 | | #include "gxdevice.h" |
30 | | #include "gxdevmem.h" |
31 | | #include "estack.h" |
32 | | #include "gsstate.h" |
33 | | |
34 | | /* Forward references */ |
35 | | static int write_string(ref *, stream *); |
36 | | static int handle_read_status(i_ctx_t *, int, const ref *, const uint *, |
37 | | op_proc_t); |
38 | | static int handle_write_status(i_ctx_t *, int, const ref *, const uint *, |
39 | | op_proc_t); |
40 | | |
41 | | /* ------ Operators ------ */ |
42 | | |
43 | | /* <file> closefile - */ |
44 | | int |
45 | | zclosefile(i_ctx_t *i_ctx_p) |
46 | 8.84k | { |
47 | 8.84k | os_ptr op = osp; |
48 | 8.84k | stream *s; |
49 | | |
50 | 8.84k | check_type(*op, t_file); |
51 | 8.84k | if (file_is_valid(s, op)) { /* closing a closed file is a no-op */ |
52 | 4.08k | int status = sclose(s); |
53 | | |
54 | 4.08k | if (status != 0 && status != EOFC) { |
55 | 0 | if (s_is_writing(s)) |
56 | 0 | return handle_write_status(i_ctx_p, status, op, NULL, |
57 | 0 | zclosefile); |
58 | 0 | else |
59 | 0 | return handle_read_status(i_ctx_p, status, op, NULL, |
60 | 0 | zclosefile); |
61 | 0 | } |
62 | 4.08k | } |
63 | 8.84k | pop(1); |
64 | 8.84k | return 0; |
65 | 8.84k | } |
66 | | |
67 | | /* <file> read <int> -true- */ |
68 | | /* <file> read -false- */ |
69 | | static int |
70 | | zread(i_ctx_t *i_ctx_p) |
71 | 131k | { |
72 | 131k | os_ptr op = osp; |
73 | 131k | stream *s; |
74 | 131k | int ch; |
75 | | |
76 | 131k | check_read_file(i_ctx_p, s, op); |
77 | | /* We 'push' first in case of ostack block overflow and the */ |
78 | | /* usual case is we will need to push anyway. If we get EOF */ |
79 | | /* we will need to 'pop' and decrement the 'op' pointer. */ |
80 | | /* This is required since the 'push' macro might return with*/ |
81 | | /* stackoverflow which will result in another stack block */ |
82 | | /* added on, then the operator being retried. We can't read */ |
83 | | /* (sgetc) prior to having a place on the ostack to return */ |
84 | | /* the character. */ |
85 | 131k | push(1); |
86 | 131k | ch = sgetc(s); |
87 | 131k | if (ch >= 0) { |
88 | 131k | make_int(op - 1, ch); |
89 | 131k | make_bool(op, 1); |
90 | 131k | } else { |
91 | 0 | pop(1); /* Adjust ostack back from preparatory 'pop' */ |
92 | 0 | op--; |
93 | 0 | if (ch == EOFC) |
94 | 0 | make_bool(op, 0); |
95 | 0 | else |
96 | 0 | return handle_read_status(i_ctx_p, ch, op, NULL, zread); |
97 | 0 | } |
98 | 131k | return 0; |
99 | 131k | } |
100 | | |
101 | | /* <file> <int> write - */ |
102 | | int |
103 | | zwrite(i_ctx_t *i_ctx_p) |
104 | 0 | { |
105 | 0 | os_ptr op = osp; |
106 | 0 | stream *s; |
107 | 0 | byte ch; |
108 | 0 | int status; |
109 | |
|
110 | 0 | check_write_file(s, op - 1); |
111 | 0 | check_type(*op, t_integer); |
112 | 0 | ch = (byte) op->value.intval; |
113 | 0 | status = sputc(s, (byte) ch); |
114 | 0 | if (status >= 0) { |
115 | 0 | pop(2); |
116 | 0 | return 0; |
117 | 0 | } |
118 | 0 | return handle_write_status(i_ctx_p, status, op - 1, NULL, zwrite); |
119 | 0 | } |
120 | | |
121 | | /* <file> <string> readhexstring <substring> <filled_bool> */ |
122 | | static int zreadhexstring_continue(i_ctx_t *); |
123 | | |
124 | | /* We pack the odd digit above the the current position for the */ |
125 | | /* convenience of reusing procedures that take 1 state parameter */ |
126 | | static int |
127 | | zreadhexstring_at(i_ctx_t *i_ctx_p, os_ptr op, uint start, int odd) |
128 | 2 | { |
129 | 2 | stream *s; |
130 | 2 | uint len, nread; |
131 | 2 | byte *str; |
132 | 2 | int odd_byte = odd; |
133 | 2 | stream_cursor_write cw; |
134 | 2 | int status; |
135 | | |
136 | 2 | check_read_file(i_ctx_p, s, op - 1); |
137 | | /*check_write_type(*op, t_string); *//* done by caller */ |
138 | 2 | str = op->value.bytes; |
139 | 2 | len = r_size(op); |
140 | 2 | cw.ptr = str + start - 1; |
141 | 2 | cw.limit = str + len - 1; |
142 | 10 | for (;;) { |
143 | 10 | status = s_hex_process(&s->cursor.r, &cw, &odd_byte, |
144 | 10 | hex_ignore_garbage); |
145 | 10 | if (status == 1) { /* filled the string */ |
146 | 0 | ref_assign_inline(op - 1, op); |
147 | 0 | make_true(op); |
148 | 0 | return 0; |
149 | 10 | } else if (status != 0) /* error or EOF */ |
150 | 0 | break; |
151 | | /* Didn't fill, keep going. */ |
152 | 10 | status = spgetc(s); |
153 | 10 | if (status < 0) |
154 | 2 | break; |
155 | 8 | sputback(s); |
156 | 8 | } |
157 | 2 | nread = cw.ptr + 1 - str; |
158 | 2 | if (status != EOFC) { /* Error */ |
159 | 0 | nread |= ((uchar)odd_byte) << 24; |
160 | 0 | return handle_read_status(i_ctx_p, status, op - 1, &nread, |
161 | 0 | zreadhexstring_continue); |
162 | 0 | } |
163 | | /* Reached end-of-file before filling the string. */ |
164 | | /* Return an appropriate substring. */ |
165 | 2 | ref_assign_inline(op - 1, op); |
166 | 2 | r_set_size(op - 1, nread); |
167 | 2 | make_false(op); |
168 | 2 | return 0; |
169 | 2 | } |
170 | | static int |
171 | | zreadhexstring(i_ctx_t *i_ctx_p) |
172 | 2 | { |
173 | 2 | os_ptr op = osp; |
174 | | |
175 | 2 | check_write_type(*op, t_string); |
176 | 2 | return zreadhexstring_at(i_ctx_p, op, 0, -1); |
177 | 2 | } |
178 | | /* Continue a readhexstring operation after a callout. */ |
179 | | /* *op contains the index within the string and the odd flag. */ |
180 | | static int |
181 | | zreadhexstring_continue(i_ctx_t *i_ctx_p) |
182 | 0 | { |
183 | 0 | os_ptr op = osp; |
184 | 0 | int code, length, odd; |
185 | |
|
186 | 0 | check_type(*op, t_integer); |
187 | 0 | length = op->value.intval & 0xFFFFFF; |
188 | 0 | odd = (schar)(op->value.intval >> 24); |
189 | |
|
190 | 0 | if (length > r_size(op - 1) || odd < -1 || odd > 0xF) |
191 | 0 | return_error(gs_error_rangecheck); |
192 | 0 | check_write_type(op[-1], t_string); |
193 | 0 | code = zreadhexstring_at(i_ctx_p, op - 1, (uint)length, odd); |
194 | 0 | if (code >= 0) |
195 | 0 | pop(1); |
196 | 0 | return code; |
197 | 0 | } |
198 | | |
199 | | /* <file> <string> writehexstring - */ |
200 | | static int zwritehexstring_continue(i_ctx_t *); |
201 | | static int |
202 | | zwritehexstring_at(i_ctx_t *i_ctx_p, os_ptr op, uint odd) |
203 | 0 | { |
204 | 0 | register stream *s; |
205 | 0 | register byte ch; |
206 | 0 | register const byte *p; |
207 | 0 | register const char *const hex_digits = "0123456789abcdef"; |
208 | 0 | register uint len; |
209 | 0 | int status; |
210 | |
|
211 | 0 | #define MAX_HEX 128 |
212 | 0 | byte buf[MAX_HEX]; |
213 | |
|
214 | 0 | check_write_file(s, op - 1); |
215 | 0 | check_read_type(*op, t_string); |
216 | 0 | p = op->value.bytes; |
217 | 0 | len = r_size(op); |
218 | 0 | while (len) { |
219 | 0 | uint len1 = min(len, MAX_HEX / 2); |
220 | 0 | register byte *q = buf; |
221 | 0 | uint count = len1; |
222 | 0 | ref rbuf; |
223 | |
|
224 | 0 | do { |
225 | 0 | ch = *p++; |
226 | 0 | *q++ = hex_digits[ch >> 4]; |
227 | 0 | *q++ = hex_digits[ch & 0xf]; |
228 | 0 | } |
229 | 0 | while (--count); |
230 | 0 | r_set_size(&rbuf, (len1 << 1) - odd); |
231 | 0 | rbuf.value.bytes = buf + odd; |
232 | 0 | status = write_string(&rbuf, s); |
233 | 0 | switch (status) { |
234 | 0 | default: |
235 | 0 | return_error(gs_error_ioerror); |
236 | 0 | case 0: |
237 | 0 | len -= len1; |
238 | 0 | odd = 0; |
239 | 0 | continue; |
240 | 0 | case INTC: |
241 | 0 | case CALLC: |
242 | 0 | count = rbuf.value.bytes - buf; |
243 | 0 | op->value.bytes += count >> 1; |
244 | 0 | r_set_size(op, len - (count >> 1)); |
245 | 0 | count &= 1; |
246 | 0 | return handle_write_status(i_ctx_p, status, op - 1, &count, |
247 | 0 | zwritehexstring_continue); |
248 | 0 | } |
249 | 0 | } |
250 | 0 | pop(2); |
251 | 0 | return 0; |
252 | 0 | #undef MAX_HEX |
253 | 0 | } |
254 | | static int |
255 | | zwritehexstring(i_ctx_t *i_ctx_p) |
256 | 0 | { |
257 | 0 | os_ptr op = osp; |
258 | |
|
259 | 0 | return zwritehexstring_at(i_ctx_p, op, 0); |
260 | 0 | } |
261 | | /* Continue a writehexstring operation after a callout. */ |
262 | | /* *op is the odd/even hex digit flag for the first byte. */ |
263 | | static int |
264 | | zwritehexstring_continue(i_ctx_t *i_ctx_p) |
265 | 0 | { |
266 | 0 | os_ptr op = osp; |
267 | 0 | int code; |
268 | |
|
269 | 0 | check_type(*op, t_integer); |
270 | 0 | if ((op->value.intval & ~1) != 0) |
271 | 0 | return_error(gs_error_rangecheck); |
272 | 0 | code = zwritehexstring_at(i_ctx_p, op - 1, (uint) op->value.intval); |
273 | 0 | if (code >= 0) |
274 | 0 | pop(1); |
275 | 0 | return code; |
276 | 0 | } |
277 | | |
278 | | /* <file> <string> readstring <substring> <filled_bool> */ |
279 | | static int zreadstring_continue(i_ctx_t *); |
280 | | static int |
281 | | zreadstring_at(i_ctx_t *i_ctx_p, os_ptr op, uint start) |
282 | 7.26k | { |
283 | 7.26k | stream *s; |
284 | 7.26k | uint len, rlen; |
285 | 7.26k | int status; |
286 | | |
287 | 7.26k | check_write_type(*op, t_string); |
288 | 7.26k | check_read_file(i_ctx_p, s, op - 1); |
289 | 7.26k | len = r_size(op); |
290 | 7.26k | status = sgets(s, op->value.bytes + start, len - start, &rlen); |
291 | 7.26k | rlen += start; |
292 | 7.26k | switch (status) { |
293 | 672 | case EOFC: |
294 | 7.26k | case 0: |
295 | 7.26k | break; |
296 | 0 | default: |
297 | 0 | return handle_read_status(i_ctx_p, status, op - 1, &rlen, |
298 | 0 | zreadstring_continue); |
299 | 7.26k | } |
300 | | /* |
301 | | * The most recent Adobe specification says that readstring |
302 | | * must signal a rangecheck if the string length is zero. |
303 | | * I can't imagine the motivation for this, but we emulate it. |
304 | | * It's safe to check it here, rather than earlier, because if |
305 | | * len is zero, sgets will return 0 immediately with rlen = 0. |
306 | | */ |
307 | 7.26k | if (len == 0) |
308 | 0 | return_error(gs_error_rangecheck); |
309 | 7.26k | r_set_size(op, rlen); |
310 | 7.26k | op[-1] = *op; |
311 | 7.26k | make_bool(op, (rlen == len ? 1 : 0)); |
312 | 7.26k | return 0; |
313 | 7.26k | } |
314 | | static int |
315 | | zreadstring(i_ctx_t *i_ctx_p) |
316 | 7.26k | { |
317 | 7.26k | os_ptr op = osp; |
318 | | |
319 | 7.26k | return zreadstring_at(i_ctx_p, op, 0); |
320 | 7.26k | } |
321 | | /* Continue a readstring operation after a callout. */ |
322 | | /* *op is the index within the string. */ |
323 | | static int |
324 | | zreadstring_continue(i_ctx_t *i_ctx_p) |
325 | 0 | { |
326 | 0 | os_ptr op = osp; |
327 | 0 | int code; |
328 | |
|
329 | 0 | check_type(*op, t_integer); |
330 | 0 | if (op->value.intval < 0 || op->value.intval > r_size(op - 1)) |
331 | 0 | return_error(gs_error_rangecheck); |
332 | 0 | code = zreadstring_at(i_ctx_p, op - 1, (uint) op->value.intval); |
333 | 0 | if (code >= 0) |
334 | 0 | pop(1); |
335 | 0 | return code; |
336 | 0 | } |
337 | | |
338 | | /* <file> <string> writestring - */ |
339 | | static int |
340 | | zwritestring(i_ctx_t *i_ctx_p) |
341 | 6.71k | { |
342 | 6.71k | os_ptr op = osp; |
343 | 6.71k | stream *s; |
344 | 6.71k | int status; |
345 | | |
346 | 6.71k | check_write_file(s, op - 1); |
347 | 6.71k | check_read_type(*op, t_string); |
348 | 6.71k | status = write_string(op, s); |
349 | 6.71k | if (status >= 0) { |
350 | 6.71k | pop(2); |
351 | 6.71k | return 0; |
352 | 6.71k | } |
353 | 0 | return handle_write_status(i_ctx_p, status, op - 1, NULL, zwritestring); |
354 | 6.71k | } |
355 | | |
356 | | /* <file> <string> readline <substring> <bool> */ |
357 | | static int zreadline(i_ctx_t *); |
358 | | static int zreadline_continue(i_ctx_t *); |
359 | | |
360 | | /* |
361 | | * We could handle readline the same way as readstring, |
362 | | * except for the anomalous situation where we get interrupted |
363 | | * between the CR and the LF of an end-of-line marker. |
364 | | * We hack around this in the following way: if we get interrupted |
365 | | * before we've read any characters, we just restart the readline; |
366 | | * if we get interrupted at any other time, we use readline_continue; |
367 | | * we use start=0 (which we have just ruled out as a possible start value |
368 | | * for readline_continue) to indicate interruption after the CR. |
369 | | */ |
370 | | static int |
371 | | zreadline_at(i_ctx_t *i_ctx_p, os_ptr op, uint count, bool in_eol) |
372 | 2 | { |
373 | 2 | stream *s; |
374 | 2 | int status; |
375 | 2 | gs_string str; |
376 | | |
377 | 2 | check_write_type(*op, t_string); |
378 | 2 | check_read_file(i_ctx_p, s, op - 1); |
379 | 2 | str.data = op->value.bytes; |
380 | 2 | str.size = r_size(op); |
381 | 2 | status = zreadline_from(s, &str, NULL, &count, &in_eol); |
382 | 2 | switch (status) { |
383 | 2 | case 0: |
384 | 2 | case EOFC: |
385 | 2 | break; |
386 | 0 | case 1: |
387 | 0 | return_error(gs_error_rangecheck); |
388 | 0 | default: |
389 | 0 | if (count == 0 && !in_eol) |
390 | 0 | return handle_read_status(i_ctx_p, status, op - 1, NULL, |
391 | 0 | zreadline); |
392 | 0 | else { |
393 | 0 | if (in_eol) { |
394 | 0 | r_set_size(op, count); |
395 | 0 | count = 0; |
396 | 0 | } |
397 | 0 | return handle_read_status(i_ctx_p, status, op - 1, &count, |
398 | 0 | zreadline_continue); |
399 | 0 | } |
400 | 2 | } |
401 | 2 | r_set_size(op, count); |
402 | 2 | op[-1] = *op; |
403 | 2 | make_bool(op, status == 0); |
404 | 2 | return 0; |
405 | 2 | } |
406 | | static int |
407 | | zreadline(i_ctx_t *i_ctx_p) |
408 | 2 | { |
409 | 2 | os_ptr op = osp; |
410 | | |
411 | 2 | return zreadline_at(i_ctx_p, op, 0, false); |
412 | 2 | } |
413 | | /* Continue a readline operation after a callout. */ |
414 | | /* *op is the index within the string, or 0 for an interrupt after a CR. */ |
415 | | static int |
416 | | zreadline_continue(i_ctx_t *i_ctx_p) |
417 | 0 | { |
418 | 0 | os_ptr op = osp; |
419 | 0 | uint size = r_size(op - 1); |
420 | 0 | uint start; |
421 | 0 | int code; |
422 | |
|
423 | 0 | check_type(*op, t_integer); |
424 | 0 | if (op->value.intval < 0 || op->value.intval > size) |
425 | 0 | return_error(gs_error_rangecheck); |
426 | 0 | start = (uint) op->value.intval; |
427 | 0 | code = (start == 0 ? zreadline_at(i_ctx_p, op - 1, size, true) : |
428 | 0 | zreadline_at(i_ctx_p, op - 1, start, false)); |
429 | 0 | if (code >= 0) |
430 | 0 | pop(1); |
431 | 0 | return code; |
432 | 0 | } |
433 | | |
434 | | /* Internal readline routine. */ |
435 | | /* Returns a stream status value, or 1 if we overflowed the string. */ |
436 | | /* This is exported for %lineedit. */ |
437 | | int |
438 | | zreadline_from(stream *s, gs_string *buf, gs_memory_t *bufmem, |
439 | | uint *pcount, bool *pin_eol) |
440 | 2 | { |
441 | 2 | sreadline_proc((*readline)); |
442 | | |
443 | 2 | if (zis_stdin(s)) |
444 | 0 | readline = gp_readline; |
445 | 2 | else |
446 | 2 | readline = sreadline; |
447 | 2 | return readline(s, NULL, NULL /*WRONG*/, NULL, buf, bufmem, |
448 | 2 | pcount, pin_eol, zis_stdin); |
449 | 2 | } |
450 | | |
451 | | /* <file> bytesavailable <int> */ |
452 | | static int |
453 | | zbytesavailable(i_ctx_t *i_ctx_p) |
454 | 0 | { |
455 | 0 | os_ptr op = osp; |
456 | 0 | stream *s; |
457 | 0 | gs_offset_t avail; |
458 | |
|
459 | 0 | check_read_file(i_ctx_p, s, op); |
460 | 0 | switch (savailable(s, &avail)) { |
461 | 0 | default: |
462 | 0 | return_error(gs_error_ioerror); |
463 | 0 | case EOFC: |
464 | 0 | avail = -1; |
465 | 0 | case 0: |
466 | 0 | ; |
467 | 0 | } |
468 | 0 | if (gs_currentcpsimode(imemory)) { |
469 | 0 | avail = (ps_int32)avail; |
470 | 0 | } |
471 | 0 | make_int(op, avail); |
472 | 0 | return 0; |
473 | 0 | } |
474 | | |
475 | | /* - flush - */ |
476 | | int |
477 | | zflush(i_ctx_t *i_ctx_p) |
478 | 752 | { |
479 | 752 | stream *s; |
480 | 752 | int status; |
481 | 752 | ref rstdout; |
482 | 752 | int code = zget_stdout(i_ctx_p, &s); |
483 | | |
484 | 752 | if (code < 0) |
485 | 0 | return code; |
486 | | |
487 | 752 | make_stream_file(&rstdout, s, "w"); |
488 | 752 | status = sflush(s); |
489 | 752 | if (status == 0 || status == EOFC) { |
490 | 752 | return 0; |
491 | 752 | } |
492 | 0 | return |
493 | 0 | (s_is_writing(s) ? |
494 | 0 | handle_write_status(i_ctx_p, status, &rstdout, NULL, zflush) : |
495 | 0 | handle_read_status(i_ctx_p, status, &rstdout, NULL, zflush)); |
496 | 752 | } |
497 | | |
498 | | /* <file> flushfile - */ |
499 | | static int |
500 | | zflushfile(i_ctx_t *i_ctx_p) |
501 | 5.37k | { |
502 | 5.37k | os_ptr op = osp; |
503 | 5.37k | stream *s; |
504 | 5.37k | int status; |
505 | | |
506 | 5.37k | check_type(*op, t_file); |
507 | | /* |
508 | | * We think flushfile is a no-op on closed input files, but causes an |
509 | | * error on closed output files. |
510 | | */ |
511 | 5.37k | if (file_is_invalid(s, op)) { |
512 | 0 | if (r_has_attr(op, a_write)) |
513 | 0 | return_error(gs_error_invalidaccess); |
514 | 0 | pop(1); |
515 | 0 | return 0; |
516 | 0 | } |
517 | 5.37k | status = sflush(s); |
518 | 5.37k | if (status == 0 || status == EOFC) { |
519 | 5.37k | pop(1); |
520 | 5.37k | return 0; |
521 | 5.37k | } |
522 | 0 | return |
523 | 0 | (s_is_writing(s) ? |
524 | 0 | handle_write_status(i_ctx_p, status, op, NULL, zflushfile) : |
525 | 0 | handle_read_status(i_ctx_p, status, op, NULL, zflushfile)); |
526 | 5.37k | } |
527 | | |
528 | | /* <file> resetfile - */ |
529 | | static int |
530 | | zresetfile(i_ctx_t *i_ctx_p) |
531 | 0 | { |
532 | 0 | os_ptr op = osp; |
533 | 0 | stream *s; |
534 | | |
535 | | /* According to Adobe, resetfile is a no-op on closed files. */ |
536 | 0 | check_type(*op, t_file); |
537 | 0 | if (file_is_valid(s, op)) |
538 | 0 | sreset(s); |
539 | 0 | pop(1); |
540 | 0 | return 0; |
541 | 0 | } |
542 | | |
543 | | /* <string> print - */ |
544 | | static int |
545 | | zprint(i_ctx_t *i_ctx_p) |
546 | 70.3k | { |
547 | 70.3k | os_ptr op = osp; |
548 | 70.3k | stream *s; |
549 | 70.3k | int status; |
550 | 70.3k | ref rstdout; |
551 | 70.3k | int code; |
552 | | |
553 | 70.3k | check_read_type(*op, t_string); |
554 | 70.3k | code = zget_stdout(i_ctx_p, &s); |
555 | 70.3k | if (code < 0) |
556 | 0 | return code; |
557 | 70.3k | status = write_string(op, s); |
558 | 70.3k | if (status >= 0) { |
559 | 70.3k | pop(1); |
560 | 70.3k | return 0; |
561 | 70.3k | } |
562 | | /* Convert print to writestring on the fly. */ |
563 | 0 | make_stream_file(&rstdout, s, "w"); |
564 | 0 | code = handle_write_status(i_ctx_p, status, &rstdout, NULL, |
565 | 0 | zwritestring); |
566 | 0 | if (code != o_push_estack) |
567 | 0 | return code; |
568 | 0 | push(1); |
569 | 0 | *op = op[-1]; |
570 | 0 | op[-1] = rstdout; |
571 | 0 | return code; |
572 | 0 | } |
573 | | |
574 | | /* <bool> echo - */ |
575 | | static int |
576 | | zecho(i_ctx_t *i_ctx_p) |
577 | 0 | { |
578 | 0 | os_ptr op = osp; |
579 | |
|
580 | 0 | check_type(*op, t_boolean); |
581 | | /****** NOT IMPLEMENTED YET ******/ |
582 | 0 | pop(1); |
583 | 0 | return 0; |
584 | 0 | } |
585 | | |
586 | | /* ------ Level 2 extensions ------ */ |
587 | | |
588 | | /* <file> fileposition <int> */ |
589 | | static int |
590 | | zfileposition(i_ctx_t *i_ctx_p) |
591 | 1 | { |
592 | 1 | os_ptr op = osp; |
593 | 1 | stream *s; |
594 | | |
595 | 1 | check_file(s, op); |
596 | | /* |
597 | | * The PLRM says fileposition must give an error for non-seekable |
598 | | * streams. |
599 | | */ |
600 | 1 | if (!s_can_seek(s)) |
601 | 1 | return_error(gs_error_ioerror); |
602 | 0 | make_int(op, stell(s)); |
603 | 0 | return 0; |
604 | 1 | } |
605 | | /* <file> .fileposition <int> */ |
606 | | static int |
607 | | zxfileposition(i_ctx_t *i_ctx_p) |
608 | 0 | { |
609 | 0 | os_ptr op = osp; |
610 | 0 | stream *s; |
611 | |
|
612 | 0 | check_file(s, op); |
613 | | /* |
614 | | * This version of fileposition doesn't give the error, so we can |
615 | | * use it to get the position of string or procedure streams. |
616 | | */ |
617 | 0 | make_int(op, stell(s)); |
618 | 0 | return 0; |
619 | 0 | } |
620 | | |
621 | | /* <file> <int> setfileposition - */ |
622 | | static int |
623 | | zsetfileposition(i_ctx_t *i_ctx_p) |
624 | 673 | { |
625 | 673 | os_ptr op = osp; |
626 | 673 | stream *s; |
627 | | |
628 | 673 | check_type(*op, t_integer); |
629 | 673 | check_file(s, op - 1); |
630 | 673 | if (sseek(s, (gs_offset_t)op->value.intval) < 0) |
631 | 0 | return_error(gs_error_ioerror); |
632 | 673 | pop(2); |
633 | 673 | return 0; |
634 | 673 | } |
635 | | |
636 | | /* ------ Non-standard extensions ------ */ |
637 | | |
638 | | /* <file> .filename <string> true */ |
639 | | /* <file> .filename false */ |
640 | | static int |
641 | | zfilename(i_ctx_t *i_ctx_p) |
642 | 1.36k | { |
643 | 1.36k | os_ptr op = osp; |
644 | 1.36k | stream *s; |
645 | 1.36k | gs_const_string fname; |
646 | 1.36k | byte *str; |
647 | | |
648 | 1.36k | check_file(s, op); |
649 | 1.36k | if (sfilename(s, &fname) < 0) { |
650 | 0 | make_false(op); |
651 | 0 | return 0; |
652 | 0 | } |
653 | 1.36k | check_ostack(1); |
654 | 1.36k | str = ialloc_string(fname.size, "filename"); |
655 | 1.36k | if (str == 0) |
656 | 0 | return_error(gs_error_VMerror); |
657 | 1.36k | memcpy(str, fname.data, fname.size); |
658 | 1.36k | push(1); /* can't fail */ |
659 | 1.36k | make_const_string( op - 1 , |
660 | 1.36k | a_all | imemory_space((const struct gs_ref_memory_s*) imemory), |
661 | 1.36k | fname.size, |
662 | 1.36k | str); |
663 | 1.36k | make_true(op); |
664 | 1.36k | return 0; |
665 | 1.36k | } |
666 | | |
667 | | /* <file> .isprocfilter <bool> */ |
668 | | static int |
669 | | zisprocfilter(i_ctx_t *i_ctx_p) |
670 | 0 | { |
671 | 0 | os_ptr op = osp; |
672 | 0 | stream *s; |
673 | |
|
674 | 0 | check_file(s, op); |
675 | 0 | while (s->strm != 0) |
676 | 0 | s = s->strm; |
677 | 0 | make_bool(op, s_is_proc(s)); |
678 | 0 | return 0; |
679 | 0 | } |
680 | | |
681 | | /* <file> <string> .peekstring <substring> <filled_bool> */ |
682 | | static int |
683 | | zpeekstring(i_ctx_t *i_ctx_p) |
684 | 135k | { |
685 | 135k | os_ptr op = osp; |
686 | 135k | stream *s; |
687 | 135k | uint len, rlen; |
688 | | |
689 | 135k | check_read_file(i_ctx_p, s, op - 1); |
690 | 135k | check_write_type(*op, t_string); |
691 | 135k | len = r_size(op); |
692 | 135k | while ((rlen = sbufavailable(s)) < len) { |
693 | 814 | int status = s->end_status; |
694 | | |
695 | 814 | switch (status) { |
696 | 0 | case EOFC: |
697 | 0 | break; |
698 | 814 | case 0: |
699 | | /* |
700 | | * The following is a HACK. It should reallocate the buffer to hold |
701 | | * at least len bytes. However, this raises messy problems about |
702 | | * which allocator to use and how it should interact with restore. |
703 | | */ |
704 | 814 | if (len >= s->bsize) |
705 | 0 | return_error(gs_error_rangecheck); |
706 | 814 | s_process_read_buf(s); |
707 | 814 | continue; |
708 | 0 | default: |
709 | 0 | return handle_read_status(i_ctx_p, status, op - 1, NULL, |
710 | 0 | zpeekstring); |
711 | 814 | } |
712 | 0 | break; |
713 | 814 | } |
714 | 135k | if (rlen > len) |
715 | 135k | rlen = len; |
716 | | /* Don't remove the data from the buffer. */ |
717 | 135k | memcpy(op->value.bytes, sbufptr(s), rlen); |
718 | 135k | r_set_size(op, rlen); |
719 | 135k | op[-1] = *op; |
720 | 135k | make_bool(op, (rlen == len ? 1 : 0)); |
721 | 135k | return 0; |
722 | 135k | } |
723 | | |
724 | | /* <file> <int> .unread - */ |
725 | | static int |
726 | | zunread(i_ctx_t *i_ctx_p) |
727 | 0 | { |
728 | 0 | os_ptr op = osp; |
729 | 0 | stream *s; |
730 | 0 | ulong ch; |
731 | |
|
732 | 0 | check_read_file(i_ctx_p, s, op - 1); |
733 | 0 | check_type(*op, t_integer); |
734 | 0 | ch = op->value.intval; |
735 | 0 | if (ch > 0xff) |
736 | 0 | return_error(gs_error_rangecheck); |
737 | 0 | if (sungetc(s, (byte) ch) < 0) |
738 | 0 | return_error(gs_error_ioerror); |
739 | 0 | pop(2); |
740 | 0 | return 0; |
741 | 0 | } |
742 | | |
743 | | /* <file> <obj> <==flag> .writecvp - */ |
744 | | static int zwritecvp_continue(i_ctx_t *); |
745 | | static int |
746 | | zwritecvp_at(i_ctx_t *i_ctx_p, os_ptr op, uint start, bool first) |
747 | 70.2k | { |
748 | 70.2k | stream *s; |
749 | 70.2k | byte str[100]; /* arbitrary */ |
750 | 70.2k | ref rstr; |
751 | 70.2k | const byte *data = str; |
752 | 70.2k | uint len; |
753 | 70.2k | int code, status; |
754 | | |
755 | 70.2k | check_write_file(s, op - 2); |
756 | 70.2k | check_type(*op, t_integer); |
757 | 70.2k | code = obj_cvp(op - 1, str, sizeof(str), &len, (int)op->value.intval, |
758 | 70.2k | start, imemory, true); |
759 | 70.2k | if (code == gs_error_rangecheck) { |
760 | 0 | code = obj_string_data(imemory, op - 1, &data, &len); |
761 | 0 | if (len < start) |
762 | 0 | return_error(gs_error_rangecheck); |
763 | 0 | data += start; |
764 | 0 | len -= start; |
765 | 0 | } |
766 | 70.2k | if (code < 0) |
767 | 0 | return code; |
768 | 70.2k | r_set_size(&rstr, len); |
769 | 70.2k | rstr.value.const_bytes = data; |
770 | 70.2k | status = write_string(&rstr, s); |
771 | 70.2k | switch (status) { |
772 | 0 | default: |
773 | 0 | return_error(gs_error_ioerror); |
774 | 70.2k | case 0: |
775 | 70.2k | break; |
776 | 0 | case INTC: |
777 | 0 | case CALLC: |
778 | 0 | len = start + len - r_size(&rstr); |
779 | 0 | if (!first) |
780 | 0 | --osp; /* pop(1) without affecting op */ |
781 | 0 | return handle_write_status(i_ctx_p, status, op - 2, &len, |
782 | 0 | zwritecvp_continue); |
783 | 70.2k | } |
784 | 70.2k | if (code == 1) { |
785 | 3 | if (first) |
786 | 3 | check_ostack(1); |
787 | 3 | push_op_estack(zwritecvp_continue); |
788 | 3 | if (first) |
789 | 3 | push(1); |
790 | 3 | make_int(osp, start + len); |
791 | 3 | return o_push_estack; |
792 | 3 | } |
793 | 70.2k | if (first) /* zwritecvp */ |
794 | 70.2k | pop(3); |
795 | 1 | else /* zwritecvp_continue */ |
796 | 1 | pop(4); |
797 | 70.2k | return 0; |
798 | 70.2k | } |
799 | | static int |
800 | | zwritecvp(i_ctx_t *i_ctx_p) |
801 | 70.2k | { |
802 | 70.2k | return zwritecvp_at(i_ctx_p, osp, 0, true); |
803 | 70.2k | } |
804 | | /* Continue a .writecvp after a callout. */ |
805 | | /* *op is the index within the string. */ |
806 | | static int |
807 | | zwritecvp_continue(i_ctx_t *i_ctx_p) |
808 | 3 | { |
809 | 3 | os_ptr op = osp; |
810 | | |
811 | 3 | check_type(*op, t_integer); |
812 | 3 | if (op->value.intval != (uint) op->value.intval) |
813 | 0 | return_error(gs_error_rangecheck); |
814 | 3 | return zwritecvp_at(i_ctx_p, op - 1, (uint) op->value.intval, false); |
815 | 3 | } |
816 | | |
817 | | /* ------ Initialization procedure ------ */ |
818 | | |
819 | | /* We need to split the table because of the 16-element limit. */ |
820 | | const op_def zfileio1_op_defs[] = { |
821 | | {"1bytesavailable", zbytesavailable}, |
822 | | {"1closefile", zclosefile}, |
823 | | /* currentfile is in zcontrol.c */ |
824 | | {"1echo", zecho}, |
825 | | {"1.filename", zfilename}, |
826 | | {"1.fileposition", zxfileposition}, |
827 | | {"1fileposition", zfileposition}, |
828 | | {"0flush", zflush}, |
829 | | {"1flushfile", zflushfile}, |
830 | | {"1.isprocfilter", zisprocfilter}, |
831 | | {"2.peekstring", zpeekstring}, |
832 | | {"1print", zprint}, |
833 | | {"1read", zread}, |
834 | | {"2readhexstring", zreadhexstring}, |
835 | | {"2readline", zreadline}, |
836 | | {"2readstring", zreadstring}, |
837 | | op_def_end(0) |
838 | | }; |
839 | | const op_def zfileio2_op_defs[] = { |
840 | | {"1resetfile", zresetfile}, |
841 | | {"2setfileposition", zsetfileposition}, |
842 | | {"2.unread", zunread}, |
843 | | {"2write", zwrite}, |
844 | | {"3.writecvp", zwritecvp}, |
845 | | {"2writehexstring", zwritehexstring}, |
846 | | {"2writestring", zwritestring}, |
847 | | /* Internal operators */ |
848 | | {"3%zreadhexstring_continue", zreadhexstring_continue}, |
849 | | {"3%zreadline_continue", zreadline_continue}, |
850 | | {"3%zreadstring_continue", zreadstring_continue}, |
851 | | {"4%zwritecvp_continue", zwritecvp_continue}, |
852 | | {"3%zwritehexstring_continue", zwritehexstring_continue}, |
853 | | op_def_end(0) |
854 | | }; |
855 | | |
856 | | /* ------ Non-operator routines ------ */ |
857 | | |
858 | | /* Switch a file open for read/write access but currently in write mode */ |
859 | | /* to read mode. */ |
860 | | int |
861 | | file_switch_to_read(const ref * op) |
862 | 669 | { |
863 | 669 | stream *s = fptr(op); |
864 | | |
865 | 669 | if (s->write_id != r_size(op) || s->file == 0) /* not valid */ |
866 | 0 | return_error(gs_error_invalidaccess); |
867 | 669 | if (sswitch(s, false) < 0) |
868 | 0 | return_error(gs_error_ioerror); |
869 | 669 | s->read_id = s->write_id; /* enable reading */ |
870 | 669 | s->write_id = 0; /* disable writing */ |
871 | 669 | return 0; |
872 | 669 | } |
873 | | |
874 | | /* Switch a file open for read/write access but currently in read mode */ |
875 | | /* to write mode. */ |
876 | | int |
877 | | file_switch_to_write(const ref * op) |
878 | 0 | { |
879 | 0 | stream *s = fptr(op); |
880 | |
|
881 | 0 | if (s->read_id != r_size(op) || s->file == 0) /* not valid */ |
882 | 0 | return_error(gs_error_invalidaccess); |
883 | 0 | if (sswitch(s, true) < 0) |
884 | 0 | return_error(gs_error_ioerror); |
885 | 0 | s->write_id = s->read_id; /* enable writing */ |
886 | 0 | s->read_id = 0; /* disable reading */ |
887 | 0 | return 0; |
888 | 0 | } |
889 | | |
890 | | /* ------ Internal routines ------ */ |
891 | | |
892 | | /* Write a string on a file. The file and string have been validated. */ |
893 | | /* If the status is INTC or CALLC, updates the string on the o-stack. */ |
894 | | static int |
895 | | write_string(ref * op, stream * s) |
896 | 147k | { |
897 | 147k | const byte *data = op->value.const_bytes; |
898 | 147k | uint len = r_size(op); |
899 | 147k | uint wlen; |
900 | 147k | int status = sputs(s, data, len, &wlen); |
901 | | |
902 | 147k | switch (status) { |
903 | 0 | case INTC: |
904 | 0 | case CALLC: |
905 | 0 | op->value.const_bytes = data + wlen; |
906 | 0 | r_set_size(op, len - wlen); |
907 | | /* falls through */ |
908 | 147k | default: /* 0, EOFC, ERRC */ |
909 | 147k | return status; |
910 | 147k | } |
911 | 147k | } |
912 | | |
913 | | /* |
914 | | * Look for a stream error message that needs to be copied to |
915 | | * $error.errorinfo, if any. |
916 | | */ |
917 | | static int |
918 | | copy_error_string(i_ctx_t *i_ctx_p, const ref *fop) |
919 | 0 | { |
920 | 0 | stream *s; |
921 | |
|
922 | 0 | for (s = fptr(fop); s->strm != 0 && s->state->error_string[0] == 0;) |
923 | 0 | s = s->strm; |
924 | 0 | if (s->state->error_string[0]) { |
925 | 0 | int code = gs_errorinfo_put_string(i_ctx_p, s->state->error_string); |
926 | |
|
927 | 0 | if (code < 0) |
928 | 0 | return code; |
929 | 0 | s->state->error_string[0] = 0; /* just do it once */ |
930 | 0 | } |
931 | 0 | return_error(gs_error_ioerror); |
932 | 0 | } |
933 | | |
934 | | /* Handle an exceptional status return from a read stream. */ |
935 | | /* fop points to the ref for the stream. */ |
936 | | /* ch may be any stream exceptional value. */ |
937 | | /* Return 0, 1 (EOF), o_push_estack, or an error. */ |
938 | | static int |
939 | | handle_read_status(i_ctx_t *i_ctx_p, int ch, const ref * fop, |
940 | | const uint * pindex, op_proc_t cont) |
941 | 0 | { |
942 | 0 | switch (ch) { |
943 | 0 | default: /* error */ |
944 | 0 | return copy_error_string(i_ctx_p, fop); |
945 | 0 | case EOFC: |
946 | 0 | return 1; |
947 | 0 | case INTC: |
948 | 0 | case CALLC: |
949 | 0 | if (pindex) { |
950 | 0 | ref index; |
951 | |
|
952 | 0 | make_int(&index, *pindex); |
953 | 0 | return s_handle_read_exception(i_ctx_p, ch, fop, &index, 1, |
954 | 0 | cont); |
955 | 0 | } else |
956 | 0 | return s_handle_read_exception(i_ctx_p, ch, fop, NULL, 0, |
957 | 0 | cont); |
958 | 0 | } |
959 | 0 | } |
960 | | |
961 | | /* Handle an exceptional status return from a write stream. */ |
962 | | /* fop points to the ref for the stream. */ |
963 | | /* ch may be any stream exceptional value. */ |
964 | | /* Return 0, 1 (EOF), o_push_estack, or an error. */ |
965 | | static int |
966 | | handle_write_status(i_ctx_t *i_ctx_p, int ch, const ref * fop, |
967 | | const uint * pindex, op_proc_t cont) |
968 | 0 | { |
969 | 0 | switch (ch) { |
970 | 0 | default: /* error */ |
971 | 0 | return copy_error_string(i_ctx_p, fop); |
972 | 0 | case EOFC: |
973 | 0 | return 1; |
974 | 0 | case INTC: |
975 | 0 | case CALLC: |
976 | 0 | if (pindex) { |
977 | 0 | ref index; |
978 | |
|
979 | 0 | make_int(&index, *pindex); |
980 | 0 | return s_handle_write_exception(i_ctx_p, ch, fop, &index, 1, |
981 | 0 | cont); |
982 | 0 | } else |
983 | 0 | return s_handle_write_exception(i_ctx_p, ch, fop, NULL, 0, |
984 | 0 | cont); |
985 | 0 | } |
986 | 0 | } |