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 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 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 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 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 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 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 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 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 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 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 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 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 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 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