/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 | | }; |