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