Coverage Report

Created: 2026-09-14 07:34

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