Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/zfrsd.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2023 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
/* ReusableStreamDecode filter support */
18
#include "memory_.h"
19
#include "ghost.h"
20
#include "gsfname.h"    /* for gs_parse_file_name */
21
#include "gxiodev.h"
22
#include "oper.h"
23
#include "stream.h"
24
#include "strimpl.h"
25
#include "sfilter.h"    /* for SubFileDecode */
26
#include "files.h"
27
#include "idict.h"
28
#include "idparam.h"
29
#include "iname.h"
30
#include "istruct.h"
31
#include "store.h"
32
#include "zfile.h"
33
#include "zfrsd.h"
34
35
/* ---------------- Reusable streams ---------------- */
36
37
/*
38
 * The actual work of constructing the filter is done in PostScript code.
39
 * The operators in this file are internal ones that handle the dirty work.
40
 */
41
42
/* <dict|null> .rsdparams <filters> <decodeparms|null> */
43
/* filters is always an array; decodeparms is always either an array */
44
/* of the same length as filters, or null. */
45
static int
46
zrsdparams(i_ctx_t *i_ctx_p)
47
248
{
48
248
    os_ptr op = osp;
49
248
    ref *pFilter;
50
248
    ref *pDecodeParms;
51
248
    int Intent = 0;
52
248
    bool AsyncRead = false;
53
248
    ref empty_array, filter1_array, parms1_array;
54
248
    uint i;
55
248
    int code = 0;
56
57
248
    check_op(1);
58
248
    if (!r_has_type(op, t_dictionary) && !r_has_type(op, t_null)) {
59
0
        return_error(gs_error_typecheck);
60
0
    }
61
62
248
    make_empty_array(&empty_array, a_readonly);
63
248
    if (r_has_type(op, t_dictionary)
64
248
        && dict_find_string(op, "Filter", &pFilter) > 0) {
65
0
        if (!r_is_array(pFilter)) {
66
0
            if (!r_has_type(pFilter, t_name))
67
0
                return_error(gs_error_typecheck);
68
0
            make_array(&filter1_array, a_readonly, 1, pFilter);
69
0
            pFilter = &filter1_array;
70
0
        }
71
0
    } else
72
248
        pFilter = &empty_array;
73
    /* If Filter is undefined, ignore DecodeParms. */
74
248
    if (pFilter != &empty_array &&
75
248
        dict_find_string(op, "DecodeParms", &pDecodeParms) > 0
76
248
        ) {
77
0
        if (pFilter == &filter1_array) {
78
0
            make_array(&parms1_array, a_readonly, 1, pDecodeParms);
79
0
            pDecodeParms = &parms1_array;
80
0
        } else if (!r_is_array(pDecodeParms))
81
0
            return_error(gs_error_typecheck);
82
0
        else if (r_size(pFilter) != r_size(pDecodeParms))
83
0
            return_error(gs_error_rangecheck);
84
0
    } else
85
248
        pDecodeParms = 0;
86
248
    for (i = 0; i < r_size(pFilter); ++i) {
87
0
        ref f, fname, dp;
88
89
0
        array_get(imemory, pFilter, (long)i, &f);
90
0
        if (!r_has_type(&f, t_name))
91
0
            return_error(gs_error_typecheck);
92
0
        name_string_ref(imemory, &f, &fname);
93
0
        if (r_size(&fname) < 6 ||
94
0
            memcmp(fname.value.bytes + r_size(&fname) - 6, "Decode", 6)
95
0
            )
96
0
            return_error(gs_error_rangecheck);
97
0
        if (pDecodeParms) {
98
0
            array_get(imemory, pDecodeParms, (long)i, &dp);
99
0
            if (!(r_has_type(&dp, t_dictionary) || r_has_type(&dp, t_null)))
100
0
                return_error(gs_error_typecheck);
101
0
        }
102
0
    }
103
248
    if (r_has_type(op, t_dictionary))
104
248
        code = dict_int_param(op, "Intent", 0, 3, 0, &Intent);
105
248
    if (code < 0 && code != gs_error_rangecheck) /* out-of-range int is ok, use 0 */
106
0
        return code;
107
248
    if (r_has_type(op, t_dictionary))
108
248
        if ((code = dict_bool_param(op, "AsyncRead", false, &AsyncRead)) < 0)
109
0
            return code;
110
248
    push(1);
111
248
    op[-1] = *pFilter;
112
248
    if (pDecodeParms)
113
0
        *op = *pDecodeParms;
114
248
    else
115
248
        make_null(op);
116
248
    return 0;
117
248
}
118
119
/* <file|string> <CloseSource> .reusablestream <filter> */
120
/*
121
 * The file|string operand must be a "reusable source", either:
122
 *      - A string or bytestring;
123
 *      - An array of strings;
124
 *      - A readable, positionable file stream;
125
 *      - A readable string stream;
126
 *      - A SubFileDecode filter with an empty EODString and a reusable
127
 *      source.
128
 * Reusable streams are also reusable sources, but they look just like
129
 * ordinary file or string streams.
130
 */
