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