Coverage Report

Created: 2025-06-10 07:24

/src/ghostpdl/psi/zfproc.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
/* Procedure-based filter stream support */
18
#include "memory_.h"
19
#include "stat_.h" /* get system header early to avoid name clash on Cygwin */
20
#include "ghost.h"
21
#include "oper.h"   /* for ifilter.h */
22
#include "estack.h"
23
#include "gsstruct.h"
24
#include "ialloc.h"
25
#include "istruct.h"    /* for RELOC_REF_VAR */
26
#include "stream.h"
27
#include "strimpl.h"
28
#include "ifilter.h"
29
#include "files.h"
30
#include "store.h"
31
32
/* ---------------- Generic ---------------- */
33
34
/* GC procedures */
35
static
36
CLEAR_MARKS_PROC(sproc_clear_marks)
37
44.7k
{
38
44.7k
    stream_proc_state *const pptr = vptr;
39
40
44.7k
    r_clear_attrs(&pptr->proc, l_mark);
41
44.7k
    r_clear_attrs(&pptr->data, l_mark);
42
44.7k
}
43
static
44
2.10k
ENUM_PTRS_WITH(sproc_enum_ptrs, stream_proc_state *pptr) return 0;
45
700
case 0:
46
700
ENUM_RETURN_REF(&pptr->proc);
47
700
case 1:
48
700
ENUM_RETURN_REF(&pptr->data);
49
2.10k
ENUM_PTRS_END
50
700
static RELOC_PTRS_WITH(sproc_reloc_ptrs, stream_proc_state *pptr);
51
700
RELOC_REF_VAR(pptr->proc);
52
700
r_clear_attrs(&pptr->proc, l_mark);
53
700
RELOC_REF_VAR(pptr->data);
54
700
r_clear_attrs(&pptr->data, l_mark);
55
700
RELOC_PTRS_END
56
57
/* Structure type for procedure-based streams. */
58
private_st_stream_proc_state();
59
60
/* Allocate and open a procedure-based filter. */
61
/* The caller must have checked that *sop is a procedure. */
62
static int
63
s_proc_init(ref * sop, stream ** psstrm, uint mode,
64
            const stream_template * temp, const stream_procs * procs,
65
            gs_ref_memory_t *imem)
66
62.9k
{
67
62.9k
    gs_memory_t *const mem = (gs_memory_t *)imem;
68
62.9k
    stream *sstrm = file_alloc_stream(mem, "s_proc_init(stream)");
69
62.9k
    stream_proc_state *state = (stream_proc_state *)
70
62.9k
        s_alloc_state(mem, &st_sproc_state, "s_proc_init(state)");
71
72
62.9k
    if (sstrm == 0 || state == 0) {
73
0
        gs_free_object(mem, state, "s_proc_init(state)");
74
        /*gs_free_object(mem, sstrm, "s_proc_init(stream)"); *//* just leave it on the file list */
75
0
        return_error(gs_error_VMerror);
76
0
    }
77
62.9k
    s_std_init(sstrm, NULL, 0, procs, mode);
78
62.9k
    sstrm->procs.process = temp->process;
79
62.9k
    state->templat = temp;
80
62.9k
    state->memory = mem;
81
62.9k
    state->eof = 0;
82
62.9k
    state->proc = *sop;
83
62.9k
    make_empty_string(&state->data, a_all);
84
62.9k
    state->index = 0;
85
62.9k
    sstrm->state = (stream_state *) state;
86
62.9k
    *psstrm = sstrm;
87
62.9k
    return 0;
88
62.9k
}
89
90
/* Handle an interrupt during a stream operation. */
91
/* This is logically unrelated to procedure streams, */
92
/* but it is also associated with the interpreter stream machinery. */
93
static int
94
s_handle_intc(i_ctx_t *i_ctx_p, const ref *pstate, int nstate,
95
              op_proc_t cont)
