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