Coverage Report

Created: 2025-06-24 07:01

/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
785k
{
38
785k
    stream_proc_state *const pptr = vptr;
39
40
785k
    r_clear_attrs(&pptr->proc, l_mark);
41
785k
    r_clear_attrs(&pptr->data, l_mark);
42
785k
}
43
static
44
101k
ENUM_PTRS_WITH(sproc_enum_ptrs, stream_proc_state *pptr) return 0;
45
33.9k
case 0:
46
33.9k
ENUM_RETURN_REF(&pptr->proc);
47
33.9k
case 1:
48
33.9k
ENUM_RETURN_REF(&pptr->data);
49
101k
ENUM_PTRS_END
50
33.9k
static RELOC_PTRS_WITH(sproc_reloc_ptrs, stream_proc_state *pptr);
51
33.9k
RELOC_REF_VAR(pptr->proc);
52
33.9k
r_clear_attrs(&pptr->proc, l_mark);
53
33.9k
RELOC_REF_VAR(pptr->data);
54
33.9k
r_clear_attrs(&pptr->data, l_mark);
55
33.9k
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
1.04M
{
67
1.04M
    gs_memory_t *const mem = (gs_memory_t *)imem;
68
1.04M
    stream *sstrm = file_alloc_stream(mem, "s_proc_init(stream)");
69
1.04M
    stream_proc_state *state = (stream_proc_state *)
70
1.04M
        s_alloc_state(mem, &st_sproc_state, "s_proc_init(state)");
71
72
1.04M
    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
1.04M
    s_std_init(sstrm, NULL, 0, procs, mode);
78
1.04M
    sstrm->procs.process = temp->process;
79
1.04M
    state->templat = temp;
80
1.04M
    state->memory = mem;
81
1.04M
    state->eof = 0;
82
1.04M
    state->proc = *sop;
83
1.04M
    make_empty_string(&state->data, a_all);
84
1.04M
    state->index = 0;
85
1.04M
    sstrm->state = (stream_state *) state;
86
1.04M
    *psstrm = sstrm;
87
1.04M
    return 0;
88
1.04M
}
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
974k
{
148
974k
    int code =
149
974k
        s_proc_init(sop, psstrm, s_mode_read, &s_proc_read_template,
150
974k
                    &s_proc_read_procs, imem);
151
152
974k
    if (code < 0)
153
0
        return code;
154
974k
    (*psstrm)->end_status = CALLC;
155
974k
    return code;
156
974k
}
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
3.41M
{
163
    /* Move data from the string returned by the procedure */
164
    /* into the stream buffer, or ask for a callback. */
165
3.41M
    stream_proc_state *const ss = (stream_proc_state *) st;
166
3.41M
    uint count = r_size(&ss->data) - ss->index;
167
168
3.41M
    if (count > 0) {
169
1.62M
        uint wcount = pw->limit - pw->ptr;
170
171
1.62M
        if (wcount < count)
172
649k
            count = wcount;
173
1.62M
        memcpy(pw->ptr + 1, ss->data.value.bytes + ss->index, count);
174
1.62M
        pw->ptr += count;
175
1.62M
        ss->index += count;
176
1.62M
        return 1;
177
1.62M
    }
178
1.78M
    return (ss->eof ? EOFC : CALLC);
179
3.41M
}
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
1.78M
{
187
1.78M
    int npush = nstate + 4;
188
1.78M
    stream *ps;
189
190
1.78M
    switch (status) {
191
0
        case INTC:
192
0
            return s_handle_intc(i_ctx_p, pstate, nstate, cont);
193
1.78M
        case CALLC:
194
1.78M
            break;
195
0
        default:
196
0
            return_error(gs_error_ioerror);
197
1.78M
    }
198
    /* Find the stream whose buffer needs refilling. */
199
3.57M
    for (ps = fptr(fop); ps->strm != 0;)
200
1.78M
        ps = ps->strm;
201
1.78M
    check_estack(npush);
202
1.78M
    if (nstate)
203
1.78M
        memcpy(esp + 2, pstate, nstate * sizeof(ref));
204
1.78M
    make_op_estack(esp + 1, cont);
205
1.78M
    esp += npush;
206
1.78M
    make_op_estack(esp - 2, s_proc_read_continue);
207
1.78M
    esp[-1] = *fop;
208
1.78M
    r_clear_attrs(esp - 1, a_executable);
209
1.78M
    *esp = ((stream_proc_state *) ps->state)->proc;
210
1.78M
    return o_push_estack;
211
1.78M
}
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
1.78M
{
219
1.78M
    os_ptr op = osp;
220
1.78M
    os_ptr opbuf = op - 1;
221
1.78M
    stream *ps;
222
1.78M
    stream_proc_state *ss;
223
224
1.78M
    check_file(ps, op);
225
1.78M
    check_read_type(*opbuf, t_string);
226
3.57M
    while ((ps->end_status = 0, ps->strm) != 0)
227
1.78M
        ps = ps->strm;
228
1.78M
    ss = (stream_proc_state *) ps->state;
229
1.78M
    ss->data = *opbuf;
230
1.78M
    ss->index = 0;
231
1.78M
    if (r_size(opbuf) == 0)
232
812k
        ss->eof = true;
233
1.78M
    pop(2);
234
1.78M
    return 0;
235
1.78M
}
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
67.2k
{
259
67.2k
    return s_proc_init(sop, psstrm, s_mode_write, &s_proc_write_template,
260
67.2k
                       &s_proc_write_procs, imem);
261
67.2k
}
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
251k
{
268
    /* Move data from the stream buffer to the string */
269
    /* returned by the procedure, or ask for a callback. */
270
251k
    stream_proc_state *const ss = (stream_proc_state *) st;
271
251k
    uint rcount = pr->limit - pr->ptr;
272
273
    /* if 'last' return CALLC even when rcount == 0. ss->eof terminates */
274
251k
    if (rcount > 0 || (last && !ss->eof)) {
275
184k
        uint wcount = r_size(&ss->data) - ss->index;
276
184k
        uint count = min(rcount, wcount);
277
278
184k
        memcpy(ss->data.value.bytes + ss->index, pr->ptr + 1, count);
279
184k
        pr->ptr += count;
280
184k
        ss->index += count;
281
184k
        if (rcount > wcount)
282
117k
            return CALLC;
283
67.2k
        else if (last) {
284
67.2k
            ss->eof = true;
285
67.2k
            return CALLC;
286
67.2k
        } else
287
0
            return 0;
288
184k
    }
289
67.2k
    return ((ss->eof = last) ? EOFC : 0);
290
251k
}
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
184k
{
309
184k
    stream *ps;
310
184k
    stream_proc_state *psst;
311
312
184k
    switch (status) {
313
0
        case INTC:
314
0
            return s_handle_intc(i_ctx_p, pstate, nstate, cont);
315
184k
        case CALLC:
316
184k
            break;
317
0
        default:
318
0
            return_error(gs_error_ioerror);
319
184k
    }
320
    /* Find the stream whose buffer needs emptying. */
321
369k
    for (ps = fptr(fop); ps->strm != 0;)
322
184k
        ps = ps->strm;
323
184k
    psst = (stream_proc_state *) ps->state;
324
184k
    {
325
184k
        int npush = nstate + 6;
326
327
184k
        check_estack(npush);
328
184k
        if (nstate)
329
0
            memcpy(esp + 2, pstate, nstate * sizeof(ref));
330
184k
        make_op_estack(esp + 1, cont);
331
184k
        esp += npush;
332
184k
        make_op_estack(esp - 4, s_proc_write_continue);
333
184k
        esp[-3] = *fop;
334
184k
        r_clear_attrs(esp - 3, a_executable);
335
184k
        make_bool(esp - 1, !psst->eof);
336
184k
    }
337
184k
    esp[-2] = psst->proc;
338
184k
    *esp = psst->data;
339
184k
    r_set_size(esp, psst->index);
340
184k
    return o_push_estack;
341
184k
}
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
184k
{
349
184k
    os_ptr op = osp;
350
184k
    os_ptr opbuf = op - 1;
351
184k
    stream *ps;
352
184k
    stream_proc_state *ss;
353
354
184k
    check_file(ps, op);
355
184k
    check_write_type(*opbuf, t_string);
356
369k
    while (ps->strm != 0) {
357
184k
        if (ps->end_status == CALLC)
358
0
            ps->end_status = 0;
359
184k
        ps = ps->strm;
360
184k
    }
361
184k
    ps->end_status = 0;
362
184k
    ss = (stream_proc_state *) ps->state;
363
184k
    ss->data = *opbuf;
364
184k
    ss->index = 0;
365
184k
    pop(2);
366
184k
    return 0;
367
184k
}
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
};