96
0
{
97
0
    int npush = nstate + 2;
98
99
0
    check_estack(npush);
100
0
    if (nstate)
101
0
        memcpy(esp + 2, pstate, nstate * sizeof(ref));
102
#if 0       /* **************** */
103
    {
104
        int code = gs_interpret_error(gs_error_interrupt, (ref *) (esp + npush));
105
106
        if (code < 0)
107
            return code;
108
    }
109
#else /* **************** */
110
0
    npush--;
111
0
#endif /* **************** */
112
0
    make_op_estack(esp + 1, cont);
113
0
    esp += npush;
114
0
    return o_push_estack;
115
0
}
116
117
/* Set default parameter values (actually, just clear pointers). */
118
static void
119
s_proc_set_defaults(stream_state * st)
120
0
{
121
0
    stream_proc_state *const ss = (stream_proc_state *) st;
122
123
0
    make_null(&ss->proc);
124
0
    make_null(&ss->data);
125
0
}
126
127
/* ---------------- Read streams ---------------- */
128
129
/* Forward references */
130
static stream_proc_process(s_proc_read_process);
131
static int s_proc_read_continue(i_ctx_t *);
132
133
/* Stream templates */
134
static const stream_template s_proc_read_template = {
135
    &st_sproc_state, NULL, s_proc_read_process, 1, 1,
136
    NULL, s_proc_set_defaults
137
};
138
static const stream_procs s_proc_read_procs = {
139
    s_std_noavailable, s_std_noseek, s_std_read_reset,
140
    s_std_read_flush, s_std_null, NULL
141
};
142
143
/* Allocate and open a procedure-based read stream. */
144
/* The caller must have checked that *sop is a procedure. */
145
int
146
sread_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
147
58.6k
{
148
58.6k
    int code =
149
58.6k
        s_proc_init(sop, psstrm, s_mode_read, &s_proc_read_template,
150
58.6k
                    &s_proc_read_procs, imem);
151
152
58.6k
    if (code < 0)
153
0
        return code;
154
58.6k
    (*psstrm)->end_status = CALLC;
155
58.6k
    return code;
156
58.6k
}
157
158
/* Handle an input request. */
159
static int
160
s_proc_read_process(stream_state * st, stream_cursor_read * ignore_pr,
161
                    stream_cursor_write * pw, bool last)
162
205k
{
163
    /* Move data from the string returned by the procedure */
164
    /* into the stream buffer, or ask for a callback. */
165
205k
    stream_proc_state *const ss = (stream_proc_state *) st;
166
205k
    uint count = r_size(&ss->data) - ss->index;
167
168
205k
    if (count > 0) {
169
97.8k
        uint wcount = pw->limit - pw->ptr;
170
171
97.8k
        if (wcount < count)
172
39.1k
            count = wcount;
173
97.8k
        memcpy(pw->ptr + 1, ss->data.value.bytes + ss->index, count);
174
97.8k
        pw->ptr += count;
175
97.8k
        ss->index += count;
176
97.8k
        return 1;
177
97.8k
    }
178
107k
    return (ss->eof ? EOFC : CALLC);
179
205k
}
180
181
/* Handle an exception (INTC or CALLC) from a read stream */
182
/* whose buffer is empty. */
183
int
184
s_handle_read_exception(i_ctx_t *i_ctx_p, int status, const ref * fop,
185
                        const ref * pstate, int nstate, op_proc_t cont)
