/src/ghostpdl/base/stream.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2024 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Stream package for Ghostscript interpreter */ |
18 | | #include "stdio_.h" /* includes std.h */ |
19 | | #include "memory_.h" |
20 | | #include "gdebug.h" |
21 | | #include "gpcheck.h" |
22 | | #include "stream.h" |
23 | | #include "strimpl.h" |
24 | | |
25 | | /* Forward declarations */ |
26 | | int s_close_disable(stream *); |
27 | | static int sreadbuf(stream *, stream_cursor_write *); |
28 | | static int swritebuf(stream *, stream_cursor_read *, bool); |
29 | | static void stream_compact(stream *, bool); |
30 | | |
31 | | /* Structure types for allocating streams. */ |
32 | | public_st_stream(); |
33 | | public_st_stream_state(); /* default */ |
34 | | /* GC procedures */ |
35 | | static |
36 | 16.3M | ENUM_PTRS_WITH(stream_enum_ptrs, stream *st) return 0; |
37 | 2.34M | case 0: |
38 | 2.34M | if (st->foreign) |
39 | 0 | ENUM_RETURN(NULL); |
40 | 2.34M | else if (st->cbuf_string.data != 0) |
41 | 0 | ENUM_RETURN_STRING_PTR(stream, cbuf_string); |
42 | 2.34M | else |
43 | 2.34M | ENUM_RETURN(st->cbuf); |
44 | 2.34M | ENUM_PTR3(1, stream, strm, prev, next); |
45 | 2.34M | ENUM_PTR(4, stream, state); |
46 | 2.34M | case 5: return ENUM_CONST_STRING(&st->file_name); |
47 | 16.3M | ENUM_PTRS_END |
48 | 2.34M | static RELOC_PTRS_WITH(stream_reloc_ptrs, stream *st) |
49 | 2.34M | { |
50 | 2.34M | byte *cbuf_old = st->cbuf; |
51 | | |
52 | 2.34M | if (cbuf_old != 0 && !st->foreign) { |
53 | 1.40M | long reloc; |
54 | | |
55 | 1.40M | if (st->cbuf_string.data != 0) { |
56 | 0 | RELOC_STRING_VAR(st->cbuf_string); |
57 | 0 | st->cbuf = st->cbuf_string.data; |
58 | 0 | } else |
59 | 1.40M | RELOC_VAR(st->cbuf); |
60 | 1.40M | reloc = cbuf_old - st->cbuf; |
61 | | /* Relocate the other buffer pointers. */ |
62 | 1.40M | st->cursor.r.ptr -= reloc; |
63 | 1.40M | st->cursor.r.limit -= reloc; /* same as swptr */ |
64 | 1.40M | st->cursor.w.limit -= reloc; |
65 | 1.40M | } |
66 | 2.34M | RELOC_VAR(st->strm); |
67 | 2.34M | RELOC_VAR(st->prev); |
68 | 2.34M | RELOC_VAR(st->next); |
69 | 2.34M | RELOC_VAR(st->state); |
70 | 2.34M | RELOC_CONST_STRING_VAR(st->file_name); |
71 | 2.34M | } |
72 | 2.34M | RELOC_PTRS_END |
73 | | /* Finalize a stream by closing it. */ |
74 | | /* We only do this for file streams, because other kinds of streams */ |
75 | | /* may attempt to free storage when closing. */ |
76 | | static void |
77 | | stream_finalize(const gs_memory_t *cmem, void *vptr) |
78 | 100M | { |
79 | 100M | stream *const st = vptr; |
80 | 100M | (void)cmem; /* unused */ |
81 | | |
82 | 100M | if_debug2m('u', st->memory, "[u]%s "PRI_INTPTR"\n", |
83 | 100M | (!s_is_valid(st) ? "already closed:" : |
84 | 100M | st->is_temp ? "is_temp set:" : |
85 | 100M | st->file == 0 ? "not file:" : |
86 | 100M | "closing file:"), (intptr_t) st); |
87 | 100M | if (s_is_valid(st) && !st->is_temp && st->file != 0) { |
88 | | /* Prevent any attempt to free the buffer. */ |
89 | 416 | st->cbuf = 0; |
90 | 416 | st->cbuf_string.data = 0; |
91 | 416 | sclose(st); /* ignore errors */ |
92 | 416 | } |
93 | 100M | } |
94 | | |
95 | | /* Dummy template for streams that don't have a separate state. */ |
96 | | static const stream_template s_no_template = { |
97 | | &st_stream_state, 0, 0, 1, 1, 0 |
98 | | }; |
99 | | |
100 | | /* ------ Generic procedures ------ */ |
101 | | |
102 | | /* Allocate a stream and initialize it minimally. */ |
103 | | void |
104 | | s_init(stream *s, gs_memory_t * mem) |
105 | 136M | { |
106 | 136M | s->memory = mem; |
107 | 136M | s->report_error = s_no_report_error; |
108 | 136M | s->min_left = 0; |
109 | 136M | s->error_string[0] = 0; |
110 | 136M | s->prev = s->next = 0; /* clean for GC */ |
111 | 136M | s->file_name.data = 0; /* ibid. */ |
112 | 136M | s->file_name.size = 0; |
113 | 136M | s->end_status = 0; |
114 | 136M | s->modes = 0; |
115 | 136M | s->close_strm = false; /* default */ |
116 | 136M | s->close_at_eod = true; /* default */ |
117 | 136M | s->cbuf_string_memory = NULL; |
118 | 136M | } |
119 | | stream * |
120 | | s_alloc(gs_memory_t * mem, client_name_t cname) |
121 | 99.9M | { |
122 | 99.9M | stream *s = gs_alloc_struct(mem, stream, &st_stream, cname); |
123 | | |
124 | 99.9M | if_debug2m('s', mem, "[s]alloc(%s) = "PRI_INTPTR"\n", |
125 | 99.9M | client_name_string(cname), (intptr_t) s); |
126 | 99.9M | if (s == 0) |
127 | 0 | return 0; |
128 | 99.9M | s_init(s, mem); |
129 | 99.9M | return s; |
130 | 99.9M | } |
131 | | stream * |
132 | | s_alloc_immovable(gs_memory_t * mem, client_name_t cname) |
133 | 94.5k | { |
134 | 94.5k | stream *s = gs_alloc_struct_immovable(mem, stream, &st_stream, cname); |
135 | | |
136 | 94.5k | if_debug2m('s', mem, "[s]alloc(%s) = "PRI_INTPTR"\n", |
137 | 94.5k | client_name_string(cname), (intptr_t) s); |
138 | 94.5k | if (s == 0) |
139 | 0 | return 0; |
140 | 94.5k | s_init(s, mem); |
141 | 94.5k | return s; |
142 | 94.5k | } |
143 | | |
144 | | /* Allocate a stream state and initialize it minimally. */ |
145 | | void |
146 | | s_init_state(stream_state *st, const stream_template *templat, |
147 | | gs_memory_t *mem) |
148 | 23.2M | { |
149 | 23.2M | st->templat = templat; |
150 | 23.2M | st->memory = mem; |
151 | 23.2M | st->report_error = s_no_report_error; |
152 | 23.2M | st->min_left = 0; |
153 | 23.2M | st->error_string[0] = 0; |
154 | 23.2M | } |
155 | | stream_state * |
156 | | s_alloc_state(gs_memory_t * mem, gs_memory_type_ptr_t stype, |
157 | | client_name_t cname) |
158 | 8.69M | { |
159 | 8.69M | stream_state *st = gs_alloc_struct(mem, stream_state, stype, cname); |
160 | | |
161 | 8.69M | if_debug3m('s', mem, "[s]alloc_state %s(%s) = "PRI_INTPTR"\n", |
162 | 8.69M | client_name_string(cname), |
163 | 8.69M | client_name_string(stype->sname), |
164 | 8.69M | (intptr_t) st); |
165 | 8.69M | if (st) |
166 | 8.69M | s_init_state(st, NULL, mem); |
167 | 8.69M | return st; |
168 | 8.69M | } |
169 | | |
170 | | /* Standard stream initialization */ |
171 | | void |
172 | | s_std_init(register stream * s, byte * ptr, uint len, const stream_procs * pp, |
173 | | int modes) |
174 | 62.7M | { |
175 | 62.7M | s->templat = &s_no_template; |
176 | 62.7M | s->cbuf = ptr; |
177 | | |
178 | | /* IMPORTANT: "read" MUST come before "write" - see comment in scommon.h about |
179 | | * the layout of read/write cursor structures. |
180 | | */ |
181 | 62.7M | stream_cursor_read_init(&s->cursor.r, ptr, 0); |
182 | 62.7M | stream_cursor_write_init(&s->cursor.w, ptr, len); |
183 | | |
184 | 62.7M | s->end_status = 0; |
185 | 62.7M | s->foreign = 0; |
186 | 62.7M | s->modes = modes; |
187 | 62.7M | s->cbuf_string.data = 0; |
188 | 62.7M | s->position = 0; |
189 | 62.7M | s->bsize = s->cbsize = len; |
190 | 62.7M | s->strm = 0; /* not a filter */ |
191 | 62.7M | s->is_temp = 0; |
192 | 62.7M | s->procs = *pp; |
193 | 62.7M | s->state = (stream_state *) s; /* hack to avoid separate state */ |
194 | 62.7M | s->file = 0; |
195 | 62.7M | s->file_name.data = 0; /* in case stream is on stack */ |
196 | 62.7M | s->file_name.size = 0; |
197 | 62.7M | s->cbuf_string_memory = NULL; |
198 | 62.7M | if (s->memory) { |
199 | 33.3M | if_debug4m('s', s->memory, "[s]init "PRI_INTPTR", buf="PRI_INTPTR", len=%u, modes=%d\n", |
200 | 33.3M | (intptr_t) s, (intptr_t) ptr, len, modes); |
201 | 33.3M | } |
202 | 62.7M | } |
203 | | |
204 | | /* Set the file name of a stream, copying the name. */ |
205 | | /* Return <0 if the copy could not be allocated. */ |
206 | | int |
207 | | ssetfilename(stream *s, const byte *data, uint size) |
208 | 54.2M | { |
209 | 54.2M | byte *str = |
210 | 54.2M | (s->file_name.data == 0 ? |
211 | 15.8M | gs_alloc_string(s->memory, size + 1, "ssetfilename") : |
212 | 54.2M | gs_resize_string(s->memory, |
213 | 54.2M | (byte *)s->file_name.data, /* break const */ |
214 | 54.2M | s->file_name.size, |
215 | 54.2M | size + 1, "ssetfilename")); |
216 | | |
217 | 54.2M | if (str == 0) |
218 | 0 | return -1; |
219 | 54.2M | memcpy(str, data, size); |
220 | 54.2M | str[size] = 0; |
221 | 54.2M | s->file_name.data = str; |
222 | 54.2M | s->file_name.size = size + 1; |
223 | 54.2M | return 0; |
224 | 54.2M | } |
225 | | |
226 | | /* Return the file name of a stream, if any. */ |
227 | | /* There is a guaranteed 0 byte after the string. */ |
228 | | int |
229 | | sfilename(stream *s, gs_const_string *pfname) |
230 | 494k | { |
231 | 494k | pfname->data = s->file_name.data; |
232 | 494k | if (pfname->data == 0) { |
233 | 104k | pfname->size = 0; |
234 | 104k | return -1; |
235 | 104k | } |
236 | 389k | pfname->size = s->file_name.size - 1; /* omit terminator */ |
237 | 389k | return 0; |
238 | 494k | } |
239 | | |
240 | | /* Implement a stream procedure as a no-op. */ |
241 | | int |
242 | | s_std_null(stream * s) |
243 | 7.13M | { |
244 | 7.13M | return 0; |
245 | 7.13M | } |
246 | | |
247 | | /* Discard the contents of the buffer when reading. */ |
248 | | void |
249 | | s_std_read_reset(stream * s) |
250 | 0 | { |
251 | 0 | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1; |
252 | 0 | } |
253 | | |
254 | | /* Discard the contents of the buffer when writing. */ |
255 | | void |
256 | | s_std_write_reset(stream * s) |
257 | 0 | { |
258 | 0 | s->cursor.w.ptr = s->cbuf - 1; |
259 | 0 | } |
260 | | |
261 | | /* Flush data to end-of-file when reading. */ |
262 | | int |
263 | | s_std_read_flush(stream * s) |
264 | 487k | { |
265 | 1.13M | while (1) { |
266 | 1.13M | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1; |
267 | 1.13M | if (s->end_status) |
268 | 487k | break; |
269 | 651k | s_process_read_buf(s); |
270 | 651k | } |
271 | 487k | return (s->end_status == EOFC ? 0 : s->end_status); |
272 | 487k | } |
273 | | |
274 | | /* Flush buffered data when writing. */ |
275 | | int |
276 | | s_std_write_flush(stream * s) |
277 | 4.19M | { |
278 | 4.19M | return s_process_write_buf(s, false); |
279 | 4.19M | } |
280 | | |
281 | | /* Indicate that the number of available input bytes is unknown. */ |
282 | | int |
283 | | s_std_noavailable(stream * s, gs_offset_t *pl) |
284 | 25 | { |
285 | 25 | *pl = -1; |
286 | 25 | return 0; |
287 | 25 | } |
288 | | |
289 | | /* Indicate an error when asked to seek. */ |
290 | | int |
291 | | s_std_noseek(stream * s, gs_offset_t pos) |
292 | 12 | { |
293 | 12 | return ERRC; |
294 | 12 | } |
295 | | |
296 | | /* Standard stream closing. */ |
297 | | int |
298 | | s_std_close(stream * s) |
299 | 8.68M | { |
300 | 8.68M | return 0; |
301 | 8.68M | } |
302 | | |
303 | | /* Standard stream mode switching. */ |
304 | | int |
305 | | s_std_switch_mode(stream * s, bool writing) |
306 | 0 | { |
307 | 0 | return ERRC; |
308 | 0 | } |
309 | | |
310 | | /* Standard stream finalization. Disable the stream. */ |
311 | | void |
312 | | s_disable(register stream * s) |
313 | 131M | { |
314 | 131M | s->cbuf = 0; |
315 | 131M | s->bsize = 0; |
316 | 131M | s->end_status = EOFC; |
317 | 131M | s->modes = 0; |
318 | 131M | s->cbuf_string.data = 0; |
319 | | /* The pointers in the next two statements should really be */ |
320 | | /* initialized to ([const] byte *)0 - 1, but some very picky */ |
321 | | /* compilers complain about this. */ |
322 | 131M | s->cursor.r.ptr = s->cursor.r.limit = 0; |
323 | 131M | s->cursor.w.limit = 0; |
324 | 131M | s->procs.close = s_std_null; |
325 | | /* Clear pointers for GC */ |
326 | 131M | s->strm = 0; |
327 | 131M | s->state = (stream_state *) s; |
328 | 131M | s->templat = &s_no_template; |
329 | | /* Free the file name. */ |
330 | 131M | if (s->file_name.data) { |
331 | 15.7M | if (s->memory) { |
332 | 15.7M | gs_free_const_string(s->memory, s->file_name.data, s->file_name.size, |
333 | 15.7M | "s_disable(file_name)"); |
334 | 15.7M | } |
335 | 15.7M | s->file_name.data = 0; |
336 | 15.7M | s->file_name.size = 0; |
337 | 15.7M | } |
338 | | /****** SHOULD DO MORE THAN THIS ******/ |
339 | 131M | if (s->memory) { |
340 | 127M | if_debug1m('s', s->memory, "[s]disable "PRI_INTPTR"\n", (intptr_t) s); |
341 | 127M | } |
342 | 131M | } |
343 | | |
344 | | /* Implement flushing for encoding filters. */ |
345 | | int |
346 | | s_filter_write_flush(register stream * s) |
347 | 114k | { |
348 | 114k | int status = s_process_write_buf(s, false); |
349 | | |
350 | 114k | if (status != 0) |
351 | 0 | return status; |
352 | 114k | return sflush(s->strm); |
353 | 114k | } |
354 | | |
355 | | /* Close a filter. If this is an encoding filter, flush it first. */ |
356 | | /* If CloseTarget was specified (close_strm), then propagate the sclose */ |
357 | | int |
358 | | s_filter_close(register stream * s) |
359 | 7.28M | { |
360 | 7.28M | int status; |
361 | 7.28M | bool close = s->close_strm; |
362 | 7.28M | stream *stemp = s->strm; |
363 | | |
364 | 7.28M | if (s_is_writing(s)) { |
365 | 1.18M | int status = s_process_write_buf(s, true); |
366 | | |
367 | 1.18M | if (status != 0 && status != EOFC) |
368 | 184k | return status; |
369 | 1.00M | if (status != EOFC) |
370 | 0 | status = sflush(stemp); |
371 | 1.00M | if (status != 0 && status != EOFC) |
372 | 0 | return status; |
373 | 1.00M | } |
374 | 7.10M | status = s_std_close(s); |
375 | 7.10M | if (status != 0 && status != EOFC) |
376 | 0 | return status; |
377 | 7.10M | if (close && stemp != 0) |
378 | 34.0k | return sclose(stemp); |
379 | 7.06M | return status; |
380 | 7.10M | } |
381 | | |
382 | | /* Disregard a stream error message. */ |
383 | | int |
384 | | s_no_report_error(stream_state * st, const char *str) |
385 | 23 | { |
386 | 23 | return 0; |
387 | 23 | } |
388 | | |
389 | | /* Generic procedure structures for filters. */ |
390 | | |
391 | | const stream_procs s_filter_read_procs = { |
392 | | s_std_noavailable, s_std_noseek, s_std_read_reset, |
393 | | s_std_read_flush, s_filter_close |
394 | | }; |
395 | | |
396 | | const stream_procs s_filter_write_procs = { |
397 | | s_std_noavailable, s_std_noseek, s_std_write_reset, |
398 | | s_filter_write_flush, s_filter_close |
399 | | }; |
400 | | |
401 | | /* ------ Implementation-independent procedures ------ */ |
402 | | |
403 | | /* Store the amount of available data in a(n input) stream. */ |
404 | | int |
405 | | savailable(stream * s, gs_offset_t *pl) |
406 | 13.8M | { |
407 | 13.8M | return (*(s)->procs.available) (s, pl); |
408 | 13.8M | } |
409 | | |
410 | | /* Return the current position of a stream. */ |
411 | | gs_offset_t |
412 | | stell(stream * s) |
413 | 263M | { |
414 | | /* |
415 | | * The stream might have been closed, but the position |
416 | | * is still meaningful in this case. |
417 | | */ |
418 | 263M | const byte *ptr = (s_is_writing(s) ? s->cursor.w.ptr : s->cursor.r.ptr); |
419 | | |
420 | 263M | return (ptr == 0 ? 0 : ptr + 1 - s->cbuf) + s->position; |
421 | 263M | } |
422 | | |
423 | | /* Set the position of a stream. */ |
424 | | int |
425 | | spseek(stream * s, gs_offset_t pos) |
426 | 57.0M | { |
427 | 57.0M | if_debug3m('s', s->memory, "[s]seek 0x%"PRIx64" to %"PRId64", position was %"PRId64"\n", |
428 | 57.0M | (uint64_t)s, (int64_t)pos, (int64_t)stell(s)); |
429 | 57.0M | return (*(s)->procs.seek) (s, pos); |
430 | 57.0M | } |
431 | | |
432 | | /* Switch a stream to read or write mode. */ |
433 | | /* Return 0 or ERRC. */ |
434 | | int |
435 | | sswitch(register stream * s, bool writing) |
436 | 94.5k | { |
437 | 94.5k | if (s->procs.switch_mode == 0) |
438 | 0 | return ERRC; |
439 | 94.5k | return (*s->procs.switch_mode) (s, writing); |
440 | 94.5k | } |
441 | | |
442 | | /* Close a stream, disabling it if successful. */ |
443 | | /* (The stream may already be closed.) */ |
444 | | int |
445 | | sclose(register stream * s) |
446 | 31.9M | { |
447 | 31.9M | stream_state *st; |
448 | 31.9M | int status = (*s->procs.close) (s); |
449 | | |
450 | 31.9M | if (status < 0) |
451 | 184k | return status; |
452 | 31.7M | st = s->state; |
453 | 31.7M | if (st != 0) { |
454 | 31.7M | stream_proc_release((*release)) = st->templat->release; |
455 | 31.7M | if (release != 0) |
456 | 2.60M | (*release) (st); |
457 | 31.7M | if (st != (stream_state *) s && st->memory != 0) { |
458 | 7.21M | gs_memory_t *mem = st->memory; |
459 | 7.21M | st->memory = NULL; |
460 | 7.21M | gs_free_object(mem, st, "s_std_close"); |
461 | 7.21M | } |
462 | 31.7M | s->state = (stream_state *) s; |
463 | 31.7M | } |
464 | 31.7M | s_disable(s); |
465 | 31.7M | return status; |
466 | 31.9M | } |
467 | | |
468 | | /* |
469 | | * Implement sgetc when the buffer may be empty. If the buffer really is |
470 | | * empty, refill it and then read a byte. Note that filters must read one |
471 | | * byte ahead, so that they can close immediately after the client reads the |
472 | | * last data byte if the next thing is an EOD. |
473 | | */ |
474 | | int |
475 | | spgetcc(register stream * s, bool close_at_eod) |
476 | 13.9G | { |
477 | 13.9G | int status, left; |
478 | 13.9G | int min_left = sbuf_min_left(s); |
479 | | |
480 | 14.1G | while (status = s->end_status, |
481 | 14.1G | left = s->cursor.r.limit - s->cursor.r.ptr, |
482 | 14.1G | left <= min_left && status >= 0 |
483 | 13.9G | ) |
484 | 167M | s_process_read_buf(s); |
485 | 13.9G | if (left <= min_left && |
486 | 13.9G | (left <= 0 || (status != EOFC && status != ERRC)) |
487 | 13.9G | ) { |
488 | | /* Compact the stream so stell will return the right result. */ |
489 | 10.4M | if (left == 0) |
490 | 10.4M | stream_compact(s, true); |
491 | 10.4M | if (status == EOFC && close_at_eod && s->close_at_eod) { |
492 | 7.74M | status = sclose(s); |
493 | 7.74M | if (status == 0) |
494 | 7.74M | status = EOFC; |
495 | 7.74M | s->end_status = status; |
496 | 7.74M | } |
497 | 10.4M | return status; |
498 | 10.4M | } |
499 | 13.9G | return *++(s->cursor.r.ptr); |
500 | 13.9G | } |
501 | | |
502 | | /* Implementing sputc when the buffer is full, */ |
503 | | /* by flushing the buffer and then writing the byte. */ |
504 | | int |
505 | | spputc(register stream * s, byte b) |
506 | 735M | { |
507 | 928M | for (;;) { |
508 | 928M | if (s->end_status) |
509 | 0 | return s->end_status; |
510 | 928M | if (!sendwp(s)) { |
511 | 735M | *++(s->cursor.w.ptr) = b; |
512 | 735M | return b; |
513 | 735M | } |
514 | 192M | s_process_write_buf(s, false); |
515 | 192M | } |
516 | 735M | } |
517 | | |
518 | | /* Push back a character onto a (read) stream. */ |
519 | | /* The character must be the same as the last one read. */ |
520 | | /* Return 0 on success, ERRC on failure. */ |
521 | | int |
522 | | sungetc(register stream * s, byte c) |
523 | 26.0k | { |
524 | | /* cbuf == NULL means this stream is stdin, and we shouldn't |
525 | | unread from stdin, ever. |
526 | | */ |
527 | 26.0k | if (s->cbuf == NULL || !s_is_reading(s) || |
528 | 26.0k | s->cursor.r.ptr < s->cbuf || *(s->cursor.r.ptr) != c) |
529 | 0 | return ERRC; |
530 | 26.0k | s->cursor.r.ptr--; |
531 | 26.0k | return 0; |
532 | 26.0k | } |
533 | | |
534 | | /* Get a string from a stream. */ |
535 | | /* Return 0 if the string was filled, or an exception status. */ |
536 | | int |
537 | | sgets(stream * s, byte * buf, uint nmax, uint * pn) |
538 | 122M | { |
539 | 122M | stream_cursor_write cw; |
540 | 122M | int status = 0; |
541 | 122M | gs_offset_t min_left = sbuf_min_left(s); |
542 | | |
543 | 122M | cw.ptr = buf - 1; |
544 | 122M | cw.limit = cw.ptr + nmax; |
545 | 367M | while (cw.ptr < cw.limit) { |
546 | 255M | int left; |
547 | | |
548 | 255M | if ((left = s->cursor.r.limit - s->cursor.r.ptr) > min_left) { |
549 | 180M | s->cursor.r.limit -= min_left; |
550 | 180M | stream_move(&s->cursor.r, &cw); |
551 | 180M | s->cursor.r.limit += min_left; |
552 | 180M | } else { |
553 | 75.0M | uint wanted = cw.limit - cw.ptr; |
554 | 75.0M | int c; |
555 | 75.0M | stream_state *st; |
556 | | |
557 | 75.0M | if (wanted >= s->bsize >> 2 && |
558 | 75.0M | (st = s->state) != 0 && |
559 | 75.0M | wanted >= st->templat->min_out_size && |
560 | 75.0M | s->end_status == 0 && |
561 | 75.0M | left == 0 |
562 | 75.0M | ) { |
563 | 28.1M | byte *wptr = cw.ptr; |
564 | | |
565 | 28.1M | cw.limit -= min_left; |
566 | 28.1M | status = sreadbuf(s, &cw); |
567 | 28.1M | cw.limit += min_left; |
568 | | /* Compact the stream so stell will return the right result. */ |
569 | 28.1M | stream_compact(s, true); |
570 | | /* |
571 | | * We know the stream buffer is empty, so it's safe to |
572 | | * update position. However, we need to reset the read |
573 | | * cursor to indicate that there is no data in the buffer. |
574 | | */ |
575 | 28.1M | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1; |
576 | 28.1M | s->position += cw.ptr - wptr; |
577 | 28.1M | if (status <= 0 || cw.ptr == cw.limit) |
578 | 9.59M | break; |
579 | 28.1M | } |
580 | 65.4M | c = spgetc(s); |
581 | 65.4M | if (c < 0) { |
582 | 1.27M | status = c; |
583 | 1.27M | break; |
584 | 1.27M | } |
585 | 64.2M | *++(cw.ptr) = c; |
586 | 64.2M | } |
587 | 255M | } |
588 | 122M | *pn = cw.ptr + 1 - buf; |
589 | 122M | return (status >= 0 ? 0 : status); |
590 | 122M | } |
591 | | |
592 | | /* Write a string on a stream. */ |
593 | | /* Return 0 if the entire string was written, or an exception status. */ |
594 | | int |
595 | | sputs(register stream * s, const byte * str, uint wlen, uint * pn) |
596 | 807M | { |
597 | 807M | uint len = wlen; |
598 | 807M | int status = s->end_status; |
599 | | |
600 | 807M | if (status >= 0) |
601 | 1.96G | while (len > 0) { |
602 | 1.15G | uint count = s->cursor.w.limit - s->cursor.w.ptr; |
603 | | |
604 | 1.15G | if (count > 0) { |
605 | 972M | if (count > len) |
606 | 771M | count = len; |
607 | 972M | memcpy(s->cursor.w.ptr + 1, str, count); |
608 | 972M | s->cursor.w.ptr += count; |
609 | 972M | str += count; |
610 | 972M | len -= count; |
611 | 972M | } else { |
612 | 187M | byte ch = *str++; |
613 | | |
614 | 187M | status = sputc(s, ch); |
615 | 187M | if (status < 0) |
616 | 0 | break; |
617 | 187M | len--; |
618 | 187M | } |
619 | 1.15G | } |
620 | 807M | *pn = wlen - len; |
621 | 807M | return (status >= 0 ? 0 : status); |
622 | 807M | } |
623 | | |
624 | | /* Skip ahead a specified distance in a read stream. */ |
625 | | /* Return 0 or an exception status. */ |
626 | | /* Store the number of bytes skipped in *pskipped. */ |
627 | | int |
628 | | spskip(register stream * s, gs_offset_t nskip, gs_offset_t *pskipped) |
629 | 0 | { |
630 | 0 | gs_offset_t n = nskip; |
631 | 0 | gs_offset_t min_left; |
632 | |
|
633 | 0 | if (nskip < 0 || !s_is_reading(s)) { |
634 | 0 | *pskipped = 0; |
635 | 0 | return ERRC; |
636 | 0 | } |
637 | 0 | if (s_can_seek(s)) { |
638 | 0 | gs_offset_t pos = stell(s); |
639 | 0 | int status = sseek(s, pos + n); |
640 | |
|
641 | 0 | *pskipped = stell(s) - pos; |
642 | 0 | return status; |
643 | 0 | } |
644 | 0 | min_left = sbuf_min_left(s); |
645 | 0 | while (sbufavailable(s) < n + min_left) { |
646 | 0 | int status; |
647 | |
|
648 | 0 | n -= sbufavailable(s); |
649 | 0 | s->cursor.r.ptr = s->cursor.r.limit; |
650 | 0 | if (s->end_status) { |
651 | 0 | *pskipped = nskip - n; |
652 | 0 | return s->end_status; |
653 | 0 | } |
654 | 0 | status = sgetc(s); |
655 | 0 | if (status < 0) { |
656 | 0 | *pskipped = nskip - n; |
657 | 0 | return status; |
658 | 0 | } |
659 | 0 | --n; |
660 | 0 | } |
661 | | /* Note that if min_left > 0, n < 0 is possible; this is harmless. */ |
662 | 0 | s->cursor.r.ptr += n; |
663 | 0 | *pskipped = nskip; |
664 | 0 | return 0; |
665 | 0 | } |
666 | | |
667 | | /* Read a line from a stream. See srdline.h for the specification. */ |
668 | | int |
669 | | sreadline(stream *s_in, stream *s_out, void *readline_data, |
670 | | gs_const_string *prompt, gs_string * buf, |
671 | | gs_memory_t * bufmem, uint * pcount, bool *pin_eol, |
672 | | bool (*is_stdin)(const stream *)) |
673 | 38 | { |
674 | 38 | uint count = *pcount; |
675 | | |
676 | | /* Most systems define \n as 0xa and \r as 0xd; however, */ |
677 | | /* OS-9 has \n == \r == 0xd and \l == 0xa. The following */ |
678 | | /* code works properly regardless of environment. */ |
679 | | #if '\n' == '\r' |
680 | | # define LF 0xa |
681 | | #else |
682 | 38 | # define LF '\n' |
683 | 38 | #endif |
684 | | |
685 | 38 | if (count == 0 && s_out && prompt) { |
686 | 0 | uint ignore_n; |
687 | 0 | int ch = sputs(s_out, prompt->data, prompt->size, &ignore_n); |
688 | |
|
689 | 0 | if (ch < 0) |
690 | 0 | return ch; |
691 | 0 | } |
692 | | |
693 | 76 | top: |
694 | 76 | if (*pin_eol) { |
695 | | /* |
696 | | * We're in the middle of checking for a two-character |
697 | | * end-of-line sequence. If we get an EOF here, stop, but |
698 | | * don't signal EOF now; wait till the next read. |
699 | | */ |
700 | 38 | int ch = spgetcc(s_in, false); |
701 | | |
702 | 38 | if (ch == EOFC) { |
703 | 0 | *pin_eol = false; |
704 | 0 | return 0; |
705 | 38 | } else if (ch < 0) |
706 | 0 | return ch; |
707 | 38 | else if (ch != LF) |
708 | 0 | sputback(s_in); |
709 | 38 | *pin_eol = false; |
710 | 38 | return 0; |
711 | 38 | } |
712 | 950 | for (;;) { |
713 | 950 | int ch = sgetc(s_in); |
714 | | |
715 | 950 | if (ch < 0) { /* EOF or exception */ |
716 | 0 | *pcount = count; |
717 | 0 | return ch; |
718 | 0 | } |
719 | 950 | switch (ch) { |
720 | 38 | case '\r': |
721 | 38 | { |
722 | | #if '\n' == '\r' /* OS-9 or similar */ |
723 | | if (!is_stdin(s_in)) |
724 | | #endif |
725 | 38 | { |
726 | 38 | *pcount = count; |
727 | 38 | *pin_eol = true; |
728 | 38 | goto top; |
729 | 0 | } |
730 | 0 | } |
731 | | /* falls through */ |
732 | 0 | case LF: |
733 | 0 | #undef LF |
734 | 0 | *pcount = count; |
735 | 0 | return 0; |
736 | 950 | } |
737 | 912 | if (count >= buf->size) { /* filled the string */ |
738 | 0 | if (!bufmem) { |
739 | 0 | sputback(s_in); |
740 | 0 | *pcount = count; |
741 | 0 | return 1; |
742 | 0 | } |
743 | 0 | { |
744 | 0 | uint nsize = count + max(count, 20); |
745 | 0 | byte *ndata = gs_resize_string(bufmem, buf->data, buf->size, |
746 | 0 | nsize, "sreadline(buffer)"); |
747 | |
|
748 | 0 | if (ndata == 0) |
749 | 0 | return ERRC; /* no better choice */ |
750 | 0 | buf->data = ndata; |
751 | 0 | buf->size = nsize; |
752 | 0 | } |
753 | 0 | } |
754 | 912 | buf->data[count++] = ch; |
755 | 912 | } |
756 | | /*return 0; *//* not reached */ |
757 | 38 | } |
758 | | |
759 | | /* ------ Utilities ------ */ |
760 | | |
761 | | /* |
762 | | * Attempt to refill the buffer of a read stream. Only call this if the |
763 | | * end_status is not EOFC, and if the buffer is (nearly) empty. |
764 | | */ |
765 | | int |
766 | | s_process_read_buf(stream * s) |
767 | 186M | { |
768 | 186M | int status; |
769 | | |
770 | 186M | stream_compact(s, false); |
771 | 186M | status = sreadbuf(s, &s->cursor.w); |
772 | 186M | s->end_status = (status >= 0 ? 0 : status); |
773 | 186M | return 0; |
774 | 186M | } |
775 | | |
776 | | /* |
777 | | * Attempt to empty the buffer of a write stream. Only call this if the |
778 | | * end_status is not EOFC. |
779 | | */ |
780 | | int |
781 | | s_process_write_buf(stream * s, bool last) |
782 | 209M | { |
783 | 209M | int status = swritebuf(s, &s->cursor.r, last); |
784 | | |
785 | 209M | stream_compact(s, false); |
786 | 209M | return (status >= 0 ? 0 : status); |
787 | 209M | } |
788 | | |
789 | | /* Move forward or backward in a pipeline. We temporarily reverse */ |
790 | | /* the direction of the pointers while doing this. */ |
791 | | /* (Cf the Deutsch-Schorr-Waite graph marking algorithm.) */ |
792 | | #define MOVE_BACK(curr, prev)\ |
793 | 157M | BEGIN\ |
794 | 157M | stream *back = prev->strm;\ |
795 | 157M | prev->strm = curr; curr = prev; prev = back;\ |
796 | 157M | END |
797 | | #define MOVE_AHEAD(curr, prev)\ |
798 | 90.8M | BEGIN\ |
799 | 90.8M | stream *ahead = curr->strm;\ |
800 | 90.8M | curr->strm = prev; prev = curr; curr = ahead;\ |
801 | 90.8M | END |
802 | | |
803 | | /* |
804 | | * Read from a stream pipeline. Update end_status for all streams that were |
805 | | * actually touched. Return the status from the outermost stream: this is |
806 | | * normally the same as s->end_status, except that if s->procs.process |
807 | | * returned 1, sreadbuf sets s->end_status to 0, but returns 1. |
808 | | */ |
809 | | static int |
810 | | sreadbuf(stream * s, stream_cursor_write * pbuf) |
811 | 214M | { |
812 | 214M | stream *prev = 0; |
813 | 214M | stream *curr = s; |
814 | 214M | int status; |
815 | | |
816 | 227M | for (;;) { |
817 | 227M | stream *strm; |
818 | 227M | stream_cursor_write *pw; |
819 | 227M | byte *oldpos; |
820 | | |
821 | 239M | for (;;) { /* Descend into the recursion. */ |
822 | 239M | stream_cursor_read cr; |
823 | 239M | stream_cursor_read *pr; |
824 | 239M | int left; |
825 | 239M | bool eof; |
826 | | |
827 | 239M | strm = curr->strm; |
828 | 239M | if (strm == 0) { |
829 | 105M | cr.ptr = 0, cr.limit = 0; |
830 | 105M | pr = &cr; |
831 | 105M | left = 0; |
832 | 105M | eof = false; |
833 | 133M | } else { |
834 | 133M | pr = &strm->cursor.r; |
835 | 133M | left = sbuf_min_left(strm); |
836 | 133M | left = min(left, pr->limit - pr->ptr); |
837 | 133M | pr->limit -= left; |
838 | 133M | eof = strm->end_status == EOFC; |
839 | 133M | } |
840 | 239M | pw = (prev == 0 ? pbuf : &curr->cursor.w); |
841 | 239M | if_debug4m('s', s->memory, "[s]read process "PRI_INTPTR", nr=%u, nw=%u, eof=%d\n", |
842 | 239M | (intptr_t) curr, (uint) (pr->limit - pr->ptr), |
843 | 239M | (uint) (pw->limit - pw->ptr), eof); |
844 | 239M | oldpos = pw->ptr; |
845 | 239M | status = (*curr->procs.process) (curr->state, pr, pw, eof); |
846 | 239M | if (pr->limit != NULL) |
847 | 133M | pr->limit += left; |
848 | 239M | if_debug5m('s', s->memory, "[s]after read "PRI_INTPTR", nr=%u, nw=%u, status=%d, position=%"PRId64"\n", |
849 | 239M | (intptr_t) curr, (uint) (pr->limit - pr->ptr), |
850 | 239M | (uint) (pw->limit - pw->ptr), status, s->position); |
851 | 239M | if (strm == 0 || status != 0) |
852 | 222M | break; |
853 | 16.8M | if (strm->end_status < 0) { |
854 | 4.76M | if (strm->end_status != EOFC || pw->ptr == oldpos) |
855 | 4.64M | status = strm->end_status; |
856 | 4.76M | break; |
857 | 4.76M | } |
858 | 16.8M | MOVE_AHEAD(curr, prev); |
859 | 12.1M | stream_compact(curr, false); |
860 | 12.1M | } |
861 | | /* If curr reached EOD and is a filter or file stream, close it |
862 | | * if it is the last filter in the pipeline. Closing the last filter |
863 | | * seems to contradict PLRM3 but matches Adobe interpreters. |
864 | | */ |
865 | 227M | if ((strm != 0 || curr->file) && status == EOFC && |
866 | 227M | curr->cursor.r.ptr >= curr->cursor.r.limit && |
867 | 227M | curr->close_at_eod && |
868 | 227M | prev == 0 |
869 | 227M | ) { |
870 | 2.14M | int cstat = sclose(curr); |
871 | | |
872 | 2.14M | if (cstat != 0) |
873 | 0 | status = cstat; |
874 | 2.14M | } |
875 | | /* Unwind from the recursion. */ |
876 | 227M | curr->end_status = (status >= 0 ? 0 : status); |
877 | 227M | if (prev == 0) |
878 | 214M | return status; |
879 | 227M | MOVE_BACK(curr, prev); |
880 | 12.1M | } |
881 | 214M | } |
882 | | |
883 | | /* Write to a pipeline. */ |
884 | | static int |
885 | | swritebuf(stream * s, stream_cursor_read * pbuf, bool last) |
886 | 209M | { |
887 | 209M | stream *prev = 0; |
888 | 209M | stream *curr = s; |
889 | 209M | int depth = 0; /* # of non-temp streams before curr */ |
890 | 209M | int status; |
891 | | |
892 | | /* |
893 | | * The handling of EOFC is a little tricky. There are two |
894 | | * invariants that keep it straight: |
895 | | * - We only pass last = true to a stream if either it is |
896 | | * the first stream in the pipeline, or it is a temporary stream |
897 | | * below the first stream and the stream immediately above it has |
898 | | * end_status = EOFC. |
899 | | */ |
900 | 276M | for (;;) { |
901 | 342M | for (;;) { |
902 | | /* Move ahead in the pipeline. */ |
903 | 342M | stream *strm = curr->strm; |
904 | 342M | stream_cursor_write cw; |
905 | 342M | stream_cursor_read *pr; |
906 | 342M | stream_cursor_write *pw; |
907 | | |
908 | | /* |
909 | | * We only want to set the last/end flag for |
910 | | * the top-level stream and any temporary streams |
911 | | * immediately below it. |
912 | | */ |
913 | 342M | bool end = last && |
914 | 342M | (prev == 0 || |
915 | 2.67M | (depth <= 1 && prev->end_status == EOFC)); |
916 | | |
917 | 342M | if (strm == 0) |
918 | 81.9M | cw.ptr = 0, cw.limit = 0, pw = &cw; |
919 | 260M | else |
920 | 260M | pw = &strm->cursor.w; |
921 | 342M | if (prev == 0) |
922 | 245M | pr = pbuf; |
923 | 97.1M | else |
924 | 97.1M | pr = &curr->cursor.r; |
925 | 342M | if_debug5m('s', s->memory, |
926 | 342M | "[s]write process "PRI_INTPTR"(%s), nr=%u, nw=%u, end=%d\n", |
927 | 342M | (intptr_t)curr, |
928 | 342M | gs_struct_type_name(curr->state->templat->stype), |
929 | 342M | (uint)(pr->limit - pr->ptr), |
930 | 342M | (uint)(pw->limit - pw->ptr), end); |
931 | 342M | status = curr->end_status; |
932 | 342M | if (status >= 0) { |
933 | 342M | status = (*curr->procs.process)(curr->state, pr, pw, end); |
934 | 342M | if_debug5m('s', s->memory, |
935 | 342M | "[s]after write "PRI_INTPTR", nr=%u, nw=%u, end=%d, status=%d\n", |
936 | 342M | (intptr_t) curr, (uint) (pr->limit - pr->ptr), |
937 | 342M | (uint) (pw->limit - pw->ptr), end, status); |
938 | 342M | if (status == 0 && end) |
939 | 962k | status = EOFC; |
940 | 342M | if (status == EOFC || status == ERRC) |
941 | 1.06M | curr->end_status = status; |
942 | 342M | } |
943 | 342M | if (strm == 0 || (status < 0 && status != EOFC)) |
944 | 81.9M | break; |
945 | 260M | if (status != 1) { |
946 | | /* |
947 | | * Keep going if we are closing a filter with a sub-stream. |
948 | | * We know status == 0 or EOFC. |
949 | | */ |
950 | 194M | if (!end || !strm->is_temp) |
951 | 194M | break; |
952 | 194M | } |
953 | 66.6M | status = strm->end_status; |
954 | 66.6M | if (status < 0 && (status != EOFC || !end)) |
955 | 0 | break; |
956 | 66.6M | if (!curr->is_temp) |
957 | 66.6M | ++depth; |
958 | 66.6M | if_debug1m('s', strm->memory, "[s]moving ahead, depth = %d\n", depth); |
959 | 66.6M | MOVE_AHEAD(curr, prev); |
960 | 66.6M | stream_compact(curr, false); |
961 | 66.6M | } |
962 | | /* Move back in the pipeline. */ |
963 | 276M | curr->end_status = (status >= 0 ? 0 : status); |
964 | 276M | if (status < 0 || prev == 0) { |
965 | | /* |
966 | | * All streams up to here were called with last = true |
967 | | * and returned 0 or EOFC (so their end_status is now EOFC): |
968 | | * finish unwinding and then return. Change the status of |
969 | | * the prior streams to ERRC if the new status is ERRC, |
970 | | * otherwise leave it alone. |
971 | | */ |
972 | 210M | while (prev) { |
973 | 251k | if_debug0m('s', s->memory, "[s]unwinding\n"); |
974 | 251k | MOVE_BACK(curr, prev); |
975 | 251k | if (status >= 0) |
976 | 0 | curr->end_status = 0; |
977 | 251k | else if (status == ERRC) |
978 | 21 | curr->end_status = ERRC; |
979 | 251k | } |
980 | 209M | return status; |
981 | 209M | } |
982 | 276M | MOVE_BACK(curr, prev); |
983 | 66.3M | if (!curr->is_temp) |
984 | 66.3M | --depth; |
985 | 66.3M | if_debug1m('s', s->memory, "[s]moving back, depth = %d\n", depth); |
986 | 66.3M | } |
987 | 209M | } |
988 | | |
989 | | /* Move as much data as possible from one buffer to another. */ |
990 | | /* Return 0 if the input became empty, 1 if the output became full. */ |
991 | | int |
992 | | stream_move(stream_cursor_read * pr, stream_cursor_write * pw) |
993 | 262M | { |
994 | 262M | uint rcount = pr->limit - pr->ptr; |
995 | 262M | uint wcount = pw->limit - pw->ptr; |
996 | 262M | uint count; |
997 | 262M | int status; |
998 | | |
999 | 262M | if (rcount <= wcount) |
1000 | 138M | count = rcount, status = 0; |
1001 | 123M | else |
1002 | 123M | count = wcount, status = 1; |
1003 | 262M | memmove(pw->ptr + 1, pr->ptr + 1, count); |
1004 | 262M | pr->ptr += count; |
1005 | 262M | pw->ptr += count; |
1006 | 262M | return status; |
1007 | 262M | } |
1008 | | |
1009 | | /* If possible, compact the information in a stream buffer to the bottom. */ |
1010 | | static void |
1011 | | stream_compact(stream * s, bool always) |
1012 | 513M | { |
1013 | 513M | if (s->cbuf != NULL && s->cursor.r.ptr >= s->cbuf |
1014 | 513M | && (always || s->end_status >= 0)) { |
1015 | 425M | uint dist = s->cursor.r.ptr + 1 - s->cbuf; |
1016 | | |
1017 | 425M | memmove(s->cbuf, s->cursor.r.ptr + 1, |
1018 | 425M | (uint) (s->cursor.r.limit - s->cursor.r.ptr)); |
1019 | 425M | s->cursor.r.ptr = s->cbuf - 1; |
1020 | 425M | s->cursor.r.limit -= dist; /* same as w.ptr */ |
1021 | 425M | s->position += dist; |
1022 | 425M | } |
1023 | 513M | } |
1024 | | |
1025 | | /* ------ String streams ------ */ |
1026 | | |
1027 | | /* String stream procedures */ |
1028 | | static int |
1029 | | s_string_available(stream *, gs_offset_t *), |
1030 | | s_string_read_seek(stream *, gs_offset_t), |
1031 | | s_string_write_seek(stream *, gs_offset_t), |
1032 | | s_string_read_process(stream_state *, stream_cursor_read *, |
1033 | | stream_cursor_write *, bool), |
1034 | | s_string_write_process(stream_state *, stream_cursor_read *, |
1035 | | stream_cursor_write *, bool); |
1036 | | |
1037 | | /* Initialize a stream for reading a string. */ |
1038 | | /* String ownership retained by the caller, for example |
1039 | | Postscript string objects owned by the Postscript |
1040 | | interpreter |
1041 | | */ |
1042 | | void |
1043 | | sread_string(register stream *s, const byte *ptr, uint len) |
1044 | 20.5M | { |
1045 | 20.5M | static const stream_procs p = { |
1046 | 20.5M | s_string_available, s_string_read_seek, s_std_read_reset, |
1047 | 20.5M | s_std_read_flush, s_std_null, s_string_read_process |
1048 | 20.5M | }; |
1049 | | |
1050 | 20.5M | s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek); |
1051 | 20.5M | s->cbuf_string.data = (byte *)ptr; |
1052 | 20.5M | s->cbuf_string.size = len; |
1053 | 20.5M | s->cbuf_string_memory = NULL; |
1054 | 20.5M | s->end_status = EOFC; |
1055 | 20.5M | s->cursor.r.limit = s->cursor.w.limit; |
1056 | 20.5M | } |
1057 | | |
1058 | | /* The string ownership is transferred from caller to stream. |
1059 | | string_mem pointer must be allocator used to allocate the |
1060 | | "string" buffer. |
1061 | | */ |
1062 | | void |
1063 | | sread_transient_string(register stream *s, gs_memory_t *string_mem, const byte *ptr, uint len) |
1064 | 88.4k | { |
1065 | 88.4k | static const stream_procs p = { |
1066 | 88.4k | s_string_available, s_string_read_seek, s_std_read_reset, |
1067 | 88.4k | s_std_read_flush, s_std_null, s_string_read_process |
1068 | 88.4k | }; |
1069 | | |
1070 | 88.4k | s_std_init(s, (byte *)ptr, len, &p, s_mode_read + s_mode_seek); |
1071 | 88.4k | s->cbuf_string.data = (byte *)ptr; |
1072 | 88.4k | s->cbuf_string.size = len; |
1073 | 88.4k | s->cbuf_string_memory = string_mem; |
1074 | 88.4k | s->end_status = EOFC; |
1075 | 88.4k | s->cursor.r.limit = s->cursor.w.limit; |
1076 | 88.4k | } |
1077 | | |
1078 | | /* Initialize a reusable stream for reading a string. */ |
1079 | | static void |
1080 | | s_string_reusable_reset(stream *s) |
1081 | 0 | { |
1082 | 0 | s->cursor.r.ptr = s->cbuf - 1; /* just reset to the beginning */ |
1083 | 0 | s->cursor.r.limit = s->cursor.r.ptr + s->bsize; /* might have gotten reset */ |
1084 | 0 | } |
1085 | | static int |
1086 | | s_string_reusable_flush(stream *s) |
1087 | 0 | { |
1088 | 0 | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf + s->bsize - 1; /* just set to the end */ |
1089 | 0 | return 0; |
1090 | 0 | } |
1091 | | |
1092 | | /* String ownership retained by the caller, for example |
1093 | | Postscript string objects owned by the Postscript |
1094 | | interpreter |
1095 | | */ |
1096 | | void |
1097 | | sread_string_reusable(stream *s, const byte *ptr, uint len) |
1098 | 65 | { |
1099 | | /* |
1100 | | * Note that s->procs.close is s_close_disable, to parallel |
1101 | | * file_close_disable. |
1102 | | */ |
1103 | 65 | static const stream_procs p = { |
1104 | 65 | s_string_available, s_string_read_seek, s_string_reusable_reset, |
1105 | 65 | s_string_reusable_flush, s_close_disable, s_string_read_process |
1106 | 65 | }; |
1107 | | |
1108 | 65 | sread_string(s, ptr, len); |
1109 | 65 | s->procs = p; |
1110 | 65 | s->close_at_eod = false; |
1111 | 65 | } |
1112 | | |
1113 | | /* The string ownership is transferred from caller to stream. |
1114 | | string_mem pointer must be allocator used to allocate the |
1115 | | "string" buffer. |
1116 | | */ |
1117 | | void |
1118 | | sread_transient_string_reusable(stream *s, gs_memory_t *string_mem, const byte *ptr, uint len) |
1119 | 0 | { |
1120 | | /* |
1121 | | * Note that s->procs.close is s_close_disable, to parallel |
1122 | | * file_close_disable. |
1123 | | */ |
1124 | 0 | static const stream_procs p = { |
1125 | 0 | s_string_available, s_string_read_seek, s_string_reusable_reset, |
1126 | 0 | s_string_reusable_flush, s_close_disable, s_string_read_process |
1127 | 0 | }; |
1128 | |
|
1129 | 0 | sread_transient_string(s, string_mem, ptr, len); |
1130 | 0 | s->procs = p; |
1131 | 0 | s->close_at_eod = false; |
1132 | 0 | } |
1133 | | |
1134 | | /* Return the number of available bytes when reading from a string. */ |
1135 | | static int |
1136 | | s_string_available(stream *s, gs_offset_t *pl) |
1137 | 30.2k | { |
1138 | 30.2k | *pl = sbufavailable(s); |
1139 | 30.2k | if (*pl == 0 && s->close_at_eod) /* EOF */ |
1140 | 0 | *pl = -1; |
1141 | 30.2k | return 0; |
1142 | 30.2k | } |
1143 | | |
1144 | | /* Seek in a string being read. Return 0 if OK, ERRC if not. */ |
1145 | | static int |
1146 | | s_string_read_seek(register stream * s, gs_offset_t pos) |
1147 | 148k | { |
1148 | 148k | if (pos < 0 || pos > s->bsize) |
1149 | 65 | return ERRC; |
1150 | 148k | s->cursor.r.ptr = s->cbuf + pos - 1; |
1151 | | /* We might be seeking after a reusable string reached EOF. */ |
1152 | 148k | s->cursor.r.limit = s->cbuf + s->bsize - 1; |
1153 | | /* |
1154 | | * When the file reaches EOF, |
1155 | | * stream_compact sets s->position to its end. |
1156 | | * Reset it now to allow stell to work properly |
1157 | | * after calls to this function. |
1158 | | * Note that if the riched EOF and this fuction |
1159 | | * was not called, stell still returns a wrong value. |
1160 | | */ |
1161 | 148k | s->position = 0; |
1162 | 148k | return 0; |
1163 | 148k | } |
1164 | | |
1165 | | /* Initialize a stream for writing a string. */ |
1166 | | void |
1167 | | swrite_string(register stream * s, byte * ptr, uint len) |
1168 | 13.5M | { |
1169 | 13.5M | static const stream_procs p = { |
1170 | 13.5M | s_std_noavailable, s_string_write_seek, s_std_write_reset, |
1171 | 13.5M | s_std_null, s_std_null, s_string_write_process |
1172 | 13.5M | }; |
1173 | | |
1174 | 13.5M | s_std_init(s, ptr, len, &p, s_mode_write + s_mode_seek); |
1175 | 13.5M | s->cbuf_string.data = ptr; |
1176 | 13.5M | s->cbuf_string.size = len; |
1177 | 13.5M | } |
1178 | | |
1179 | | /* Seek in a string being written. Return 0 if OK, ERRC if not. */ |
1180 | | static int |
1181 | | s_string_write_seek(register stream * s, gs_offset_t pos) |
1182 | 0 | { |
1183 | 0 | if (pos < 0 || pos > s->bsize) |
1184 | 0 | return ERRC; |
1185 | 0 | s->cursor.w.ptr = s->cbuf + pos - 1; |
1186 | 0 | return 0; |
1187 | 0 | } |
1188 | | |
1189 | | /* Since we initialize the input buffer of a string read stream */ |
1190 | | /* to contain all of the data in the string, if we are ever asked */ |
1191 | | /* to refill the buffer, we should signal EOF. */ |
1192 | | static int |
1193 | | s_string_read_process(stream_state * st, stream_cursor_read * ignore_pr, |
1194 | | stream_cursor_write * pw, bool last) |
1195 | 0 | { |
1196 | 0 | return EOFC; |
1197 | 0 | } |
1198 | | /* Similarly, if we are ever asked to empty the buffer, it means that */ |
1199 | | /* there has been an overrun (unless we are closing the stream). */ |
1200 | | static int |
1201 | | s_string_write_process(stream_state * st, stream_cursor_read * pr, |
1202 | | stream_cursor_write * ignore_pw, bool last) |
1203 | 21 | { |
1204 | 21 | return (last ? EOFC : ERRC); |
1205 | 21 | } |
1206 | | |
1207 | | /* ------ Position-tracking stream ------ */ |
1208 | | |
1209 | | static int |
1210 | | s_write_position_process(stream_state *, stream_cursor_read *, |
1211 | | stream_cursor_write *, bool); |
1212 | | |
1213 | | /* Set up a write stream that just keeps track of the position. */ |
1214 | | void |
1215 | | swrite_position_only(stream *s) |
1216 | 6.50M | { |
1217 | 6.50M | static byte discard_buf[50]; /* size is arbitrary */ |
1218 | | |
1219 | 6.50M | swrite_string(s, discard_buf, sizeof(discard_buf)); |
1220 | 6.50M | s->procs.process = s_write_position_process; |
1221 | 6.50M | } |
1222 | | |
1223 | | static int |
1224 | | s_write_position_process(stream_state * st, stream_cursor_read * pr, |
1225 | | stream_cursor_write * ignore_pw, bool last) |
1226 | 13.7M | { |
1227 | 13.7M | pr->ptr = pr->limit; /* discard data */ |
1228 | 13.7M | return 0; |
1229 | 13.7M | } |
1230 | | |
1231 | | /* ------ Filter pipelines ------ */ |
1232 | | |
1233 | | /* |
1234 | | * Add a filter to an output pipeline. The client must have allocated the |
1235 | | * stream state, if any, using the given allocator. For s_init_filter, the |
1236 | | * client must have called s_init and s_init_state. |
1237 | | */ |
1238 | | int |
1239 | | s_init_filter(stream *fs, stream_state *fss, byte *buf, uint bsize, |
1240 | | stream *target) |
1241 | 923k | { |
1242 | 923k | const stream_template *templat = fss->templat; |
1243 | | |
1244 | 923k | if (bsize < templat->min_in_size) |
1245 | 0 | return ERRC; |
1246 | 923k | s_std_init(fs, buf, bsize, &s_filter_write_procs, s_mode_write); |
1247 | 923k | fs->procs.process = templat->process; |
1248 | 923k | fs->state = fss; |
1249 | 923k | if (templat->init) { |
1250 | 692k | fs->end_status = (templat->init)(fss); |
1251 | 692k | if (fs->end_status < 0) |
1252 | 0 | return fs->end_status; |
1253 | 692k | } |
1254 | 923k | fs->strm = target; |
1255 | 923k | return 0; |
1256 | 923k | } |
1257 | | stream * |
1258 | | s_add_filter(stream **ps, const stream_template *templat, |
1259 | | stream_state *ss, gs_memory_t *mem) |
1260 | 575k | { |
1261 | 575k | stream *es; |
1262 | 575k | stream_state *ess; |
1263 | 575k | uint bsize = max(templat->min_in_size, 256); /* arbitrary */ |
1264 | 575k | byte *buf; |
1265 | | |
1266 | | /* |
1267 | | * Ensure enough buffering. This may require adding an additional |
1268 | | * stream. |
1269 | | */ |
1270 | 575k | if (bsize > (*ps)->bsize && templat->process != s_NullE_template.process) { |
1271 | 216k | stream_template null_template; |
1272 | | |
1273 | 216k | null_template = s_NullE_template; |
1274 | 216k | null_template.min_in_size = bsize; |
1275 | 216k | if (s_add_filter(ps, &null_template, NULL, mem) == 0) |
1276 | 0 | return 0; |
1277 | 216k | } |
1278 | 575k | es = s_alloc(mem, "s_add_filter(stream)"); |
1279 | 575k | buf = gs_alloc_bytes(mem, bsize, "s_add_filter(buf)"); |
1280 | 575k | if (es == 0 || buf == 0) { |
1281 | 0 | gs_free_object(mem, buf, "s_add_filter(buf)"); |
1282 | 0 | gs_free_object(mem, es, "s_add_filter(stream)"); |
1283 | 0 | return 0; |
1284 | 0 | } |
1285 | 575k | ess = (ss == 0 ? (stream_state *)es : ss); |
1286 | 575k | ess->templat = templat; |
1287 | 575k | ess->memory = mem; |
1288 | 575k | es->memory = mem; |
1289 | 575k | if (s_init_filter(es, ess, buf, bsize, *ps) < 0) { |
1290 | 0 | gs_free_object(mem, buf, "s_add_filter(buf)"); |
1291 | 0 | gs_free_object(mem, es, "s_add_filter(stream)"); |
1292 | 0 | return 0; |
1293 | 0 | } |
1294 | 575k | *ps = es; |
1295 | 575k | return es; |
1296 | 575k | } |
1297 | | |
1298 | | /* |
1299 | | * Close the filters in a pipeline, up to a given target stream, freeing |
1300 | | * their buffers and state structures. |
1301 | | */ |
1302 | | int |
1303 | | s_close_filters(stream **ps, stream *target) |
1304 | 1.01M | { |
1305 | 1.01M | int code = 0; |
1306 | 2.38M | while (*ps != target) { |
1307 | 1.36M | stream *s = *ps; |
1308 | 1.36M | gs_memory_t *mem = s->state->memory; |
1309 | 1.36M | gs_memory_t *cbuf_string_memory = s->cbuf_string_memory; |
1310 | 1.36M | byte *sbuf = s->cbuf; |
1311 | 1.36M | byte *cbuf = s->cbuf_string.data; |
1312 | 1.36M | stream *next = s->strm; |
1313 | 1.36M | int status = sclose(s); |
1314 | 1.36M | stream_state *ss = s->state; /* sclose may set this to s */ |
1315 | | |
1316 | 1.36M | if (code == 0) |
1317 | 1.36M | code = status; |
1318 | | |
1319 | 1.36M | if (s->cbuf_string_memory != NULL) { /* stream owns string buffer, so free it */ |
1320 | 50.4k | gs_free_object(cbuf_string_memory, cbuf, "s_close_filters(cbuf)"); |
1321 | 50.4k | } |
1322 | | |
1323 | 1.36M | if (mem) { |
1324 | 1.34M | if (sbuf != cbuf) |
1325 | 1.29M | gs_free_object(mem, sbuf, "s_close_filters(buf)"); |
1326 | 1.34M | gs_free_object(mem, s, "s_close_filters(stream)"); |
1327 | 1.34M | if (ss != (stream_state *)s) |
1328 | 352 | gs_free_object(mem, ss, "s_close_filters(state)"); |
1329 | 1.34M | } |
1330 | 1.36M | *ps = next; |
1331 | 1.36M | } |
1332 | 1.01M | return code; |
1333 | 1.01M | } |
1334 | | |
1335 | | /* ------ Stream closing ------ */ |
1336 | | |
1337 | | /* |
1338 | | * Finish closing a file stream. This used to check whether it was |
1339 | | * currentfile, but we don't have to do this any longer. This replaces the |
1340 | | * close procedure for the std* streams, which cannot actually be closed. |
1341 | | * |
1342 | | * This is exported for ziodev.c. */ |
1343 | | int |
1344 | | file_close_finish(stream * s) |
1345 | 6.56M | { |
1346 | 6.56M | return 0; |
1347 | 6.56M | } |
1348 | | |
1349 | | /* |
1350 | | * Close a file stream, but don't deallocate the buffer. This replaces the |
1351 | | * close procedure for %lineedit and %statementedit. (This is WRONG: these |
1352 | | * streams should allocate a new buffer each time they are opened, but that |
1353 | | * would overstress the allocator right now.) This is exported for ziodev.c. |
1354 | | * This also replaces the close procedure for the string-reading streams |
1355 | | * created for gs_run_string and for reusable streams. |
1356 | | */ |
1357 | | int |
1358 | | s_close_disable(stream *s) |
1359 | 6.56M | { |
1360 | | /* Increment the IDs to prevent further access. */ |
1361 | 6.56M | s->read_id = s->write_id = (s->read_id | s->write_id) + 1; |
1362 | 6.56M | return 0; |
1363 | 6.56M | } |
1364 | | int |
1365 | | file_close_disable(stream * s) |
1366 | 6.74M | { |
1367 | 6.74M | int code; |
1368 | | |
1369 | 6.74M | if ((*s->save_close != NULL) && ((code = (*s->save_close)(s)) != 0)) |
1370 | 184k | return code; |
1371 | 6.56M | s_close_disable(s); |
1372 | 6.56M | return file_close_finish(s); |
1373 | 6.74M | } |
1374 | | |
1375 | | /* ------ NullEncode/Decode ------ */ |
1376 | | |
1377 | | /* Process a buffer */ |
1378 | | static int |
1379 | | s_Null_process(stream_state * st, stream_cursor_read * pr, |
1380 | | stream_cursor_write * pw, bool last) |
1381 | 32.8M | { |
1382 | 32.8M | return stream_move(pr, pw); |
1383 | 32.8M | } |
1384 | | |
1385 | | /* Stream template */ |
1386 | | const stream_template s_NullE_template = { |
1387 | | &st_stream_state, NULL, s_Null_process, 1, 1 |
1388 | | }; |
1389 | | const stream_template s_NullD_template = { |
1390 | | &st_stream_state, NULL, s_Null_process, 1, 1 |
1391 | | }; |