xref: /plan9/sys/src/cmd/gs/src/zfrsd.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
1 /* Copyright (C) 1998, 2000 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: zfrsd.c,v 1.10 2004/08/04 19:36:13 stefan Exp $ */
18 /* ReusableStreamDecode filter support */
19 #include "memory_.h"
20 #include "ghost.h"
21 #include "gsfname.h"		/* for gs_parse_file_name */
22 #include "gxiodev.h"
23 #include "oper.h"
24 #include "stream.h"
25 #include "strimpl.h"
26 #include "sfilter.h"		/* for SubFileDecode */
27 #include "files.h"
28 #include "idict.h"
29 #include "idparam.h"
30 #include "iname.h"
31 #include "store.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 private 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;
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     if ((code = dict_int_param(op, "Intent", 0, 3, 0, &Intent)) < 0 ||
96 	(code = dict_bool_param(op, "AsyncRead", false, &AsyncRead)) < 0
97 	)
98 	return code;
99     push(1);
100     op[-1] = *pFilter;
101     if (pDecodeParms)
102 	*op = *pDecodeParms;
103     else
104 	make_null(op);
105     return 0;
106 }
107 
108 /* <file|string> <CloseSource> .reusablestream <filter> */
109 /*
110  * The file|string operand must be a "reusable source", either:
111  *      - A string or bytestring;
112  *      - A readable, positionable file stream;
113  *      - A readable string stream;
114  *      - A SubFileDecode filter with an empty EODString and a reusable
115  *      source.
116  * Reusable streams are also reusable sources, but they look just like
117  * ordinary file or string streams.
118  */
119 private int make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data,
120 		     uint size, int space, long offset, long length,
121 		     bool is_bytestring);
122 private int make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs,
123 		     long offset, long length);
124 private int
zreusablestream(i_ctx_t * i_ctx_p)125 zreusablestream(i_ctx_t *i_ctx_p)
126 {
127     os_ptr op = osp;
128     os_ptr source_op = op - 1;
129     long length = max_long;
130     bool close_source;
131     int code;
132 
133     check_type(*op, t_boolean);
134     close_source = op->value.boolval;
135     if (r_has_type(source_op, t_string)) {
136 	uint size = r_size(source_op);
137 
138 	check_read(*source_op);
139 	code = make_rss(i_ctx_p, source_op, source_op->value.const_bytes,
140 			size, r_space(source_op), 0L, size, false);
141     } else if (r_has_type(source_op, t_astruct)) {
142 	uint size = gs_object_size(imemory, source_op->value.pstruct);
143 
144 	if (gs_object_type(imemory, source_op->value.pstruct) != &st_bytes)
145 	    return_error(e_rangecheck);
146 	check_read(*source_op);
147 	code = make_rss(i_ctx_p, source_op,
148 			(const byte *)source_op->value.pstruct, size,
149 			r_space(source_op), 0L, size, true);
150     } else {
151 	long offset = 0;
152 	stream *source;
153 	stream *s;
154 
155 	check_read_file(source, source_op);
156 	s = source;
157 rs:
158 	if (s->cbuf_string.data != 0) {	/* string stream */
159 	    long pos = stell(s);
160 	    long avail = sbufavailable(s) + pos;
161 
162 	    offset += pos;
163 	    code = make_rss(i_ctx_p, source_op, s->cbuf_string.data,
164 			    s->cbuf_string.size,
165 			    imemory_space((const gs_ref_memory_t *)s->memory),
166 			    offset, min(avail, length), false);
167 	} else if (s->file != 0) { /* file stream */
168 	    if (~s->modes & (s_mode_read | s_mode_seek))
169 		return_error(e_ioerror);
170 	    code = make_rfs(i_ctx_p, source_op, s, offset + stell(s), length);
171 	} else if (s->state->template == &s_SFD_template) {
172 	    /* SubFileDecode filter */
173 	    const stream_SFD_state *const sfd_state =
174 		(const stream_SFD_state *)s->state;
175 
176 	    if (sfd_state->eod.size != 0)
177 		return_error(e_rangecheck);
178 	    offset += sfd_state->skip_count - sbufavailable(s);
179 	    if (sfd_state->count != 0) {
180 		long left = max(sfd_state->count, 0) + sbufavailable(s);
181 
182 		if (left < length)
183 		    length = left;
184 	    }
185 	    s = s->strm;
186 	    goto rs;
187 	}
188 	else			/* some other kind of stream */
189 	    return_error(e_rangecheck);
190 	if (close_source) {
191 	    stream *rs = fptr(source_op);
192 
193 	    rs->strm = source;	/* only for close_source */
194 	    rs->close_strm = true;
195 	}
196     }
197     if (code >= 0)
198 	pop(1);
199     return code;
200 }
201 
202 /* Make a reusable string stream. */
203 private int
make_rss(i_ctx_t * i_ctx_p,os_ptr op,const byte * data,uint size,int string_space,long offset,long length,bool is_bytestring)204 make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data, uint size,
205 	 int string_space, long offset, long length, bool is_bytestring)
206 {
207     stream *s;
208     long left = min(length, size - offset);
209 
210     if (icurrent_space < string_space)
211 	return_error(e_invalidaccess);
212     s = file_alloc_stream(imemory, "make_rss");
213     if (s == 0)
214 	return_error(e_VMerror);
215     sread_string_reusable(s, data + offset, max(left, 0));
216     if (is_bytestring)
217 	s->cbuf_string.data = 0;	/* byte array, not string */
218     make_stream_file(op, s, "r");
219     return 0;
220 }
221 
222 /* Make a reusable file stream. */
223 private int
make_rfs(i_ctx_t * i_ctx_p,os_ptr op,stream * fs,long offset,long length)224 make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs, long offset, long length)
225 {
226     gs_const_string fname;
227     gs_parsed_file_name_t pname;
228     stream *s;
229     int code;
230 
231     if (sfilename(fs, &fname) < 0)
232 	return_error(e_ioerror);
233     code = gs_parse_file_name(&pname, (const char *)fname.data, fname.size);
234     if (code < 0)
235 	return code;
236     if (pname.len == 0)		/* %stdin% etc. won't have a filename */
237 	return_error(e_invalidfileaccess); /* can't reopen */
238     if (pname.iodev == NULL)
239 	pname.iodev = iodev_default;
240     /* Open the file again, to be independent of the source. */
241     code = file_open_stream((const char *)pname.fname, pname.len, "r",
242 			    fs->cbsize, &s, pname.iodev,
243 			    pname.iodev->procs.fopen, imemory);
244     if (code < 0)
245 	return code;
246     if (sread_subfile(s, offset, length) < 0) {
247 	sclose(s);
248 	return_error(e_ioerror);
249     }
250     s->close_at_eod = false;
251     make_stream_file(op, s, "r");
252     return 0;
253 }
254 
255 /* ---------------- Initialization procedure ---------------- */
256 
257 const op_def zfrsd_op_defs[] =
258 {
259     {"2.reusablestream", zreusablestream},
260     {"2.rsdparams", zrsdparams},
261     op_def_end(0)
262 };
263