131
static int make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs,
132
                     long offset, long length);
133
134
static int make_aos(i_ctx_t *i_ctx_p, os_ptr op,
135
                    int blk_sz, int blk_sz_last, unsigned int file_sz);
136
137
static int
138
zreusablestream(i_ctx_t *i_ctx_p)
139
248
{
140
248
    os_ptr op = osp;
141
248
    os_ptr source_op = op - 1;
142
248
    long length = max_long;
143
248
    bool close_source;
144
248
    int code;
145
146
248
    check_op(2);
147
248
    check_type(*op, t_boolean);
148
248
    close_source = op->value.boolval;
149
248
    if (r_has_type(source_op, t_string)) {
150
0
        uint size = r_size(source_op);
151
152
0
        check_read(*source_op);
153
0
        code = make_rss(i_ctx_p, source_op, source_op->value.const_bytes,
154
0
                        size, r_space(source_op), 0L, size, false);
155
248
    } else if (r_has_type(source_op, t_astruct)) {
156
0
        uint size = gs_object_size(imemory, source_op->value.pstruct);
157
158
0
        if (gs_object_type(imemory, source_op->value.pstruct) != &st_bytes)
159
0
            return_error(gs_error_rangecheck);
160
0
        check_read(*source_op);
161
0
        code = make_rss(i_ctx_p, source_op,
162
0
                        (const byte *)source_op->value.pstruct, size,
163
0
                        r_space(source_op), 0L, size, true);
164
248
    } else if (r_has_type(source_op, t_array)) {  /* no packedarrays */
165
248
        int i, blk_cnt, blk_sz;
166
248
        ref *blk_ref;
167
248
        ulong filelen = 0;
168
169
248
        check_read(*source_op);
170
248
        blk_cnt = r_size(source_op);
171
248
        blk_ref = source_op->value.refs;
172
248
        if (blk_cnt > 0) {
173
248
            blk_sz = r_size(blk_ref);
174
496
            for (i = 0; i < blk_cnt; i++) {
175
248
                int len;
176
177
248
                check_read_type(blk_ref[i], t_string);
178
248
                len = r_size(&blk_ref[i]);
179
248
                if (len > blk_sz || (len < blk_sz && i < blk_cnt - 1))
180
0
                   return_error(gs_error_rangecheck); /* last block can be smaller */
181
248
                filelen += len;
182
248
            }
183
248
        }
184
248
        if (filelen == 0) {
185
65
           code = make_rss(i_ctx_p, source_op, (unsigned char *)"", 0,
186
65
               r_space(source_op), 0, 0, false);
187
183
        } else {
188
183
           code = make_aos(i_ctx_p, source_op, blk_sz, r_size(&blk_ref[blk_cnt - 1]), filelen);
189
183
        }
190
248
    } else {
191
0
        long offset = 0;
192
0
        stream *source;
193
0
        stream *s;
194
195
0
        check_read_file(i_ctx_p, source, source_op);
196
0
        s = source;
197
0
rs:
198
0
        if (s->cbuf_string.data != 0) { /* string stream */
199
0
            long pos = stell(s);
200
0
            long avail = sbufavailable(s) + pos;
201
202
0
            offset += pos;
203
0
            code = make_rss(i_ctx_p, source_op, s->cbuf_string.data,
204
0
                            s->cbuf_string.size,
205
0
                            imemory_space((const gs_ref_memory_t *)s->memory),
206
0
                            offset, min(avail, length), false);
207
0
        } else if (s->file != 0) { /* file stream */
208
0
            if (~s->modes & (s_mode_read | s_mode_seek))
209
0
                return_error(gs_error_ioerror);
210
0
            code = make_rfs(i_ctx_p, source_op, s, offset + stell(s), length);
211
0
        } else if (s->state->templat == &s_SFD_template) {
212
            /* SubFileDecode filter */
213
0
            const stream_SFD_state *const sfd_state =
214
0
                (const stream_SFD_state *)s->state;
215
216
0
            if (sfd_state->eod.size != 0)
217
0
                return_error(gs_error_rangecheck);
218
0
            offset += sfd_state->skip_count - sbufavailable(s);
219
0
            if (sfd_state->count != 0) {
220
0
                long left = max(sfd_state->count, 0) + sbufavailable(s);
221
222
0
                if (left < length)
223
0
                    length = left;
224
0
            }
225
0
            s = s->strm;
226
0
            goto rs;
227
0
        }
228
0
        else      /* some other kind of stream */
229
0
            return_error(gs_error_rangecheck);
230
0
        if (close_source) {
231
0
            stream *rs = fptr(source_op);
232
233
0
            rs->strm = source;  /* only for close_source */
234
0
            rs->close_strm = true;
235
0
        }
236
0
    }
237
248
    if (code >= 0)
238
248
        pop(1);
239
248
    return code;
240
248
}
241
242
/* Make a reusable string stream. */
243
int
244
make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data, uint size,
245
         uint string_space, long offset, long length, bool is_bytestring)
