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