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