1 /* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2001 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: zfproc.c,v 1.12 2002/06/16 03:43:51 lpd Exp $ */
18 /* Procedure-based filter stream support */
19 #include "memory_.h"
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 private
CLEAR_MARKS_PROC(sproc_clear_marks)36 CLEAR_MARKS_PROC(sproc_clear_marks)
37 {
38 stream_proc_state *const pptr = vptr;
39
40 r_clear_attrs(&pptr->proc, l_mark);
41 r_clear_attrs(&pptr->data, l_mark);
42 }
43 private
44 ENUM_PTRS_WITH(sproc_enum_ptrs, stream_proc_state *pptr) return 0;
45 case 0:
46 ENUM_RETURN_REF(&pptr->proc);
47 case 1:
48 ENUM_RETURN_REF(&pptr->data);
49 ENUM_PTRS_END
50 private RELOC_PTRS_WITH(sproc_reloc_ptrs, stream_proc_state *pptr);
51 RELOC_REF_VAR(pptr->proc);
52 r_clear_attrs(&pptr->proc, l_mark);
53 RELOC_REF_VAR(pptr->data);
54 r_clear_attrs(&pptr->data, l_mark);
55 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 private int
s_proc_init(ref * sop,stream ** psstrm,uint mode,const stream_template * temp,const stream_procs * procs,gs_ref_memory_t * imem)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 {
67 gs_memory_t *const mem = (gs_memory_t *)imem;
68 stream *sstrm = file_alloc_stream(mem, "s_proc_init(stream)");
69 stream_proc_state *state = (stream_proc_state *)
70 s_alloc_state(mem, &st_sproc_state, "s_proc_init(state)");
71
72 if (sstrm == 0 || state == 0) {
73 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 return_error(e_VMerror);
76 }
77 s_std_init(sstrm, NULL, 0, procs, mode);
78 sstrm->procs.process = temp->process;
79 state->template = temp;
80 state->memory = mem;
81 state->eof = 0;
82 state->proc = *sop;
83 make_empty_string(&state->data, a_all);
84 state->index = 0;
85 sstrm->state = (stream_state *) state;
86 *psstrm = sstrm;
87 return 0;
88 }
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 private int
s_handle_intc(i_ctx_t * i_ctx_p,const ref * pstate,int nstate,op_proc_t cont)94 s_handle_intc(i_ctx_t *i_ctx_p, const ref *pstate, int nstate,
95 op_proc_t cont)
96 {
97 int npush = nstate + 2;
98
99 check_estack(npush);
100 if (nstate)
101 memcpy(esp + 2, pstate, nstate * sizeof(ref));
102 #if 0 /* **************** */
103 {
104 int code = gs_interpret_error(e_interrupt, (ref *) (esp + npush));
105
106 if (code < 0)
107 return code;
108 }
109 #else /* **************** */
110 npush--;
111 #endif /* **************** */
112 make_op_estack(esp + 1, cont);
113 esp += npush;
114 return o_push_estack;
115 }
116
117 /* Set default parameter values (actually, just clear pointers). */
118 private void
s_proc_set_defaults(stream_state * st)119 s_proc_set_defaults(stream_state * st)
120 {
121 stream_proc_state *const ss = (stream_proc_state *) st;
122
123 make_null(&ss->proc);
124 make_null(&ss->data);
125 }
126
127 /* ---------------- Read streams ---------------- */
128
129 /* Forward references */
130 private stream_proc_process(s_proc_read_process);
131 private int s_proc_read_continue(i_ctx_t *);
132
133 /* Stream templates */
134 private 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 private 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
sread_proc(ref * sop,stream ** psstrm,gs_ref_memory_t * imem)146 sread_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
147 {
148 int code =
149 s_proc_init(sop, psstrm, s_mode_read, &s_proc_read_template,
150 &s_proc_read_procs, imem);
151
152 if (code < 0)
153 return code;
154 (*psstrm)->end_status = CALLC;
155 return code;
156 }
157
158 /* Handle an input request. */
159 private int
s_proc_read_process(stream_state * st,stream_cursor_read * ignore_pr,stream_cursor_write * pw,bool last)160 s_proc_read_process(stream_state * st, stream_cursor_read * ignore_pr,
161 stream_cursor_write * pw, bool last)
162 {
163 /* Move data from the string returned by the procedure */
164 /* into the stream buffer, or ask for a callback. */
165 stream_proc_state *const ss = (stream_proc_state *) st;
166 uint count = r_size(&ss->data) - ss->index;
167
168 if (count > 0) {
169 uint wcount = pw->limit - pw->ptr;
170
171 if (wcount < count)
172 count = wcount;
173 memcpy(pw->ptr + 1, ss->data.value.bytes + ss->index, count);
174 pw->ptr += count;
175 ss->index += count;
176 return 1;
177 }
178 return (ss->eof ? EOFC : CALLC);
179 }
180
181 /* Handle an exception (INTC or CALLC) from a read stream */
182 /* whose buffer is empty. */
183 int
s_handle_read_exception(i_ctx_t * i_ctx_p,int status,const ref * fop,const ref * pstate,int nstate,op_proc_t cont)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 {
187 int npush = nstate + 4;
188 stream *ps;
189 stream *psstdin;
190
191 switch (status) {
192 case INTC:
193 return s_handle_intc(i_ctx_p, pstate, nstate, cont);
194 case CALLC:
195 break;
196 default:
197 return_error(e_ioerror);
198 }
199 /* Find the stream whose buffer needs refilling. */
200 for (ps = fptr(fop); ps->strm != 0;)
201 ps = ps->strm;
202 check_estack(npush);
203 if (nstate)
204 memcpy(esp + 2, pstate, nstate * sizeof(ref));
205 make_op_estack(esp + 1, cont);
206 esp += npush;
207 make_op_estack(esp - 2, s_proc_read_continue);
208 esp[-1] = *fop;
209 r_clear_attrs(esp - 1, a_executable);
210 *esp = ((stream_proc_state *) ps->state)->proc;
211
212 /* If stream is stdin, ask for callout. */
213 zget_stdin(i_ctx_p, &psstdin);
214 if (ps == psstdin) {
215 check_estack(1);
216 esp += 1;
217 make_op_estack(esp, zneedstdin);
218 }
219 return o_push_estack;
220 }
221 /* Continue a read operation after returning from a procedure callout. */
222 /* osp[0] contains the file (pushed on the e-stack by handle_read_status); */
223 /* osp[-1] contains the new data string (pushed by the procedure). */
224 /* The top of the e-stack contains the real continuation. */
225 private int
s_proc_read_continue(i_ctx_t * i_ctx_p)226 s_proc_read_continue(i_ctx_t *i_ctx_p)
227 {
228 os_ptr op = osp;
229 os_ptr opbuf = op - 1;
230 stream *ps;
231 stream_proc_state *ss;
232
233 check_file(ps, op);
234 check_read_type(*opbuf, t_string);
235 while ((ps->end_status = 0, ps->strm) != 0)
236 ps = ps->strm;
237 ss = (stream_proc_state *) ps->state;
238 ss->data = *opbuf;
239 ss->index = 0;
240 if (r_size(opbuf) == 0)
241 ss->eof = true;
242 pop(2);
243 return 0;
244 }
245
246 /* ---------------- Write streams ---------------- */
247
248 /* Forward references */
249 private stream_proc_flush(s_proc_write_flush);
250 private stream_proc_process(s_proc_write_process);
251 private int s_proc_write_continue(i_ctx_t *);
252
253 /* Stream templates */
254 private const stream_template s_proc_write_template = {
255 &st_sproc_state, NULL, s_proc_write_process, 1, 1,
256 NULL, s_proc_set_defaults
257 };
258 private const stream_procs s_proc_write_procs = {
259 s_std_noavailable, s_std_noseek, s_std_write_reset,
260 s_proc_write_flush, s_std_null, NULL
261 };
262
263 /* Allocate and open a procedure-based write stream. */
264 /* The caller must have checked that *sop is a procedure. */
265 int
swrite_proc(ref * sop,stream ** psstrm,gs_ref_memory_t * imem)266 swrite_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
267 {
268 return s_proc_init(sop, psstrm, s_mode_write, &s_proc_write_template,
269 &s_proc_write_procs, imem);
270 }
271
272 /* Handle an output request. */
273 private int
s_proc_write_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)274 s_proc_write_process(stream_state * st, stream_cursor_read * pr,
275 stream_cursor_write * ignore_pw, bool last)
276 {
277 /* Move data from the stream buffer to the string */
278 /* returned by the procedure, or ask for a callback. */
279 stream_proc_state *const ss = (stream_proc_state *) st;
280 uint rcount = pr->limit - pr->ptr;
281
282 if (rcount > 0) {
283 uint wcount = r_size(&ss->data) - ss->index;
284 uint count = min(rcount, wcount);
285
286 memcpy(ss->data.value.bytes + ss->index, pr->ptr + 1, count);
287 pr->ptr += count;
288 ss->index += count;
289 if (rcount > wcount)
290 return CALLC;
291 else if (last) {
292 ss->eof = true;
293 return CALLC;
294 } else
295 return 0;
296 }
297 return ((ss->eof = last) ? EOFC : 0);
298 }
299
300 /* Flush the output. This is non-standard because it must call the */
301 /* procedure. */
302 private int
s_proc_write_flush(stream * s)303 s_proc_write_flush(stream *s)
304 {
305 int result = s_process_write_buf(s, false);
306 stream_proc_state *const ss = (stream_proc_state *)s->state;
307
308 return (result < 0 || ss->index == 0 ? result : CALLC);
309 }
310
311 /* Handle an exception (INTC or CALLC) from a write stream */
312 /* whose buffer is full. */
313 int
s_handle_write_exception(i_ctx_t * i_ctx_p,int status,const ref * fop,const ref * pstate,int nstate,op_proc_t cont)314 s_handle_write_exception(i_ctx_t *i_ctx_p, int status, const ref * fop,
315 const ref * pstate, int nstate, op_proc_t cont)
316 {
317 stream *ps;
318 stream *psstderr;
319 stream *psstdout;
320 stream_proc_state *psst;
321
322 switch (status) {
323 case INTC:
324 return s_handle_intc(i_ctx_p, pstate, nstate, cont);
325 case CALLC:
326 break;
327 default:
328 return_error(e_ioerror);
329 }
330 /* Find the stream whose buffer needs emptying. */
331 for (ps = fptr(fop); ps->strm != 0;)
332 ps = ps->strm;
333 psst = (stream_proc_state *) ps->state;
334 {
335 int npush = nstate + 6;
336
337 check_estack(npush);
338 if (nstate)
339 memcpy(esp + 2, pstate, nstate * sizeof(ref));
340 make_op_estack(esp + 1, cont);
341 esp += npush;
342 make_op_estack(esp - 4, s_proc_write_continue);
343 esp[-3] = *fop;
344 r_clear_attrs(esp - 3, a_executable);
345 make_bool(esp - 1, !psst->eof);
346 }
347 esp[-2] = psst->proc;
348 *esp = psst->data;
349 r_set_size(esp, psst->index);
350
351 /* If stream is stdout/err, ask for callout. */
352 zget_stdout(i_ctx_p, &psstdout);
353 zget_stderr(i_ctx_p, &psstderr);
354 if ((ps == psstderr) || (ps == psstdout)) {
355 check_estack(1);
356 esp += 1;
357 make_op_estack(esp, (ps == psstderr) ? zneedstderr : zneedstdout);
358 }
359 return o_push_estack;
360 }
361 /* Continue a write operation after returning from a procedure callout. */
362 /* osp[0] contains the file (pushed on the e-stack by handle_write_status); */
363 /* osp[-1] contains the new buffer string (pushed by the procedure). */
364 /* The top of the e-stack contains the real continuation. */
365 private int
s_proc_write_continue(i_ctx_t * i_ctx_p)366 s_proc_write_continue(i_ctx_t *i_ctx_p)
367 {
368 os_ptr op = osp;
369 os_ptr opbuf = op - 1;
370 stream *ps;
371 stream_proc_state *ss;
372
373 check_file(ps, op);
374 check_write_type(*opbuf, t_string);
375 while (ps->strm != 0) {
376 if (ps->end_status == CALLC)
377 ps->end_status = 0;
378 ps = ps->strm;
379 }
380 ps->end_status = 0;
381 ss = (stream_proc_state *) ps->state;
382 ss->data = *opbuf;
383 ss->index = 0;
384 pop(2);
385 return 0;
386 }
387
388 /* ------ More generic ------ */
389
390 /* Test whether a stream is procedure-based. */
391 bool
s_is_proc(const stream * s)392 s_is_proc(const stream *s)
393 {
394 return (s->procs.process == s_proc_read_process ||
395 s->procs.process == s_proc_write_process);
396 }
397
398 /* ------ Initialization procedure ------ */
399
400 const op_def zfproc_op_defs[] =
401 {
402 /* Internal operators */
403 {"2%s_proc_read_continue", s_proc_read_continue},
404 {"2%s_proc_write_continue", s_proc_write_continue},
405 op_def_end(0)
406 };
407