246
65
{
247
65
    uint save_space = icurrent_space;
248
65
    stream *s;
249
65
    long left = min(length, size - offset);
250
251
65
    ialloc_set_space(idmemory, string_space);
252
65
    s = file_alloc_stream(imemory, "make_rss");
253
65
    ialloc_set_space(idmemory, save_space);
254
65
    if (s == 0)
255
0
        return_error(gs_error_VMerror);
256
65
    sread_string_reusable(s, data + offset, max(left, 0));
257
65
    if (is_bytestring)
258
0
        s->cbuf_string.data = 0; /* byte array, not string */
259
65
    make_stream_file(op, s, "r");
260
65
    return 0;
261
65
}
262
263
/* Make a reusable file stream. */
264
static int
265
make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs, long offset, long length)
266
0
{
267
0
    uint save_space = icurrent_space;
268
0
    uint stream_space = imemory_space((const gs_ref_memory_t *)fs->memory);
269
0
    gs_const_string fname;
270
0
    gs_parsed_file_name_t pname;
271
0
    stream *s;
272
0
    int code;
273
274
0
    if (sfilename(fs, &fname) < 0)
275
0
        return_error(gs_error_ioerror);
276
0
    code = gs_parse_file_name(&pname, (const char *)fname.data, fname.size,
277
0
                              imemory);
278
0
    if (code < 0)
279
0
        return code;
280
0
    if (pname.len == 0)   /* %stdin% etc. won't have a filename */
281
0
        return_error(gs_error_invalidfileaccess); /* can't reopen */
282
0
    if (pname.iodev == NULL)
283
0
        pname.iodev = iodev_default(imemory);
284
    /* Open the file again, to be independent of the source. */
285
0
    ialloc_set_space(idmemory, stream_space);
286
0
    code = zopen_file(i_ctx_p, &pname, "r", &s, imemory);
287
0
    ialloc_set_space(idmemory, save_space);
288
0
    if (code < 0)
289
0
        return code;
290
0
    if (sread_subfile(s, offset, length) < 0) {
291
0
        sclose(s);
292
0
        return_error(gs_error_ioerror);
293
0
    }
294
0
    s->close_at_eod = false;
295
0
    make_stream_file(op, s, "r");
296
0
    return 0;
297
0
}
298
/* ----------- Reusable array-of-strings stream ------------- */
299
300
static int  s_aos_available(stream *, gs_offset_t *);
301
static int  s_aos_seek(stream *, gs_offset_t);
302
static void s_aos_reset(stream *s);
303
static int  s_aos_flush(stream *s);
304
static int  s_aos_close(stream *);
305
static int  s_aos_process(stream_state *, stream_cursor_read *,
306
                        stream_cursor_write *, bool);