186
107k
{
187
107k
    int npush = nstate + 4;
188
107k
    stream *ps;
189
190
107k
    switch (status) {
191
0
        case INTC:
192
0
            return s_handle_intc(i_ctx_p, pstate, nstate, cont);
193
107k
        case CALLC:
194
107k
            break;
195
0
        default:
196
0
            return_error(gs_error_ioerror);
197
107k
    }
198
    /* Find the stream whose buffer needs refilling. */
199
215k
    for (ps = fptr(fop); ps->strm != 0;)
200
107k
        ps = ps->strm;
201
107k
    check_estack(npush);
202
107k
    if (nstate)
203
107k
        memcpy(esp + 2, pstate, nstate * sizeof(ref));
204
107k
    make_op_estack(esp + 1, cont);
205
107k
    esp += npush;
206
107k
    make_op_estack(esp - 2, s_proc_read_continue);
207
107k
    esp[-1] = *fop;
208
107k
    r_clear_attrs(esp - 1, a_executable);
209
107k
    *esp = ((stream_proc_state *) ps->state)->proc;
210
107k
    return o_push_estack;
211
107k
}
212
/* Continue a read operation after returning from a procedure callout. */
213
/* osp[0] contains the file (pushed on the e-stack by handle_read_status); */
214
/* osp[-1] contains the new data string (pushed by the procedure). */
215
/* The top of the e-stack contains the real continuation. */
216
static int
217
s_proc_read_continue(i_ctx_t *i_ctx_p)
218
107k
{
219
107k
    os_ptr op = osp;
220
107k
    os_ptr opbuf = op - 1;
221
107k
    stream *ps;
222
107k
    stream_proc_state *ss;
223
224
107k
    check_file(ps, op);
225
107k
    check_read_type(*opbuf, t_string);
226
215k
    while ((ps->end_status = 0, ps->strm) != 0)
227
107k
        ps = ps->strm;
228
107k
    ss = (stream_proc_state *) ps->state;
229
107k
    ss->data = *opbuf;
230
107k
    ss->index = 0;
231
107k
    if (r_size(opbuf) == 0)
232
48.9k
        ss->eof = true;
233
107k
    pop(2);
234
107k
    return 0;
235
107k
}
236
237
/* ---------------- Write streams ---------------- */
238
239
/* Forward references */
240
static stream_proc_flush(s_proc_write_flush);
241
static stream_proc_process(s_proc_write_process);
242
static int s_proc_write_continue(i_ctx_t *);
243
244
/* Stream templates */
245
static const stream_template s_proc_write_template = {
246
    &st_sproc_state, NULL, s_proc_write_process, 1, 1,
247
    NULL, s_proc_set_defaults
248
};
249
static const stream_procs s_proc_write_procs = {
250
    s_std_noavailable, s_std_noseek, s_std_write_reset,
251
    s_proc_write_flush, s_std_null, NULL
252
};
253
254
/* Allocate and open a procedure-based write stream. */
255
/* The caller must have checked that *sop is a procedure. */
256
int
257
swrite_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
258
4.24k
{
259
4.24k
    return s_proc_init(sop, psstrm, s_mode_write, &s_proc_write_template,
260
4.24k
                       &s_proc_write_procs, imem);
261
4.24k
}
262
263
/* Handle an output request. */
264
static int
265
s_proc_write_process(stream_state * st, stream_cursor_read * pr,
266
                     stream_cursor_write * ignore_pw, bool last)
267
13.9k
{
268
    /* Move data from the stream buffer to the string */
269
    /* returned by the procedure, or ask for a callback. */
270
13.9k
    stream_proc_state *const ss = (stream_proc_state *) st;
271
13.9k
    uint rcount = pr->limit - pr->ptr;
272
273
    /* if 'last' return CALLC even when rcount == 0. ss->eof terminates */
274
13.9k
    if (rcount > 0 || (last && !ss->eof)) {
275
9.70k
        uint wcount = r_size(&ss->data) - ss->index;
276
9.70k
        uint count = min(rcount, wcount);
277
278
9.70k
        memcpy(ss->data.value.bytes + ss->index, pr->ptr + 1, count);
279
9.70k
        pr->ptr += count;
280
9.70k
        ss->index += count;
281
9.70k
        if (rcount > wcount)
282
5.46k
            return CALLC;
283
4.24k
        else if (last) {
284
4.24k
            ss->eof = true;
285
4.24k
            return CALLC;
286
4.24k
        } else
287
0
            return 0;
288
9.70k
    }
289
4.24k
    return ((ss->eof = last) ? EOFC : 0);
290
13.9k
}
291
292
/* Flush the output.  This is non-standard because it must call the */
293
/* procedure. */
294
static int
295
s_proc_write_flush(stream *s)
296
0
{
297
0
    int result = s_process_write_buf(s, false);
298
0
    stream_proc_state *const ss = (stream_proc_state *)s->state;
299
300
0
    return (result < 0 || ss->index == 0 ? result : CALLC);
301
0
}
302
303
/* Handle an exception (INTC or CALLC) from a write stream */
304
/* whose buffer is full. */
305
int
306
s_handle_write_exception(i_ctx_t *i_ctx_p, int status, const ref * fop,
307
                         const ref * pstate, int nstate, op_proc_t cont)
