xref: /plan9/sys/src/cmd/gs/src/zfproc.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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