xref: /plan9/sys/src/cmd/gs/src/zfproc.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
1 /* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2001 Aladdin Enterprises.  All rights reserved.
2 
3   This file is part of AFPL Ghostscript.
4 
5   AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author or
6   distributor accepts any responsibility for the consequences of using it, or
7   for whether it serves any particular purpose or works at all, unless he or
8   she says so in writing.  Refer to the Aladdin Free Public License (the
9   "License") for full details.
10 
11   Every copy of AFPL Ghostscript must include a copy of the License, normally
12   in a plain ASCII text file named PUBLIC.  The License grants you the right
13   to copy, modify and redistribute AFPL Ghostscript, but only under certain
14   conditions described in the License.  Among other things, the License
15   requires that the copyright notice and this notice be preserved on all
16   copies.
17 */
18 
19 /*$Id: zfproc.c,v 1.9 2001/03/29 04:09:40 rayjj Exp $ */
20 /* Procedure-based filter stream support */
21 #include "memory_.h"
22 #include "ghost.h"
23 #include "oper.h"		/* for ifilter.h */
24 #include "estack.h"
25 #include "gsstruct.h"
26 #include "ialloc.h"
27 #include "istruct.h"		/* for RELOC_REF_VAR */
28 #include "stream.h"
29 #include "strimpl.h"
30 #include "ifilter.h"
31 #include "files.h"
32 #include "store.h"
33 
34 /* ---------------- Generic ---------------- */
35 
36 /* GC procedures */
37 private
38 CLEAR_MARKS_PROC(sproc_clear_marks)
39 {
40     stream_proc_state *const pptr = vptr;
41 
42     r_clear_attrs(&pptr->proc, l_mark);
43     r_clear_attrs(&pptr->data, l_mark);
44 }
45 private
46 ENUM_PTRS_WITH(sproc_enum_ptrs, stream_proc_state *pptr) return 0;
47 case 0:
48 ENUM_RETURN_REF(&pptr->proc);
49 case 1:
50 ENUM_RETURN_REF(&pptr->data);
51 ENUM_PTRS_END
52 private RELOC_PTRS_WITH(sproc_reloc_ptrs, stream_proc_state *pptr);
53 RELOC_REF_VAR(pptr->proc);
54 r_clear_attrs(&pptr->proc, l_mark);
55 RELOC_REF_VAR(pptr->data);
56 r_clear_attrs(&pptr->data, l_mark);
57 RELOC_PTRS_END
58 
59 /* Structure type for procedure-based streams. */
60 private_st_stream_proc_state();
61 
62 /* Allocate and open a procedure-based filter. */
63 /* The caller must have checked that *sop is a procedure. */
64 private int
65 s_proc_init(ref * sop, stream ** psstrm, uint mode,
66 	    const stream_template * temp, const stream_procs * procs,
67 	    gs_ref_memory_t *imem)
68 {
69     gs_memory_t *const mem = (gs_memory_t *)imem;
70     stream *sstrm = file_alloc_stream(mem, "s_proc_init(stream)");
71     stream_proc_state *state = (stream_proc_state *)
72 	s_alloc_state(mem, &st_sproc_state, "s_proc_init(state)");
73 
74     if (sstrm == 0 || state == 0) {
75 	gs_free_object(mem, state, "s_proc_init(state)");
76 	/*gs_free_object(mem, sstrm, "s_proc_init(stream)"); *//* just leave it on the file list */
77 	return_error(e_VMerror);
78     }
79     s_std_init(sstrm, NULL, 0, procs, mode);
80     sstrm->procs.process = temp->process;
81     state->template = temp;
82     state->memory = mem;
83     state->eof = 0;
84     state->proc = *sop;
85     make_empty_string(&state->data, a_all);
86     state->index = 0;
87     sstrm->state = (stream_state *) state;
88     *psstrm = sstrm;
89     return 0;
90 }
91 
92 /* Handle an interrupt during a stream operation. */
93 /* This is logically unrelated to procedure streams, */
94 /* but it is also associated with the interpreter stream machinery. */
95 private int
96 s_handle_intc(i_ctx_t *i_ctx_p, const ref *pstate, int nstate,
97 	      op_proc_t cont)
98 {
99     int npush = nstate + 2;
100 
101     check_estack(npush);
102     if (nstate)
103 	memcpy(esp + 2, pstate, nstate * sizeof(ref));
104 #if 0				/* **************** */
105     {
106 	int code = gs_interpret_error(e_interrupt, (ref *) (esp + npush));
107 
108 	if (code < 0)
109 	    return code;
110     }
111 #else /* **************** */
112     npush--;
113 #endif /* **************** */
114     make_op_estack(esp + 1, cont);
115     esp += npush;
116     return o_push_estack;
117 }
118 
119 /* Set default parameter values (actually, just clear pointers). */
120 private void
121 s_proc_set_defaults(stream_state * st)
122 {
123     stream_proc_state *const ss = (stream_proc_state *) st;
124 
125     make_null(&ss->proc);
126     make_null(&ss->data);
127 }
128 
129 /* ---------------- Read streams ---------------- */
130 
131 /* Forward references */
132 private stream_proc_process(s_proc_read_process);
133 private int s_proc_read_continue(P1(i_ctx_t *));
134 
135 /* Stream templates */
136 private const stream_template s_proc_read_template = {
137     &st_sproc_state, NULL, s_proc_read_process, 1, 1,
138     NULL, s_proc_set_defaults
139 };
140 private const stream_procs s_proc_read_procs = {
141     s_std_noavailable, s_std_noseek, s_std_read_reset,
142     s_std_read_flush, s_std_null, NULL
143 };
144 
145 /* Allocate and open a procedure-based read stream. */
146 /* The caller must have checked that *sop is a procedure. */
147 int
148 sread_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
149 {
150     int code =
151 	s_proc_init(sop, psstrm, s_mode_read, &s_proc_read_template,
152 		    &s_proc_read_procs, imem);
153 
154     if (code < 0)
155 	return code;
156     (*psstrm)->end_status = CALLC;
157     return code;
158 }
159 
160 /* Handle an input request. */
161 private int
162 s_proc_read_process(stream_state * st, stream_cursor_read * ignore_pr,
163 		    stream_cursor_write * pw, bool last)
164 {
165     /* Move data from the string returned by the procedure */
166     /* into the stream buffer, or ask for a callback. */
167     stream_proc_state *const ss = (stream_proc_state *) st;
168     uint count = r_size(&ss->data) - ss->index;
169 
170     if (count > 0) {
171 	uint wcount = pw->limit - pw->ptr;
172 
173 	if (wcount < count)
174 	    count = wcount;
175 	memcpy(pw->ptr + 1, ss->data.value.bytes + ss->index, count);
176 	pw->ptr += count;
177 	ss->index += count;
178 	return 1;
179     }
180     return (ss->eof ? EOFC : CALLC);
181 }
182 
183 /* Handle an exception (INTC or CALLC) from a read stream */
184 /* whose buffer is empty. */
185 int
186 s_handle_read_exception(i_ctx_t *i_ctx_p, int status, const ref * fop,
187 			const ref * pstate, int nstate, op_proc_t cont)
188 {
189     int npush = nstate + 4;
190     stream *ps;
191     stream *psstdin;
192 
193     switch (status) {
194 	case INTC:
195 	    return s_handle_intc(i_ctx_p, pstate, nstate, cont);
196 	case CALLC:
197 	    break;
198 	default:
199 	    return_error(e_ioerror);
200     }
201     /* Find the stream whose buffer needs refilling. */
202     for (ps = fptr(fop); ps->strm != 0;)
203 	ps = ps->strm;
204     check_estack(npush);
205     if (nstate)
206 	memcpy(esp + 2, pstate, nstate * sizeof(ref));
207     make_op_estack(esp + 1, cont);
208     esp += npush;
209     make_op_estack(esp - 2, s_proc_read_continue);
210     esp[-1] = *fop;
211     r_clear_attrs(esp - 1, a_executable);
212     *esp = ((stream_proc_state *) ps->state)->proc;
213 
214     /* If stream is stdin, ask for callout. */
215     zget_stdin(i_ctx_p, &psstdin);
216     if (ps == psstdin) {
217 	check_estack(1);
218 	esp += 1;
219 	make_op_estack(esp, zneedstdin);
220     }
221     return o_push_estack;
222 }
223 /* Continue a read operation after returning from a procedure callout. */
224 /* osp[0] contains the file (pushed on the e-stack by handle_read_status); */
225 /* osp[-1] contains the new data string (pushed by the procedure). */
226 /* The top of the e-stack contains the real continuation. */
227 private int
228 s_proc_read_continue(i_ctx_t *i_ctx_p)
229 {
230     os_ptr op = osp;
231     os_ptr opbuf = op - 1;
232     stream *ps;
233     stream_proc_state *ss;
234 
235     check_file(ps, op);
236     check_read_type(*opbuf, t_string);
237     while ((ps->end_status = 0, ps->strm) != 0)
238 	ps = ps->strm;
239     ss = (stream_proc_state *) ps->state;
240     ss->data = *opbuf;
241     ss->index = 0;
242     if (r_size(opbuf) == 0)
243 	ss->eof = true;
244     pop(2);
245     return 0;
246 }
247 
248 /* ---------------- Write streams ---------------- */
249 
250 /* Forward references */
251 private stream_proc_flush(s_proc_write_flush);
252 private stream_proc_process(s_proc_write_process);
253 private int s_proc_write_continue(P1(i_ctx_t *));
254 
255 /* Stream templates */
256 private const stream_template s_proc_write_template = {
257     &st_sproc_state, NULL, s_proc_write_process, 1, 1,
258     NULL, s_proc_set_defaults
259 };
260 private const stream_procs s_proc_write_procs = {
261     s_std_noavailable, s_std_noseek, s_std_write_reset,
262     s_proc_write_flush, s_std_null, NULL
263 };
264 
265 /* Allocate and open a procedure-based write stream. */
266 /* The caller must have checked that *sop is a procedure. */
267 int
268 swrite_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem)
269 {
270     return s_proc_init(sop, psstrm, s_mode_write, &s_proc_write_template,
271 		       &s_proc_write_procs, imem);
272 }
273 
274 /* Handle an output request. */
275 private int
276 s_proc_write_process(stream_state * st, stream_cursor_read * pr,
277 		     stream_cursor_write * ignore_pw, bool last)
278 {
279     /* Move data from the stream buffer to the string */
280     /* returned by the procedure, or ask for a callback. */
281     stream_proc_state *const ss = (stream_proc_state *) st;
282     uint rcount = pr->limit - pr->ptr;
283 
284     if (rcount > 0) {
285 	uint wcount = r_size(&ss->data) - ss->index;
286 	uint count = min(rcount, wcount);
287 
288 	memcpy(ss->data.value.bytes + ss->index, pr->ptr + 1, count);
289 	pr->ptr += count;
290 	ss->index += count;
291 	if (rcount > wcount)
292 	    return CALLC;
293 	else if (last) {
294 	    ss->eof = true;
295 	    return CALLC;
296 	} else
297 	    return 0;
298     }
299     return ((ss->eof = last) ? EOFC : 0);
300 }
301 
302 /* Flush the output.  This is non-standard because it must call the */
303 /* procedure. */
304 private int
305 s_proc_write_flush(stream *s)
306 {
307     int result = s_process_write_buf(s, false);
308     stream_proc_state *const ss = (stream_proc_state *)s->state;
309 
310     return (result < 0 || ss->index == 0 ? result : CALLC);
311 }
312 
313 /* Handle an exception (INTC or CALLC) from a write stream */
314 /* whose buffer is full. */
315 int
316 s_handle_write_exception(i_ctx_t *i_ctx_p, int status, const ref * fop,
317 			 const ref * pstate, int nstate, op_proc_t cont)
318 {
319     stream *ps;
320     stream *psstderr;
321     stream *psstdout;
322     stream_proc_state *psst;
323 
324     switch (status) {
325 	case INTC:
326 	    return s_handle_intc(i_ctx_p, pstate, nstate, cont);
327 	case CALLC:
328 	    break;
329 	default:
330 	    return_error(e_ioerror);
331     }
332     /* Find the stream whose buffer needs emptying. */
333     for (ps = fptr(fop); ps->strm != 0;)
334 	ps = ps->strm;
335     psst = (stream_proc_state *) ps->state;
336     {
337 	int npush = nstate + 6;
338 
339 	check_estack(npush);
340 	if (nstate)
341 	    memcpy(esp + 2, pstate, nstate * sizeof(ref));
342 	make_op_estack(esp + 1, cont);
343 	esp += npush;
344 	make_op_estack(esp - 4, s_proc_write_continue);
345 	esp[-3] = *fop;
346 	r_clear_attrs(esp - 3, a_executable);
347 	make_bool(esp - 1, !psst->eof);
348     }
349     esp[-2] = psst->proc;
350     *esp = psst->data;
351     r_set_size(esp, psst->index);
352 
353     /* If stream is stdout/err, ask for callout. */
354     zget_stdout(i_ctx_p, &psstdout);
355     zget_stderr(i_ctx_p, &psstderr);
356     if ((ps == psstderr) || (ps == psstdout)) {
357 	check_estack(1);
358 	esp += 1;
359 	make_op_estack(esp, (ps == psstderr) ? zneedstderr : zneedstdout);
360     }
361     return o_push_estack;
362 }
363 /* Continue a write operation after returning from a procedure callout. */
364 /* osp[0] contains the file (pushed on the e-stack by handle_write_status); */
365 /* osp[-1] contains the new buffer string (pushed by the procedure). */
366 /* The top of the e-stack contains the real continuation. */
367 private int
368 s_proc_write_continue(i_ctx_t *i_ctx_p)
369 {
370     os_ptr op = osp;
371     os_ptr opbuf = op - 1;
372     stream *ps;
373     stream_proc_state *ss;
374 
375     check_file(ps, op);
376     check_write_type(*opbuf, t_string);
377     while (ps->strm != 0) {
378 	if (ps->end_status == CALLC)
379 	    ps->end_status = 0;
380 	ps = ps->strm;
381     }
382     ps->end_status = 0;
383     ss = (stream_proc_state *) ps->state;
384     ss->data = *opbuf;
385     ss->index = 0;
386     pop(2);
387     return 0;
388 }
389 
390 /* ------ More generic ------ */
391 
392 /* Test whether a stream is procedure-based. */
393 bool
394 s_is_proc(const stream *s)
395 {
396     return (s->procs.process == s_proc_read_process ||
397 	    s->procs.process == s_proc_write_process);
398 }
399 
400 /* ------ Initialization procedure ------ */
401 
402 const op_def zfproc_op_defs[] =
403 {
404 		/* Internal operators */
405     {"2%s_proc_read_continue", s_proc_read_continue},
406     {"2%s_proc_write_continue", s_proc_write_continue},
407     op_def_end(0)
408 };
409