/* Copyright (C) 1994, 1995, 1997, 1998, 1999, 2001 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: zfproc.c,v 1.9.2.1.2.1 2003/01/17 00:49:06 giles Exp $ */ /* Procedure-based filter stream support */ #include "memory_.h" #include "ghost.h" #include "oper.h" /* for ifilter.h */ #include "estack.h" #include "gsstruct.h" #include "ialloc.h" #include "istruct.h" /* for RELOC_REF_VAR */ #include "stream.h" #include "strimpl.h" #include "ifilter.h" #include "files.h" #include "store.h" /* ---------------- Generic ---------------- */ /* GC procedures */ private CLEAR_MARKS_PROC(sproc_clear_marks) { stream_proc_state *const pptr = vptr; r_clear_attrs(&pptr->proc, l_mark); r_clear_attrs(&pptr->data, l_mark); } private ENUM_PTRS_WITH(sproc_enum_ptrs, stream_proc_state *pptr) return 0; case 0: ENUM_RETURN_REF(&pptr->proc); case 1: ENUM_RETURN_REF(&pptr->data); ENUM_PTRS_END private RELOC_PTRS_WITH(sproc_reloc_ptrs, stream_proc_state *pptr); RELOC_REF_VAR(pptr->proc); r_clear_attrs(&pptr->proc, l_mark); RELOC_REF_VAR(pptr->data); r_clear_attrs(&pptr->data, l_mark); RELOC_PTRS_END /* Structure type for procedure-based streams. */ private_st_stream_proc_state(); /* Allocate and open a procedure-based filter. */ /* The caller must have checked that *sop is a procedure. */ private int s_proc_init(ref * sop, stream ** psstrm, uint mode, const stream_template * temp, const stream_procs * procs, gs_ref_memory_t *imem) { gs_memory_t *const mem = (gs_memory_t *)imem; stream *sstrm = file_alloc_stream(mem, "s_proc_init(stream)"); stream_proc_state *state = (stream_proc_state *) s_alloc_state(mem, &st_sproc_state, "s_proc_init(state)"); if (sstrm == 0 || state == 0) { gs_free_object(mem, state, "s_proc_init(state)"); /*gs_free_object(mem, sstrm, "s_proc_init(stream)"); *//* just leave it on the file list */ return_error(e_VMerror); } s_std_init(sstrm, NULL, 0, procs, mode); sstrm->procs.process = temp->process; state->template = temp; state->memory = mem; state->eof = 0; state->proc = *sop; make_empty_string(&state->data, a_all); state->index = 0; sstrm->state = (stream_state *) state; *psstrm = sstrm; return 0; } /* Handle an interrupt during a stream operation. */ /* This is logically unrelated to procedure streams, */ /* but it is also associated with the interpreter stream machinery. */ private int s_handle_intc(i_ctx_t *i_ctx_p, const ref *pstate, int nstate, op_proc_t cont) { int npush = nstate + 2; check_estack(npush); if (nstate) memcpy(esp + 2, pstate, nstate * sizeof(ref)); #if 0 /* **************** */ { int code = gs_interpret_error(e_interrupt, (ref *) (esp + npush)); if (code < 0) return code; } #else /* **************** */ npush--; #endif /* **************** */ make_op_estack(esp + 1, cont); esp += npush; return o_push_estack; } /* Set default parameter values (actually, just clear pointers). */ private void s_proc_set_defaults(stream_state * st) { stream_proc_state *const ss = (stream_proc_state *) st; make_null(&ss->proc); make_null(&ss->data); } /* ---------------- Read streams ---------------- */ /* Forward references */ private stream_proc_process(s_proc_read_process); private int s_proc_read_continue(P1(i_ctx_t *)); /* Stream templates */ private const stream_template s_proc_read_template = { &st_sproc_state, NULL, s_proc_read_process, 1, 1, NULL, s_proc_set_defaults }; private const stream_procs s_proc_read_procs = { s_std_noavailable, s_std_noseek, s_std_read_reset, s_std_read_flush, s_std_null, NULL }; /* Allocate and open a procedure-based read stream. */ /* The caller must have checked that *sop is a procedure. */ int sread_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem) { int code = s_proc_init(sop, psstrm, s_mode_read, &s_proc_read_template, &s_proc_read_procs, imem); if (code < 0) return code; (*psstrm)->end_status = CALLC; return code; } /* Handle an input request. */ private int s_proc_read_process(stream_state * st, stream_cursor_read * ignore_pr, stream_cursor_write * pw, bool last) { /* Move data from the string returned by the procedure */ /* into the stream buffer, or ask for a callback. */ stream_proc_state *const ss = (stream_proc_state *) st; uint count = r_size(&ss->data) - ss->index; if (count > 0) { uint wcount = pw->limit - pw->ptr; if (wcount < count) count = wcount; memcpy(pw->ptr + 1, ss->data.value.bytes + ss->index, count); pw->ptr += count; ss->index += count; return 1; } return (ss->eof ? EOFC : CALLC); } /* Handle an exception (INTC or CALLC) from a read stream */ /* whose buffer is empty. */ 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) { int npush = nstate + 4; stream *ps; stream *psstdin; switch (status) { case INTC: return s_handle_intc(i_ctx_p, pstate, nstate, cont); case CALLC: break; default: return_error(e_ioerror); } /* Find the stream whose buffer needs refilling. */ for (ps = fptr(fop); ps->strm != 0;) ps = ps->strm; check_estack(npush); if (nstate) memcpy(esp + 2, pstate, nstate * sizeof(ref)); make_op_estack(esp + 1, cont); esp += npush; make_op_estack(esp - 2, s_proc_read_continue); esp[-1] = *fop; r_clear_attrs(esp - 1, a_executable); *esp = ((stream_proc_state *) ps->state)->proc; /* If stream is stdin, ask for callout. */ zget_stdin(i_ctx_p, &psstdin); if (ps == psstdin) { check_estack(1); esp += 1; make_op_estack(esp, zneedstdin); } return o_push_estack; } /* Continue a read operation after returning from a procedure callout. */ /* osp[0] contains the file (pushed on the e-stack by handle_read_status); */ /* osp[-1] contains the new data string (pushed by the procedure). */ /* The top of the e-stack contains the real continuation. */ private int s_proc_read_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr opbuf = op - 1; stream *ps; stream_proc_state *ss; check_file(ps, op); check_read_type(*opbuf, t_string); while ((ps->end_status = 0, ps->strm) != 0) ps = ps->strm; ss = (stream_proc_state *) ps->state; ss->data = *opbuf; ss->index = 0; if (r_size(opbuf) == 0) ss->eof = true; pop(2); return 0; } /* ---------------- Write streams ---------------- */ /* Forward references */ private stream_proc_flush(s_proc_write_flush); private stream_proc_process(s_proc_write_process); private int s_proc_write_continue(P1(i_ctx_t *)); /* Stream templates */ private const stream_template s_proc_write_template = { &st_sproc_state, NULL, s_proc_write_process, 1, 1, NULL, s_proc_set_defaults }; private const stream_procs s_proc_write_procs = { s_std_noavailable, s_std_noseek, s_std_write_reset, s_proc_write_flush, s_std_null, NULL }; /* Allocate and open a procedure-based write stream. */ /* The caller must have checked that *sop is a procedure. */ int swrite_proc(ref * sop, stream ** psstrm, gs_ref_memory_t *imem) { return s_proc_init(sop, psstrm, s_mode_write, &s_proc_write_template, &s_proc_write_procs, imem); } /* Handle an output request. */ private int s_proc_write_process(stream_state * st, stream_cursor_read * pr, stream_cursor_write * ignore_pw, bool last) { /* Move data from the stream buffer to the string */ /* returned by the procedure, or ask for a callback. */ stream_proc_state *const ss = (stream_proc_state *) st; uint rcount = pr->limit - pr->ptr; if (rcount > 0) { uint wcount = r_size(&ss->data) - ss->index; uint count = min(rcount, wcount); memcpy(ss->data.value.bytes + ss->index, pr->ptr + 1, count); pr->ptr += count; ss->index += count; if (rcount > wcount) return CALLC; else if (last) { ss->eof = true; return CALLC; } else return 0; } return ((ss->eof = last) ? EOFC : 0); } /* Flush the output. This is non-standard because it must call the */ /* procedure. */ private int s_proc_write_flush(stream *s) { int result = s_process_write_buf(s, false); stream_proc_state *const ss = (stream_proc_state *)s->state; return (result < 0 || ss->index == 0 ? result : CALLC); } /* Handle an exception (INTC or CALLC) from a write stream */ /* whose buffer is full. */ 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) { stream *ps; stream *psstderr; stream *psstdout; stream_proc_state *psst; switch (status) { case INTC: return s_handle_intc(i_ctx_p, pstate, nstate, cont); case CALLC: break; default: return_error(e_ioerror); } /* Find the stream whose buffer needs emptying. */ for (ps = fptr(fop); ps->strm != 0;) ps = ps->strm; psst = (stream_proc_state *) ps->state; { int npush = nstate + 6; check_estack(npush); if (nstate) memcpy(esp + 2, pstate, nstate * sizeof(ref)); make_op_estack(esp + 1, cont); esp += npush; make_op_estack(esp - 4, s_proc_write_continue); esp[-3] = *fop; r_clear_attrs(esp - 3, a_executable); make_bool(esp - 1, !psst->eof); } esp[-2] = psst->proc; *esp = psst->data; r_set_size(esp, psst->index); /* If stream is stdout/err, ask for callout. */ zget_stdout(i_ctx_p, &psstdout); zget_stderr(i_ctx_p, &psstderr); if ((ps == psstderr) || (ps == psstdout)) { check_estack(1); esp += 1; make_op_estack(esp, (ps == psstderr) ? zneedstderr : zneedstdout); } return o_push_estack; } /* Continue a write operation after returning from a procedure callout. */ /* osp[0] contains the file (pushed on the e-stack by handle_write_status); */ /* osp[-1] contains the new buffer string (pushed by the procedure). */ /* The top of the e-stack contains the real continuation. */ private int s_proc_write_continue(i_ctx_t *i_ctx_p) { os_ptr op = osp; os_ptr opbuf = op - 1; stream *ps; stream_proc_state *ss; check_file(ps, op); check_write_type(*opbuf, t_string); while (ps->strm != 0) { if (ps->end_status == CALLC) ps->end_status = 0; ps = ps->strm; } ps->end_status = 0; ss = (stream_proc_state *) ps->state; ss->data = *opbuf; ss->index = 0; pop(2); return 0; } /* ------ More generic ------ */ /* Test whether a stream is procedure-based. */ bool s_is_proc(const stream *s) { return (s->procs.process == s_proc_read_process || s->procs.process == s_proc_write_process); } /* ------ Initialization procedure ------ */ const op_def zfproc_op_defs[] = { /* Internal operators */ {"2%s_proc_read_continue", s_proc_read_continue}, {"2%s_proc_write_continue", s_proc_write_continue}, op_def_end(0) };