308
9.70k
{
309
9.70k
    stream *ps;
310
9.70k
    stream_proc_state *psst;
311
312
9.70k
    switch (status) {
313
0
        case INTC:
314
0
            return s_handle_intc(i_ctx_p, pstate, nstate, cont);
315
9.70k
        case CALLC:
316
9.70k
            break;
317
0
        default:
318
0
            return_error(gs_error_ioerror);
319
9.70k
    }
320
    /* Find the stream whose buffer needs emptying. */
321
19.4k
    for (ps = fptr(fop); ps->strm != 0;)
322
9.70k
        ps = ps->strm;
323
9.70k
    psst = (stream_proc_state *) ps->state;
324
9.70k
    {
325
9.70k
        int npush = nstate + 6;
326
327
9.70k
        check_estack(npush);
328
9.70k
        if (nstate)
329
0
            memcpy(esp + 2, pstate, nstate * sizeof(ref));
330
9.70k
        make_op_estack(esp + 1, cont);
331
9.70k
        esp += npush;
332
9.70k
        make_op_estack(esp - 4, s_proc_write_continue);
333
9.70k
        esp[-3] = *fop;
334
9.70k
        r_clear_attrs(esp - 3, a_executable);
335
9.70k
        make_bool(esp - 1, !psst->eof);
336
9.70k
    }
337
9.70k
    esp[-2] = psst->proc;
338
9.70k
    *esp = psst->data;
339
9.70k
    r_set_size(esp, psst->index);
340
9.70k
    return o_push_estack;
341
9.70k
}
342
/* Continue a write operation after returning from a procedure callout. */
343
/* osp[0] contains the file (pushed on the e-stack by handle_write_status); */
344
/* osp[-1] contains the new buffer string (pushed by the procedure). */
345
/* The top of the e-stack contains the real continuation. */
346
static int
347
s_proc_write_continue(i_ctx_t *i_ctx_p)
348
9.70k
{
349
9.70k
    os_ptr op = osp;
350
9.70k
    os_ptr opbuf = op - 1;
351
9.70k
    stream *ps;
352
9.70k
    stream_proc_state *ss;
353
354
9.70k
    check_file(ps, op);
355
9.70k
    check_write_type(*opbuf, t_string);
356
19.4k
    while (ps->strm != 0) {
357
9.70k
        if (ps->end_status == CALLC)
358
0
            ps->end_status = 0;
359
9.70k
        ps = ps->strm;
360
9.70k
    }
361
9.70k
    ps->end_status = 0;
362
9.70k
    ss = (stream_proc_state *) ps->state;
363
9.70k
    ss->data = *opbuf;
364
9.70k
    ss->index = 0;
365
9.70k
    pop(2);
366
9.70k
    return 0;
367
9.70k
}
368
369
/* ------ More generic ------ */
370
371
/* Test whether a stream is procedure-based. */
372
bool
373
s_is_proc(const stream *s)
374
0
{
375
0
    return (s->procs.process == s_proc_read_process ||
376
0
            s->procs.process == s_proc_write_process);
377
0
}
378
379
/* ------ Initialization procedure ------ */
380
381
const op_def zfproc_op_defs[] =
382
{
383
                /* Internal operators */
384
    {"2%s_proc_read_continue", s_proc_read_continue},
385
    {"2%s_proc_write_continue", s_proc_write_continue},
386
    op_def_end(0)
387
};