xref: /plan9/sys/src/cmd/gs/src/stream.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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