1 /* Copyright (C) 2001-2006 Artifex Software, Inc.
2    All Rights Reserved.
3 
4    This software is provided AS-IS with no warranty, either express or
5    implied.
6 
7    This software is distributed under license and may not be copied, modified
8    or distributed except as expressly authorized under the terms of that
9    license.  Refer to licensing information at http://www.artifex.com/
10    or contact Artifex Software, Inc.,  7 Mt. Lassen Drive - Suite A-134,
11    San Rafael, CA  94903, U.S.A., +1(415)492-9861, for further information.
12 */
13 
14 /* $Id: zfrsd.c 10563 2009-12-28 07:03:59Z alexcher $ */
15 /* ReusableStreamDecode filter support */
16 #include "memory_.h"
17 #include "ghost.h"
18 #include "gsfname.h"		/* for gs_parse_file_name */
19 #include "gxiodev.h"
20 #include "oper.h"
21 #include "stream.h"
22 #include "strimpl.h"
23 #include "sfilter.h"		/* for SubFileDecode */
24 #include "files.h"
25 #include "idict.h"
26 #include "idparam.h"
27 #include "iname.h"
28 #include "istruct.h"
29 #include "store.h"
30 #include "zfile.h"
31 #include "zfrsd.h"
32 
33 /* ---------------- Reusable streams ---------------- */
34 
35 /*
36  * The actual work of constructing the filter is done in PostScript code.
37  * The operators in this file are internal ones that handle the dirty work.
38  */
39 
40 /* <dict|null> .rsdparams <filters> <decodeparms|null> */
41 /* filters is always an array; decodeparms is always either an array */
42 /* of the same length as filters, or null. */
43 static int
zrsdparams(i_ctx_t * i_ctx_p)44 zrsdparams(i_ctx_t *i_ctx_p)
45 {
46     os_ptr op = osp;
47     ref *pFilter;
48     ref *pDecodeParms;
49     int Intent = 0;
50     bool AsyncRead;
51     ref empty_array, filter1_array, parms1_array;
52     uint i;
53     int code;
54 
55     make_empty_array(&empty_array, a_readonly);
56     if (dict_find_string(op, "Filter", &pFilter) > 0) {
57 	if (!r_is_array(pFilter)) {
58 	    if (!r_has_type(pFilter, t_name))
59 		return_error(e_typecheck);
60 	    make_array(&filter1_array, a_readonly, 1, pFilter);
61 	    pFilter = &filter1_array;
62 	}
63     } else
64 	pFilter = &empty_array;
65     /* If Filter is undefined, ignore DecodeParms. */
66     if (pFilter != &empty_array &&
67 	dict_find_string(op, "DecodeParms", &pDecodeParms) > 0
68 	) {
69 	if (pFilter == &filter1_array) {
70 	    make_array(&parms1_array, a_readonly, 1, pDecodeParms);
71 	    pDecodeParms = &parms1_array;
72 	} else if (!r_is_array(pDecodeParms))
73 	    return_error(e_typecheck);
74 	else if (r_size(pFilter) != r_size(pDecodeParms))
75 	    return_error(e_rangecheck);
76     } else
77 	pDecodeParms = 0;
78     for (i = 0; i < r_size(pFilter); ++i) {
79 	ref f, fname, dp;
80 
81 	array_get(imemory, pFilter, (long)i, &f);
82 	if (!r_has_type(&f, t_name))
83 	    return_error(e_typecheck);
84 	name_string_ref(imemory, &f, &fname);
85 	if (r_size(&fname) < 6 ||
86 	    memcmp(fname.value.bytes + r_size(&fname) - 6, "Decode", 6)
87 	    )
88 	    return_error(e_rangecheck);
89 	if (pDecodeParms) {
90 	    array_get(imemory, pDecodeParms, (long)i, &dp);
91 	    if (!(r_has_type(&dp, t_dictionary) || r_has_type(&dp, t_null)))
92 		return_error(e_typecheck);
93 	}
94     }
95     code = dict_int_param(op, "Intent", 0, 3, 0, &Intent);
96     if (code < 0 && code != e_rangecheck) /* out-of-range int is ok, use 0 */
97 	return code;
98     if ((code = dict_bool_param(op, "AsyncRead", false, &AsyncRead)) < 0
99 	)
100 	return code;
101     push(1);
102     op[-1] = *pFilter;
103     if (pDecodeParms)
104 	*op = *pDecodeParms;
105     else
106 	make_null(op);
107     return 0;
108 }
109 
110 /* <file|string> <CloseSource> .reusablestream <filter> */
111 /*
112  * The file|string operand must be a "reusable source", either:
113  *      - A string or bytestring;
114  *      - An array of strings;
115  *      - A readable, positionable file stream;
116  *      - A readable string stream;
117  *      - A SubFileDecode filter with an empty EODString and a reusable
118  *      source.
119  * Reusable streams are also reusable sources, but they look just like
120  * ordinary file or string streams.
121  */
122 static int make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs,
123 		     long offset, long length);
124 
125 static int make_aos(i_ctx_t *i_ctx_p, os_ptr op,
126                     int blk_sz, int blk_sz_last, unsigned int file_sz);
127 
128 static int
zreusablestream(i_ctx_t * i_ctx_p)129 zreusablestream(i_ctx_t *i_ctx_p)
130 {
131     os_ptr op = osp;
132     os_ptr source_op = op - 1;
133     long length = max_long;
134     bool close_source;
135     int code;
136 
137     check_type(*op, t_boolean);
138     close_source = op->value.boolval;
139     if (r_has_type(source_op, t_string)) {
140 	uint size = r_size(source_op);
141 
142 	check_read(*source_op);
143 	code = make_rss(i_ctx_p, source_op, source_op->value.const_bytes,
144 			size, r_space(source_op), 0L, size, false);
145     } else if (r_has_type(source_op, t_astruct)) {
146 	uint size = gs_object_size(imemory, source_op->value.pstruct);
147 
148 	if (gs_object_type(imemory, source_op->value.pstruct) != &st_bytes)
149 	    return_error(e_rangecheck);
150 	check_read(*source_op);
151 	code = make_rss(i_ctx_p, source_op,
152 			(const byte *)source_op->value.pstruct, size,
153 			r_space(source_op), 0L, size, true);
154     } else if (r_has_type(source_op, t_array)) {  /* no packedarrays */
155 	int i, blk_cnt, blk_sz;
156         ref *blk_ref;
157         ulong filelen = 0;
158 
159         check_read(*source_op);
160         blk_cnt = r_size(source_op);
161 	blk_ref = source_op->value.refs;
162         if (blk_cnt > 0) {
163             blk_sz = r_size(blk_ref);
164             for (i = 0; i < blk_cnt; i++) {
165                 int len;
166 
167                 check_read_type(blk_ref[i], t_string);
168                 len = r_size(&blk_ref[i]);
169                 if (len > blk_sz || (len < blk_sz && i < blk_cnt - 1))
170                    return_error(e_rangecheck); /* last block can be smaller */
171                 filelen += len;
172             }
173         }
174         if (filelen == 0) {
175            code = make_rss(i_ctx_p, source_op, (unsigned char *)"", 0,
176 	       r_space(source_op), 0, 0, false);
177         } else {
178            code = make_aos(i_ctx_p, source_op, blk_sz, r_size(&blk_ref[blk_cnt - 1]), filelen);
179         }
180     } else {
181 	long offset = 0;
182 	stream *source;
183 	stream *s;
184 
185 	check_read_file(source, source_op);
186 	s = source;
187 rs:
188 	if (s->cbuf_string.data != 0) {	/* string stream */
189 	    long pos = stell(s);
190 	    long avail = sbufavailable(s) + pos;
191 
192 	    offset += pos;
193 	    code = make_rss(i_ctx_p, source_op, s->cbuf_string.data,
194 			    s->cbuf_string.size,
195 			    imemory_space((const gs_ref_memory_t *)s->memory),
196 			    offset, min(avail, length), false);
197 	} else if (s->file != 0) { /* file stream */
198 	    if (~s->modes & (s_mode_read | s_mode_seek))
199 		return_error(e_ioerror);
200 	    code = make_rfs(i_ctx_p, source_op, s, offset + stell(s), length);
201 	} else if (s->state->template == &s_SFD_template) {
202 	    /* SubFileDecode filter */
203 	    const stream_SFD_state *const sfd_state =
204 		(const stream_SFD_state *)s->state;
205 
206 	    if (sfd_state->eod.size != 0)
207 		return_error(e_rangecheck);
208 	    offset += sfd_state->skip_count - sbufavailable(s);
209 	    if (sfd_state->count != 0) {
210 		long left = max(sfd_state->count, 0) + sbufavailable(s);
211 
212 		if (left < length)
213 		    length = left;
214 	    }
215 	    s = s->strm;
216 	    goto rs;
217 	}
218 	else			/* some other kind of stream */
219 	    return_error(e_rangecheck);
220 	if (close_source) {
221 	    stream *rs = fptr(source_op);
222 
223 	    rs->strm = source;	/* only for close_source */
224 	    rs->close_strm = true;
225 	}
226     }
227     if (code >= 0)
228 	pop(1);
229     return code;
230 }
231 
232 /* Make a reusable string stream. */
233 int
make_rss(i_ctx_t * i_ctx_p,os_ptr op,const byte * data,uint size,uint string_space,long offset,long length,bool is_bytestring)234 make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data, uint size,
235 	 uint string_space, long offset, long length, bool is_bytestring)
236 {
237     uint save_space = icurrent_space;
238     stream *s;
239     long left = min(length, size - offset);
240 
241     ialloc_set_space(idmemory, string_space);
242     s = file_alloc_stream(imemory, "make_rss");
243     ialloc_set_space(idmemory, save_space);
244     if (s == 0)
245 	return_error(e_VMerror);
246     sread_string_reusable(s, data + offset, max(left, 0));
247     if (is_bytestring)
248 	s->cbuf_string.data = 0;	/* byte array, not string */
249     make_stream_file(op, s, "r");
250     return 0;
251 }
252 
253 /* Make a reusable file stream. */
254 static int
make_rfs(i_ctx_t * i_ctx_p,os_ptr op,stream * fs,long offset,long length)255 make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs, long offset, long length)
256 {
257     uint save_space = icurrent_space;
258     uint stream_space = imemory_space((const gs_ref_memory_t *)fs->memory);
259     gs_const_string fname;
260     gs_parsed_file_name_t pname;
261     stream *s;
262     int code;
263 
264     if (sfilename(fs, &fname) < 0)
265 	return_error(e_ioerror);
266     code = gs_parse_file_name(&pname, (const char *)fname.data, fname.size);
267     if (code < 0)
268 	return code;
269     if (pname.len == 0)		/* %stdin% etc. won't have a filename */
270 	return_error(e_invalidfileaccess); /* can't reopen */
271     if (pname.iodev == NULL)
272 	pname.iodev = iodev_default;
273     /* Open the file again, to be independent of the source. */
274     ialloc_set_space(idmemory, stream_space);
275     code = zopen_file(i_ctx_p, &pname, "r", &s, imemory);
276     ialloc_set_space(idmemory, save_space);
277     if (code < 0)
278 	return code;
279     if (sread_subfile(s, offset, length) < 0) {
280 	sclose(s);
281 	return_error(e_ioerror);
282     }
283     s->close_at_eod = false;
284     make_stream_file(op, s, "r");
285     return 0;
286 }
287 /* ----------- Reusable array-of-strings stream ------------- */
288 
289 static int  s_aos_available(stream *, long *);
290 static int  s_aos_seek(stream *, long);
291 static void s_aos_reset(stream *s);
292 static int  s_aos_flush(stream *s);
293 static int  s_aos_close(stream *);
294 static int  s_aos_process(stream_state *, stream_cursor_read *,
295 	                stream_cursor_write *, bool);
296 
297 /* Stream state */
298 typedef struct aos_state_s {
299     stream_state_common;
300     ref   blocks;
301     stream *s;
302     int   blk_sz;
303     int   blk_sz_last;
304     uint  file_sz;
305 } aos_state_t;
306 
307 /* GC procedures */
308 static
CLEAR_MARKS_PROC(aos_clear_marks)309 CLEAR_MARKS_PROC(aos_clear_marks)
310 {   aos_state_t *const pptr = vptr;
311 
312     r_clear_attrs(&pptr->blocks, l_mark);
313 }
314 static
315 ENUM_PTRS_WITH(aos_enum_ptrs, aos_state_t *pptr) return 0;
316 ENUM_PTR(0, aos_state_t, s);
317 case 1:
318 ENUM_RETURN_REF(&pptr->blocks);
319 ENUM_PTRS_END
320 static RELOC_PTRS_WITH(aos_reloc_ptrs, aos_state_t *pptr);
321 RELOC_PTR(aos_state_t, s);
322 RELOC_REF_VAR(pptr->blocks);
323 r_clear_attrs(&pptr->blocks, l_mark);
324 RELOC_PTRS_END
325 
326 gs_private_st_complex_only(st_aos_state, aos_state_t,
327     "aos_state", aos_clear_marks, aos_enum_ptrs, aos_reloc_ptrs, 0);
328 
329 /* Stream template */
330 static const stream_template s_aos_template = {
331      &st_aos_state, 0, s_aos_process, 1, 1, 0, 0 };
332 
333 /* Stream procs */
334 static const stream_procs s_aos_procs = {
335      s_aos_available, s_aos_seek, s_aos_reset,
336      s_aos_flush, s_aos_close, s_aos_process,
337      NULL		/* no s_aos_switch */
338 };
339 
340 static int
make_aos(i_ctx_t * i_ctx_p,os_ptr op,int blk_sz,int blk_sz_last,uint file_sz)341 make_aos(i_ctx_t *i_ctx_p, os_ptr op, int blk_sz, int blk_sz_last, uint file_sz)
342 {
343     stream *s;
344     aos_state_t *ss;
345     byte *buf;
346     const int aos_buf_size = 1024; /* arbitrary */
347     uint save_space = icurrent_space;
348     ialloc_set_space(idmemory, r_space(op));
349 
350     s = s_alloc(imemory, "aos_stream");
351     ss = (aos_state_t *)s_alloc_state(imemory, &st_aos_state, "st_aos_state");
352     buf = gs_alloc_bytes(imemory, aos_buf_size, "aos_stream_buf");
353     if (s == 0 || ss == 0 || buf == 0) {
354         gs_free_object(imemory, buf, "aos_stream_buf");
355         gs_free_object(imemory, ss, "st_aos_state");
356         gs_free_object(imemory, s, "aos_stream");
357         ialloc_set_space(idmemory, save_space);
358         return_error(e_VMerror);
359     }
360     ialloc_set_space(idmemory, save_space);
361     ss->template = &s_aos_template;
362     ss->blocks = *op;
363     ss->s = s;
364     ss->blk_sz = blk_sz;
365     ss->blk_sz_last = blk_sz_last;
366     ss->file_sz = file_sz;
367     s_std_init(s, buf, aos_buf_size, &s_aos_procs, s_mode_read + s_mode_seek);
368     s->state = (stream_state *)ss;
369     s->file_offset = 0;
370     s->file_limit = max_long;
371     s->close_at_eod = false;
372     s->read_id = 1;
373     make_stream_file(op, s, "r");
374     return 0;
375 }
376 
377 /* Return the number of available bytes */
378 static int
s_aos_available(stream * s,long * pl)379 s_aos_available(stream *s, long *pl)
380 {
381     *pl = ((aos_state_t *)s->state)->file_sz - stell(s);
382     return 0;
383 }
384 
385 /* Seek in a string being read.  Return 0 if OK, ERRC if not. */
386 static int
s_aos_seek(register stream * s,long pos)387 s_aos_seek(register stream * s, long pos)
388 {
389     uint end = s->srlimit - s->cbuf + 1;
390     long offset = pos - s->position;
391 
392     if (offset >= 0 && offset <= end) {  /* Staying within the same buffer */
393 	s->srptr = s->cbuf + offset - 1;
394 	return 0;
395     }
396     if (pos < 0 || pos > s->file_limit)
397 	return ERRC;
398     s->srptr = s->srlimit = s->cbuf - 1;
399     s->end_status = 0;
400     s->position = pos;
401     return 0;
402 }
403 
404 static void
s_aos_reset(stream * s)405 s_aos_reset(stream *s)
406 {
407     /* PLRM definition of reset operator is strange. */
408     /* Rewind the file and discard the buffer. */
409     s->position = 0;
410     s->srptr = s->srlimit = s->cbuf - 1;
411     s->end_status = 0;
412 }
413 
414 static int
s_aos_flush(stream * s)415 s_aos_flush(stream *s)
416 {
417     s->position = ((aos_state_t *)s->state)->file_sz;
418     s->srptr = s->srlimit = s->cbuf - 1;
419     return 0;
420 }
421 
422 static int
s_aos_close(stream * s)423 s_aos_close(stream * s)
424 {
425     gs_free_object(s->memory, s->cbuf, "s_aos_close(buffer)");
426     s->cbuf = 0;
427     /* Increment the IDs to prevent further access. */
428     s->read_id = s->write_id = (s->read_id | s->write_id) + 1;
429     return 0;
430 }
431 
432 static int
s_aos_process(stream_state * st,stream_cursor_read * ignore_pr,stream_cursor_write * pw,bool last)433 s_aos_process(stream_state * st, stream_cursor_read * ignore_pr,
434 		      stream_cursor_write * pw, bool last)
435 {
436     int blk_i, blk_off, blk_cnt, status = 1;
437     uint count;
438     aos_state_t *ss = (aos_state_t *)st;
439     uint max_count = pw->limit - pw->ptr;
440     uint pos = stell(ss->s);
441     unsigned const char *data;
442     ref *blk_ref;
443 
444     if (pos >= ss->file_sz)
445 	return EOFC;
446     pos += sbufavailable(ss->s);
447     blk_i   = pos / ss->blk_sz;
448     blk_off = pos % ss->blk_sz;
449     blk_cnt = r_size(&ss->blocks);
450     count = blk_i < blk_cnt - 1 ? ss->blk_sz : ss->blk_sz_last;
451     blk_ref = ss->blocks.value.refs;
452     data = blk_ref[blk_i].value.bytes;
453 
454     if (max_count > count - blk_off) {
455         max_count = count - blk_off;
456         if (blk_i == blk_cnt - 1)
457 	     status = EOFC;
458     }
459     memcpy(pw->ptr+1, data + blk_off, max_count);
460     pw->ptr += max_count;
461     return status;
462 }
463 
464 /* ---------------- Initialization procedure ---------------- */
465 
466 const op_def zfrsd_op_defs[] =
467 {
468     {"2.reusablestream", zreusablestream},
469     {"2.rsdparams", zrsdparams},
470     op_def_end(0)
471 };
472