307
308
/* Stream state */
309
typedef struct aos_state_s {
310
    stream_state_common;
311
    ref   blocks;
312
    stream *s;
313
    int   blk_sz;
314
    int   blk_sz_last;
315
    uint  file_sz;
316
} aos_state_t;
317
318
/* GC procedures */
319
static
320
CLEAR_MARKS_PROC(aos_clear_marks)
321
151
{   aos_state_t *const pptr = vptr;
322
323
151
    r_clear_attrs(&pptr->blocks, l_mark);
324
151
}
325
static
326
27
ENUM_PTRS_WITH(aos_enum_ptrs, aos_state_t *pptr) return 0;
327
9
ENUM_PTR(0, aos_state_t, s);
328
9
case 1:
329
9
ENUM_RETURN_REF(&pptr->blocks);
330
27
ENUM_PTRS_END
331
9
static RELOC_PTRS_WITH(aos_reloc_ptrs, aos_state_t *pptr);
332
9
RELOC_PTR(aos_state_t, s);
333
9
RELOC_REF_VAR(pptr->blocks);
334
9
r_clear_attrs(&pptr->blocks, l_mark);
335
9
RELOC_PTRS_END
336
337
gs_private_st_complex_only(st_aos_state, aos_state_t,
338
    "aos_state", aos_clear_marks, aos_enum_ptrs, aos_reloc_ptrs, 0);
339
340
/* Stream template */
341
static const stream_template s_aos_template = {
342
     &st_aos_state, 0, s_aos_process, 1, 1, 0, 0 };
343
344
/* Stream procs */
345
static const stream_procs s_aos_procs = {
346
     s_aos_available, s_aos_seek, s_aos_reset,
347
     s_aos_flush, s_aos_close, s_aos_process,
348
     NULL   /* no s_aos_switch */
349
};
350
351
static int
352
make_aos(i_ctx_t *i_ctx_p, os_ptr op, int blk_sz, int blk_sz_last, uint file_sz)
353
183
{
354
183
    stream *s;
355
183
    aos_state_t *ss;
356
183
    byte *buf;
357
183
    const int aos_buf_size = 1024; /* arbitrary */
358
183
    uint save_space = icurrent_space;
359
183
    ialloc_set_space(idmemory, r_space(op));
360
361
183
    s = s_alloc(imemory, "aos_stream");
362
183
    ss = (aos_state_t *)s_alloc_state(imemory, &st_aos_state, "st_aos_state");
363
183
    buf = gs_alloc_bytes(imemory, aos_buf_size, "aos_stream_buf");
364
183
    if (s == 0 || ss == 0 || buf == 0) {
365
0
        gs_free_object(imemory, buf, "aos_stream_buf");
366
0
        gs_free_object(imemory, ss, "st_aos_state");
367
0
        gs_free_object(imemory, s, "aos_stream");
368
0
        ialloc_set_space(idmemory, save_space);
369
0
        return_error(gs_error_VMerror);
370
0
    }
371
183
    ialloc_set_space(idmemory, save_space);
372
183
    ss->templat = &s_aos_template;
373
183
    ss->blocks = *op;
374
183
    ss->s = s;
375
183
    ss->blk_sz = blk_sz;
376
183
    ss->blk_sz_last = blk_sz_last;
377
183
    ss->file_sz = file_sz;
378
183
    s_std_init(s, buf, aos_buf_size, &s_aos_procs, s_mode_read + s_mode_seek);
379
183
    s->state = (stream_state *)ss;
380
183
    s->file_offset = 0;
381
183
    s->file_limit = S_FILE_LIMIT_MAX;
382
183
    s->close_at_eod = false;
383
183
    s->read_id = 1;
384
183
    make_stream_file(op, s, "r");
385
183
    return 0;
386
183
}
387
388
/* Return the number of available bytes */
389
static int
390
s_aos_available(stream *s, gs_offset_t *pl)
391
41
{
392
41
    *pl = ((aos_state_t *)s->state)->file_sz - stell(s);
393
41
    return 0;
394
41
}
395
396
/* Seek in a string being read.  Return 0 if OK, ERRC if not. */
397
static int
398
s_aos_seek(register stream * s, gs_offset_t pos)
399
489
{
400
489
    uint end = s->cursor.r.limit - s->cbuf + 1;
401
489
    long offset = pos - s->position;
402
403
489
    if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
404
41
        s->cursor.r.ptr = s->cbuf + offset - 1;
405
41
        return 0;
406
41
    }
