/src/ghostpdl/psi/zfrsd.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2023 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., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, 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 | 248 | { |
48 | 248 | os_ptr op = osp; |
49 | 248 | ref *pFilter; |
50 | 248 | ref *pDecodeParms; |
51 | 248 | int Intent = 0; |
52 | 248 | bool AsyncRead = false; |
53 | 248 | ref empty_array, filter1_array, parms1_array; |
54 | 248 | uint i; |
55 | 248 | int code = 0; |
56 | | |
57 | 248 | check_op(1); |
58 | 248 | if (!r_has_type(op, t_dictionary) && !r_has_type(op, t_null)) { |
59 | 0 | return_error(gs_error_typecheck); |
60 | 0 | } |
61 | | |
62 | 248 | make_empty_array(&empty_array, a_readonly); |
63 | 248 | if (r_has_type(op, t_dictionary) |
64 | 248 | && 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 | 248 | pFilter = &empty_array; |
73 | | /* If Filter is undefined, ignore DecodeParms. */ |
74 | 248 | if (pFilter != &empty_array && |
75 | 248 | dict_find_string(op, "DecodeParms", &pDecodeParms) > 0 |
76 | 248 | ) { |
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 | 248 | pDecodeParms = 0; |
86 | 248 | 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 | 248 | if (r_has_type(op, t_dictionary)) |
104 | 248 | code = dict_int_param(op, "Intent", 0, 3, 0, &Intent); |
105 | 248 | if (code < 0 && code != gs_error_rangecheck) /* out-of-range int is ok, use 0 */ |
106 | 0 | return code; |
107 | 248 | if (r_has_type(op, t_dictionary)) |
108 | 248 | if ((code = dict_bool_param(op, "AsyncRead", false, &AsyncRead)) < 0) |
109 | 0 | return code; |
110 | 248 | push(1); |
111 | 248 | op[-1] = *pFilter; |
112 | 248 | if (pDecodeParms) |
113 | 0 | *op = *pDecodeParms; |
114 | 248 | else |
115 | 248 | make_null(op); |
116 | 248 | return 0; |
117 | 248 | } |
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 | 248 | { |
140 | 248 | os_ptr op = osp; |
141 | 248 | os_ptr source_op = op - 1; |
142 | 248 | long length = max_long; |
143 | 248 | bool close_source; |
144 | 248 | int code; |
145 | | |
146 | 248 | check_op(2); |
147 | 248 | check_type(*op, t_boolean); |
148 | 248 | close_source = op->value.boolval; |
149 | 248 | if (r_has_type(source_op, t_string)) { |
150 | 0 | uint size = r_size(source_op); |
151 | |
|
152 | 0 | check_read(*source_op); |
153 | 0 | code = make_rss(i_ctx_p, source_op, source_op->value.const_bytes, |
154 | 0 | size, r_space(source_op), 0L, size, false); |
155 | 248 | } else if (r_has_type(source_op, t_astruct)) { |
156 | 0 | uint size = gs_object_size(imemory, source_op->value.pstruct); |
157 | |
|
158 | 0 | if (gs_object_type(imemory, source_op->value.pstruct) != &st_bytes) |
159 | 0 | return_error(gs_error_rangecheck); |
160 | 0 | check_read(*source_op); |
161 | 0 | code = make_rss(i_ctx_p, source_op, |
162 | 0 | (const byte *)source_op->value.pstruct, size, |
163 | 0 | r_space(source_op), 0L, size, true); |
164 | 248 | } else if (r_has_type(source_op, t_array)) { /* no packedarrays */ |
165 | 248 | int i, blk_cnt, blk_sz; |
166 | 248 | ref *blk_ref; |
167 | 248 | ulong filelen = 0; |
168 | | |
169 | 248 | check_read(*source_op); |
170 | 248 | blk_cnt = r_size(source_op); |
171 | 248 | blk_ref = source_op->value.refs; |
172 | 248 | if (blk_cnt > 0) { |
173 | 248 | blk_sz = r_size(blk_ref); |
174 | 496 | for (i = 0; i < blk_cnt; i++) { |
175 | 248 | int len; |
176 | | |
177 | 248 | check_read_type(blk_ref[i], t_string); |
178 | 248 | len = r_size(&blk_ref[i]); |
179 | 248 | if (len > blk_sz || (len < blk_sz && i < blk_cnt - 1)) |
180 | 0 | return_error(gs_error_rangecheck); /* last block can be smaller */ |
181 | 248 | filelen += len; |
182 | 248 | } |
183 | 248 | } |
184 | 248 | if (filelen == 0) { |
185 | 65 | code = make_rss(i_ctx_p, source_op, (unsigned char *)"", 0, |
186 | 65 | r_space(source_op), 0, 0, false); |
187 | 183 | } else { |
188 | 183 | code = make_aos(i_ctx_p, source_op, blk_sz, r_size(&blk_ref[blk_cnt - 1]), filelen); |
189 | 183 | } |
190 | 248 | } else { |
191 | 0 | long offset = 0; |
192 | 0 | stream *source; |
193 | 0 | stream *s; |
194 | |
|
195 | 0 | check_read_file(i_ctx_p, source, source_op); |
196 | 0 | s = source; |
197 | 0 | rs: |
198 | 0 | if (s->cbuf_string.data != 0) { /* string stream */ |
199 | 0 | long pos = stell(s); |
200 | 0 | long avail = sbufavailable(s) + pos; |
201 | |
|
202 | 0 | offset += pos; |
203 | 0 | code = make_rss(i_ctx_p, source_op, s->cbuf_string.data, |
204 | 0 | s->cbuf_string.size, |
205 | 0 | imemory_space((const gs_ref_memory_t *)s->memory), |
206 | 0 | offset, min(avail, length), false); |
207 | 0 | } else if (s->file != 0) { /* file stream */ |
208 | 0 | if (~s->modes & (s_mode_read | s_mode_seek)) |
209 | 0 | return_error(gs_error_ioerror); |
210 | 0 | code = make_rfs(i_ctx_p, source_op, s, offset + stell(s), length); |
211 | 0 | } else if (s->state->templat == &s_SFD_template) { |
212 | | /* SubFileDecode filter */ |
213 | 0 | const stream_SFD_state *const sfd_state = |
214 | 0 | (const stream_SFD_state *)s->state; |
215 | |
|
216 | 0 | if (sfd_state->eod.size != 0) |
217 | 0 | return_error(gs_error_rangecheck); |
218 | 0 | offset += sfd_state->skip_count - sbufavailable(s); |
219 | 0 | if (sfd_state->count != 0) { |
220 | 0 | long left = max(sfd_state->count, 0) + sbufavailable(s); |
221 | |
|
222 | 0 | if (left < length) |
223 | 0 | length = left; |
224 | 0 | } |
225 | 0 | s = s->strm; |
226 | 0 | goto rs; |
227 | 0 | } |
228 | 0 | else /* some other kind of stream */ |
229 | 0 | return_error(gs_error_rangecheck); |
230 | 0 | if (close_source) { |
231 | 0 | stream *rs = fptr(source_op); |
232 | |
|
233 | 0 | rs->strm = source; /* only for close_source */ |
234 | 0 | rs->close_strm = true; |
235 | 0 | } |
236 | 0 | } |
237 | 248 | if (code >= 0) |
238 | 248 | pop(1); |
239 | 248 | return code; |
240 | 248 | } |
241 | | |
242 | | /* Make a reusable string stream. */ |
243 | | int |
244 | | make_rss(i_ctx_t *i_ctx_p, os_ptr op, const byte * data, uint size, |
245 | | uint string_space, long offset, long length, bool is_bytestring) |
246 | 65 | { |
247 | 65 | uint save_space = icurrent_space; |
248 | 65 | stream *s; |
249 | 65 | long left = min(length, size - offset); |
250 | | |
251 | 65 | ialloc_set_space(idmemory, string_space); |
252 | 65 | s = file_alloc_stream(imemory, "make_rss"); |
253 | 65 | ialloc_set_space(idmemory, save_space); |
254 | 65 | if (s == 0) |
255 | 0 | return_error(gs_error_VMerror); |
256 | 65 | sread_string_reusable(s, data + offset, max(left, 0)); |
257 | 65 | if (is_bytestring) |
258 | 0 | s->cbuf_string.data = 0; /* byte array, not string */ |
259 | 65 | make_stream_file(op, s, "r"); |
260 | 65 | return 0; |
261 | 65 | } |
262 | | |
263 | | /* Make a reusable file stream. */ |
264 | | static int |
265 | | make_rfs(i_ctx_t *i_ctx_p, os_ptr op, stream *fs, long offset, long length) |
266 | 0 | { |
267 | 0 | uint save_space = icurrent_space; |
268 | 0 | uint stream_space = imemory_space((const gs_ref_memory_t *)fs->memory); |
269 | 0 | gs_const_string fname; |
270 | 0 | gs_parsed_file_name_t pname; |
271 | 0 | stream *s; |
272 | 0 | int code; |
273 | |
|
274 | 0 | if (sfilename(fs, &fname) < 0) |
275 | 0 | return_error(gs_error_ioerror); |
276 | 0 | code = gs_parse_file_name(&pname, (const char *)fname.data, fname.size, |
277 | 0 | imemory); |
278 | 0 | if (code < 0) |
279 | 0 | return code; |
280 | 0 | if (pname.len == 0) /* %stdin% etc. won't have a filename */ |
281 | 0 | return_error(gs_error_invalidfileaccess); /* can't reopen */ |
282 | 0 | if (pname.iodev == NULL) |
283 | 0 | pname.iodev = iodev_default(imemory); |
284 | | /* Open the file again, to be independent of the source. */ |
285 | 0 | ialloc_set_space(idmemory, stream_space); |
286 | 0 | code = zopen_file(i_ctx_p, &pname, "r", &s, imemory); |
287 | 0 | ialloc_set_space(idmemory, save_space); |
288 | 0 | if (code < 0) |
289 | 0 | return code; |
290 | 0 | if (sread_subfile(s, offset, length) < 0) { |
291 | 0 | sclose(s); |
292 | 0 | return_error(gs_error_ioerror); |
293 | 0 | } |
294 | 0 | s->close_at_eod = false; |
295 | 0 | make_stream_file(op, s, "r"); |
296 | 0 | return 0; |
297 | 0 | } |
298 | | /* ----------- Reusable array-of-strings stream ------------- */ |
299 | | |
300 | | static int s_aos_available(stream *, gs_offset_t *); |
301 | | static int s_aos_seek(stream *, gs_offset_t); |
302 | | static void s_aos_reset(stream *s); |
303 | | static int s_aos_flush(stream *s); |
304 | | static int s_aos_close(stream *); |
305 | | static int s_aos_process(stream_state *, stream_cursor_read *, |
306 | | stream_cursor_write *, bool); |
307 | | |
308 | | /* Stream state */ |
309 | | typedef struct aos_state_s { |
310 | | stream_state_common; |
311 | | ref blocks; |
312 | | stream *s; |
313 | | int blk_sz; |
314 | | int blk_sz_last; |
315 | | uint file_sz; |
316 | | } aos_state_t; |
317 | | |
318 | | /* GC procedures */ |
319 | | static |
320 | | CLEAR_MARKS_PROC(aos_clear_marks) |
321 | 151 | { aos_state_t *const pptr = vptr; |
322 | | |
323 | 151 | r_clear_attrs(&pptr->blocks, l_mark); |
324 | 151 | } |
325 | | static |
326 | 27 | ENUM_PTRS_WITH(aos_enum_ptrs, aos_state_t *pptr) return 0; |
327 | 9 | ENUM_PTR(0, aos_state_t, s); |
328 | 9 | case 1: |
329 | 9 | ENUM_RETURN_REF(&pptr->blocks); |
330 | 27 | ENUM_PTRS_END |
331 | 9 | static RELOC_PTRS_WITH(aos_reloc_ptrs, aos_state_t *pptr); |
332 | 9 | RELOC_PTR(aos_state_t, s); |
333 | 9 | RELOC_REF_VAR(pptr->blocks); |
334 | 9 | r_clear_attrs(&pptr->blocks, l_mark); |
335 | 9 | RELOC_PTRS_END |
336 | | |
337 | | gs_private_st_complex_only(st_aos_state, aos_state_t, |
338 | | "aos_state", aos_clear_marks, aos_enum_ptrs, aos_reloc_ptrs, 0); |
339 | | |
340 | | /* Stream template */ |
341 | | static const stream_template s_aos_template = { |
342 | | &st_aos_state, 0, s_aos_process, 1, 1, 0, 0 }; |
343 | | |
344 | | /* Stream procs */ |
345 | | static const stream_procs s_aos_procs = { |
346 | | s_aos_available, s_aos_seek, s_aos_reset, |
347 | | s_aos_flush, s_aos_close, s_aos_process, |
348 | | NULL /* no s_aos_switch */ |
349 | | }; |
350 | | |
351 | | static int |
352 | | make_aos(i_ctx_t *i_ctx_p, os_ptr op, int blk_sz, int blk_sz_last, uint file_sz) |
353 | 183 | { |
354 | 183 | stream *s; |
355 | 183 | aos_state_t *ss; |
356 | 183 | byte *buf; |
357 | 183 | const int aos_buf_size = 1024; /* arbitrary */ |
358 | 183 | uint save_space = icurrent_space; |
359 | 183 | ialloc_set_space(idmemory, r_space(op)); |
360 | | |
361 | 183 | s = s_alloc(imemory, "aos_stream"); |
362 | 183 | ss = (aos_state_t *)s_alloc_state(imemory, &st_aos_state, "st_aos_state"); |
363 | 183 | buf = gs_alloc_bytes(imemory, aos_buf_size, "aos_stream_buf"); |
364 | 183 | if (s == 0 || ss == 0 || buf == 0) { |
365 | 0 | gs_free_object(imemory, buf, "aos_stream_buf"); |
366 | 0 | gs_free_object(imemory, ss, "st_aos_state"); |
367 | 0 | gs_free_object(imemory, s, "aos_stream"); |
368 | 0 | ialloc_set_space(idmemory, save_space); |
369 | 0 | return_error(gs_error_VMerror); |
370 | 0 | } |
371 | 183 | ialloc_set_space(idmemory, save_space); |
372 | 183 | ss->templat = &s_aos_template; |
373 | 183 | ss->blocks = *op; |
374 | 183 | ss->s = s; |
375 | 183 | ss->blk_sz = blk_sz; |
376 | 183 | ss->blk_sz_last = blk_sz_last; |
377 | 183 | ss->file_sz = file_sz; |
378 | 183 | s_std_init(s, buf, aos_buf_size, &s_aos_procs, s_mode_read + s_mode_seek); |
379 | 183 | s->state = (stream_state *)ss; |
380 | 183 | s->file_offset = 0; |
381 | 183 | s->file_limit = S_FILE_LIMIT_MAX; |
382 | 183 | s->close_at_eod = false; |
383 | 183 | s->read_id = 1; |
384 | 183 | make_stream_file(op, s, "r"); |
385 | 183 | return 0; |
386 | 183 | } |
387 | | |
388 | | /* Return the number of available bytes */ |
389 | | static int |
390 | | s_aos_available(stream *s, gs_offset_t *pl) |
391 | 41 | { |
392 | 41 | *pl = ((aos_state_t *)s->state)->file_sz - stell(s); |
393 | 41 | return 0; |
394 | 41 | } |
395 | | |
396 | | /* Seek in a string being read. Return 0 if OK, ERRC if not. */ |
397 | | static int |
398 | | s_aos_seek(register stream * s, gs_offset_t pos) |
399 | 489 | { |
400 | 489 | uint end = s->cursor.r.limit - s->cbuf + 1; |
401 | 489 | long offset = pos - s->position; |
402 | | |
403 | 489 | if (offset >= 0 && offset <= end) { /* Staying within the same buffer */ |
404 | 41 | s->cursor.r.ptr = s->cbuf + offset - 1; |
405 | 41 | return 0; |
406 | 41 | } |
407 | 448 | if (pos < 0 || pos > s->file_limit) |
408 | 0 | return ERRC; |
409 | 448 | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1; |
410 | 448 | s->end_status = 0; |
411 | 448 | s->position = pos; |
412 | 448 | return 0; |
413 | 448 | } |
414 | | |
415 | | static void |
416 | | s_aos_reset(stream *s) |
417 | 0 | { |
418 | | /* PLRM definition of reset operator is strange. */ |
419 | | /* Rewind the file and discard the buffer. */ |
420 | 0 | s->position = 0; |
421 | 0 | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1; |
422 | 0 | s->end_status = 0; |
423 | 0 | } |
424 | | |
425 | | static int |
426 | | s_aos_flush(stream *s) |
427 | 0 | { |
428 | 0 | s->position = ((aos_state_t *)s->state)->file_sz; |
429 | 0 | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1; |
430 | 0 | return 0; |
431 | 0 | } |
432 | | |
433 | | static int |
434 | | s_aos_close(stream * s) |
435 | 0 | { |
436 | 0 | gs_free_object(s->memory, s->cbuf, "s_aos_close(buffer)"); |
437 | 0 | s->cbuf = 0; |
438 | | /* Increment the IDs to prevent further access. */ |
439 | 0 | s->read_id = s->write_id = (s->read_id | s->write_id) + 1; |
440 | 0 | return 0; |
441 | 0 | } |
442 | | |
443 | | static int |
444 | | s_aos_process(stream_state * st, stream_cursor_read * ignore_pr, |
445 | | stream_cursor_write * pw, bool last) |
446 | 224 | { |
447 | 224 | int blk_i, blk_off, blk_cnt, status = 1; |
448 | 224 | uint count; |
449 | 224 | aos_state_t *ss = (aos_state_t *)st; |
450 | 224 | uint max_count = pw->limit - pw->ptr; |
451 | 224 | uint pos = stell(ss->s); |
452 | 224 | unsigned const char *data; |
453 | 224 | ref *blk_ref; |
454 | | |
455 | 224 | pos += sbufavailable(ss->s); |
456 | 224 | if (pos >= ss->file_sz) |
457 | 73 | return EOFC; |
458 | 151 | blk_i = pos / ss->blk_sz; |
459 | 151 | blk_off = pos % ss->blk_sz; |
460 | 151 | blk_cnt = r_size(&ss->blocks); |
461 | 151 | count = blk_i < blk_cnt - 1 ? ss->blk_sz : ss->blk_sz_last; |
462 | 151 | blk_ref = &ss->blocks.value.refs[blk_i]; |
463 | | |
464 | 151 | if (!r_has_type_attrs(blk_ref, t_string, a_read) || r_size(blk_ref) != count) |
465 | 0 | return ERRC; |
466 | | |
467 | 151 | data = blk_ref->value.bytes; |
468 | | |
469 | 151 | if (max_count > count - blk_off) { |
470 | 62 | max_count = count - blk_off; |
471 | 62 | if (blk_i == blk_cnt - 1) |
472 | 62 | status = EOFC; |
473 | 62 | } |
474 | 151 | memcpy(pw->ptr+1, data + blk_off, max_count); |
475 | 151 | pw->ptr += max_count; |
476 | 151 | return status; |
477 | 151 | } |
478 | | |
479 | | /* ---------------- Initialization procedure ---------------- */ |
480 | | |
481 | | const op_def zfrsd_op_defs[] = |
482 | | { |
483 | | {"2.reusablestream", zreusablestream}, |
484 | | {"2.rsdparams", zrsdparams}, |
485 | | op_def_end(0) |
486 | | }; |