407
448
    if (pos < 0 || pos > s->file_limit)
408
0
        return ERRC;
409
448
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
410
448
    s->end_status = 0;
411
448
    s->position = pos;
412
448
    return 0;
413
448
}
414
415
static void
416
s_aos_reset(stream *s)
417
0
{
418
    /* PLRM definition of reset operator is strange. */
419
    /* Rewind the file and discard the buffer. */
420
0
    s->position = 0;
421
0
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
422
0
    s->end_status = 0;
423
0
}
424
425
static int
426
s_aos_flush(stream *s)
427
0
{
428
0
    s->position = ((aos_state_t *)s->state)->file_sz;
429
0
    s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1;
430
0
    return 0;
431
0
}
432
433
static int
434
s_aos_close(stream * s)
435
0
{
436
0
    gs_free_object(s->memory, s->cbuf, "s_aos_close(buffer)");
437
0
    s->cbuf = 0;
438
    /* Increment the IDs to prevent further access. */
439
0
    s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
440
0
    return 0;
441
0
}
442
443
static int
444
s_aos_process(stream_state * st, stream_cursor_read * ignore_pr,
445
                      stream_cursor_write * pw, bool last)
446
224
{
447
224
    int blk_i, blk_off, blk_cnt, status = 1;
448
224
    uint count;
449
224
    aos_state_t *ss = (aos_state_t *)st;
450
224
    uint max_count = pw->limit - pw->ptr;
451
224
    uint pos = stell(ss->s);
452
224
    unsigned const char *data;
453
224
    ref *blk_ref;
454
455
224
    pos += sbufavailable(ss->s);
456
224
    if (pos >= ss->file_sz)
457
73
        return EOFC;
458
151
    blk_i   = pos / ss->blk_sz;
459
151
    blk_off = pos % ss->blk_sz;
460
151
    blk_cnt = r_size(&ss->blocks);
461
151
    count = blk_i < blk_cnt - 1 ? ss->blk_sz : ss->blk_sz_last;
462
151
    blk_ref = &ss->blocks.value.refs[blk_i];
463
464
151
    if (!r_has_type_attrs(blk_ref, t_string, a_read) || r_size(blk_ref) != count)
465
0
        return ERRC;
466
467
151
    data = blk_ref->value.bytes;
468
469
151
    if (max_count > count - blk_off) {
470
62
        max_count = count - blk_off;
471
62
        if (blk_i == blk_cnt - 1)
472
62
             status = EOFC;
473
62
    }
474
151
    memcpy(pw->ptr+1, data + blk_off, max_count);
475
151
    pw->ptr += max_count;
476
151
    return status;
477
151
}
478
479
/* ---------------- Initialization procedure ---------------- */
480
481
const op_def zfrsd_op_defs[] =
482
{
483
    {"2.reusablestream", zreusablestream},
484
    {"2.rsdparams", zrsdparams},
485
    op_def_end(0)
486
};