/src/ghostpdl/psi/ztoken.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 | | /* Token reading operators */ |
18 | | #include "string_.h" |
19 | | #include "stat_.h" /* get system header early to avoid name clash on Cygwin */ |
20 | | #include "ghost.h" |
21 | | #include "oper.h" |
22 | | #include "dstack.h" /* for dict_find_name */ |
23 | | #include "estack.h" |
24 | | #include "gsstruct.h" /* for iscan.h */ |
25 | | #include "gsutil.h" |
26 | | #include "stream.h" |
27 | | #include "files.h" |
28 | | #include "store.h" |
29 | | #include "strimpl.h" /* for sfilter.h */ |
30 | | #include "sfilter.h" /* for iscan.h */ |
31 | | #include "idict.h" |
32 | | #include "iname.h" |
33 | | #include "iscan.h" |
34 | | #include "itoken.h" /* for prototypes */ |
35 | | |
36 | | /* <file> token <obj> -true- */ |
37 | | /* <string> token <post> <obj> -true- */ |
38 | | /* <string|file> token -false- */ |
39 | | static int ztoken_continue(i_ctx_t *); |
40 | | static int token_continue(i_ctx_t *, scanner_state *, bool); |
41 | | int |
42 | | ztoken(i_ctx_t *i_ctx_p) |
43 | 205k | { |
44 | 205k | os_ptr op = osp; |
45 | | |
46 | 205k | switch (r_type(op)) { |
47 | 0 | default: |
48 | 0 | return_op_typecheck(op); |
49 | 205k | case t_file: { |
50 | 205k | stream *s; |
51 | 205k | scanner_state state; |
52 | | |
53 | 205k | check_read_file(i_ctx_p, s, op); |
54 | 205k | check_ostack(1); |
55 | 205k | gs_scanner_init(&state, op); |
56 | 205k | return token_continue(i_ctx_p, &state, true); |
57 | 205k | } |
58 | 16 | case t_string: { |
59 | 16 | ref token; |
60 | | /* -1 is to remove the string operand in case of error. */ |
61 | 16 | int orig_ostack_depth = ref_stack_count(&o_stack) - 1; |
62 | 16 | int code; |
63 | | |
64 | | /* Don't pop the operand in case of invalidaccess. */ |
65 | 16 | if (!r_has_attr(op, a_read)) |
66 | 0 | return_error(gs_error_invalidaccess); |
67 | 16 | code = gs_scan_string_token(i_ctx_p, op, &token); |
68 | 16 | switch (code) { |
69 | 0 | case scan_EOF: /* no tokens */ |
70 | 0 | make_false(op); |
71 | 0 | return 0; |
72 | 16 | default: |
73 | 16 | if (code < 0) { |
74 | | /* |
75 | | * Clear anything that may have been left on the ostack, |
76 | | * including the string operand. |
77 | | */ |
78 | 0 | if (orig_ostack_depth < ref_stack_count(&o_stack)) |
79 | 0 | pop(ref_stack_count(&o_stack)- orig_ostack_depth); |
80 | 0 | return code; |
81 | 0 | } |
82 | 16 | } |
83 | 16 | push(2); |
84 | 16 | op[-1] = token; |
85 | 16 | make_true(op); |
86 | 16 | return 0; |
87 | 16 | } |
88 | 205k | } |
89 | 205k | } |
90 | | /* Continue reading a token after an interrupt or callout. */ |
91 | | /* *op is the scanner state. */ |
92 | | static int |
93 | | ztoken_continue(i_ctx_t *i_ctx_p) |
94 | 0 | { |
95 | 0 | os_ptr op = osp; |
96 | 0 | scanner_state *pstate; |
97 | |
|
98 | 0 | check_stype(*op, st_scanner_state_dynamic); |
99 | 0 | pstate = r_ptr(op, scanner_state); |
100 | 0 | return token_continue(i_ctx_p, pstate, false); |
101 | 0 | } |
102 | | /* Common code for token reading. */ |
103 | | static int |
104 | | token_continue(i_ctx_t *i_ctx_p, scanner_state * pstate, bool save) |
105 | 205k | { |
106 | 205k | os_ptr op = osp; |
107 | 205k | int code; |
108 | 205k | ref token; |
109 | | |
110 | | /* Since we might free pstate below, and we're dealing with |
111 | | * gc memory referenced by the stack, we need to explicitly |
112 | | * remove the reference to pstate from the stack, otherwise |
113 | | * the garbager will fall over |
114 | | */ |
115 | 205k | make_null(osp); |
116 | | /* Note that gs_scan_token may change osp! */ |
117 | 205k | pop(1); /* remove the file or scanner state */ |
118 | 205k | again: |
119 | 205k | code = gs_scan_token(i_ctx_p, &token, pstate); |
120 | 205k | op = osp; |
121 | 205k | switch (code) { |
122 | 0 | default: /* error */ |
123 | 0 | if (code > 0) /* comment, not possible */ |
124 | 0 | code = gs_note_error(gs_error_syntaxerror); |
125 | 0 | gs_scanner_error_object(i_ctx_p, pstate, &i_ctx_p->error_object); |
126 | 0 | make_op_estack(esp + 1, ztoken); |
127 | 0 | break; |
128 | 0 | case scan_BOS: |
129 | 0 | code = 0; |
130 | 204k | case 0: /* read a token */ |
131 | 204k | push(2); |
132 | 204k | ref_assign(op - 1, &token); |
133 | 204k | make_true(op); |
134 | 204k | break; |
135 | 1.36k | case scan_EOF: /* no tokens */ |
136 | 1.36k | push(1); |
137 | 1.36k | make_false(op); |
138 | 1.36k | code = 0; |
139 | 1.36k | break; |
140 | 0 | case scan_Refill: /* need more data */ |
141 | 0 | code = gs_scan_handle_refill(i_ctx_p, pstate, save, |
142 | 0 | ztoken_continue); |
143 | 0 | switch (code) { |
144 | 0 | case 0: /* state is not copied to the heap */ |
145 | 0 | goto again; |
146 | 0 | case o_push_estack: |
147 | 0 | return code; |
148 | 0 | } |
149 | 0 | break; /* error */ |
150 | 205k | } |
151 | 205k | if (code <= 0 && !save) { /* Deallocate the scanner state record. */ |
152 | 0 | ifree_object(pstate, "token_continue"); |
153 | 0 | } |
154 | 205k | if (code < 0) |
155 | 205k | make_op_estack(esp + 1, ztoken); |
156 | 205k | return code; |
157 | 205k | } |
158 | | |
159 | | /* <file> .tokenexec - */ |
160 | | /* Read a token and do what the interpreter would do with it. */ |
161 | | /* This is different from token + exec because literal procedures */ |
162 | | /* are not executed (although binary object sequences ARE executed). */ |
163 | | int ztokenexec_continue(i_ctx_t *); /* export for interpreter */ |
164 | | static int tokenexec_continue(i_ctx_t *, scanner_state *, bool); |
165 | | int |
166 | | ztokenexec(i_ctx_t *i_ctx_p) |
167 | 0 | { |
168 | 0 | os_ptr op = osp; |
169 | 0 | stream *s; |
170 | 0 | scanner_state state; |
171 | |
|
172 | 0 | check_read_file(i_ctx_p, s, op); |
173 | 0 | check_estack(1); |
174 | 0 | gs_scanner_init(&state, op); |
175 | 0 | return tokenexec_continue(i_ctx_p, &state, true); |
176 | 0 | } |
177 | | /* Continue reading a token for execution after an interrupt or callout. */ |
178 | | /* *op is the scanner state. */ |
179 | | /* We export this because this is how the interpreter handles a */ |
180 | | /* scan_Refill for an executable file. */ |
181 | | int |
182 | | ztokenexec_continue(i_ctx_t *i_ctx_p) |
183 | 6.19k | { |
184 | 6.19k | os_ptr op = osp; |
185 | 6.19k | scanner_state *pstate; |
186 | | |
187 | 6.19k | check_stype(*op, st_scanner_state_dynamic); |
188 | 6.19k | pstate = r_ptr(op, scanner_state); |
189 | 6.19k | return tokenexec_continue(i_ctx_p, pstate, false); |
190 | 6.19k | } |
191 | | /* Common code for token reading + execution. */ |
192 | | static int |
193 | | tokenexec_continue(i_ctx_t *i_ctx_p, scanner_state * pstate, bool save) |
194 | 6.19k | { |
195 | 6.19k | os_ptr op = osp; |
196 | 6.19k | int code; |
197 | | /* Since we might free pstate below, and we're dealing with |
198 | | * gc memory referenced by the stack, we need to explicitly |
199 | | * remove the reference to pstate from the stack, otherwise |
200 | | * the garbager will fall over |
201 | | */ |
202 | 6.19k | make_null(osp); |
203 | | /* Note that gs_scan_token may change osp! */ |
204 | 6.19k | pop(1); |
205 | 6.19k | again: |
206 | 6.19k | check_estack(1); |
207 | 6.19k | code = gs_scan_token(i_ctx_p, (ref *) (esp + 1), pstate); |
208 | 6.19k | op = osp; |
209 | 6.19k | switch (code) { |
210 | 5.47k | case 0: |
211 | 5.47k | if (r_is_proc(esp + 1)) { /* Treat procedure as a literal. */ |
212 | 0 | push(1); |
213 | 0 | ref_assign(op, esp + 1); |
214 | 0 | code = 0; |
215 | 0 | break; |
216 | 0 | } |
217 | | /* falls through */ |
218 | 5.47k | case scan_BOS: |
219 | 5.47k | ++esp; |
220 | 5.47k | code = o_push_estack; |
221 | 5.47k | break; |
222 | 1 | case scan_EOF: /* no tokens */ |
223 | 1 | code = 0; |
224 | 1 | break; |
225 | 683 | case scan_Refill: /* need more data */ |
226 | 683 | code = gs_scan_handle_refill(i_ctx_p, pstate, save, |
227 | 683 | ztokenexec_continue); |
228 | 683 | switch (code) { |
229 | 0 | case 0: /* state is not copied to the heap */ |
230 | 0 | goto again; |
231 | 683 | case o_push_estack: |
232 | 683 | return code; |
233 | 683 | } |
234 | 0 | break; /* error */ |
235 | 0 | case scan_Comment: |
236 | 34 | case scan_DSC_Comment: |
237 | 34 | return ztoken_handle_comment(i_ctx_p, pstate, esp + 1, code, |
238 | 34 | save, true, ztokenexec_continue); |
239 | 0 | default: /* error */ |
240 | 0 | gs_scanner_error_object(i_ctx_p, pstate, &i_ctx_p->error_object); |
241 | 0 | break; |
242 | 6.19k | } |
243 | 5.47k | if (!save) { /* Deallocate the scanner state record. */ |
244 | 5.47k | gs_free_object(((scanner_state_dynamic *)pstate)->mem, pstate, "token_continue"); |
245 | 5.47k | } |
246 | 5.47k | return code; |
247 | 6.19k | } |
248 | | |
249 | | /* |
250 | | * Handle a scan_Comment or scan_DSC_Comment return from gs_scan_token |
251 | | * (scan_code) by calling out to %Process[DSC]Comment. The continuation |
252 | | * procedure expects the scanner state on the o-stack. |
253 | | */ |
254 | | int |
255 | | ztoken_handle_comment(i_ctx_t *i_ctx_p, scanner_state *sstate, |
256 | | const ref *ptoken, int scan_code, |
257 | | bool save, bool push_file, op_proc_t cont) |
258 | 44 | { |
259 | 44 | const char *proc_name; |
260 | 44 | scanner_state *pstate; |
261 | 44 | os_ptr op; |
262 | 44 | ref *ppcproc; |
263 | 44 | int code; |
264 | | |
265 | 44 | switch (scan_code) { |
266 | 0 | case scan_Comment: |
267 | 0 | proc_name = "%ProcessComment"; |
268 | 0 | break; |
269 | 44 | case scan_DSC_Comment: |
270 | 44 | proc_name = "%ProcessDSCComment"; |
271 | 44 | break; |
272 | 0 | default: |
273 | 0 | return_error(gs_error_Fatal); /* can't happen */ |
274 | 44 | } |
275 | | /* |
276 | | * We can't use check_ostack here, because it returns on overflow. |
277 | | */ |
278 | | /*check_ostack(2);*/ |
279 | 44 | if (ostop - osp < 2) { |
280 | 0 | code = ref_stack_extend(&o_stack, 2); |
281 | 0 | if (code < 0) |
282 | 0 | return code; |
283 | 0 | } |
284 | 44 | check_estack(3); |
285 | 44 | code = name_enter_string(imemory, proc_name, esp + 3); |
286 | 44 | if (code < 0) |
287 | 0 | return code; |
288 | 44 | if (save) { |
289 | 10 | pstate = (scanner_state *)ialloc_struct(scanner_state_dynamic, &st_scanner_state_dynamic, |
290 | 10 | "ztoken_handle_comment"); |
291 | 10 | if (pstate == 0) |
292 | 0 | return_error(gs_error_VMerror); |
293 | 10 | ((scanner_state_dynamic *)pstate)->mem = imemory; |
294 | 10 | *pstate = *sstate; |
295 | 10 | } else |
296 | 34 | pstate = sstate; |
297 | | /* Save the token now -- it might be on the e-stack. */ |
298 | 44 | if (!pstate->s_pstack) |
299 | 44 | osp[2] = *ptoken; |
300 | | /* |
301 | | * Push the continuation, scanner state, file, and callout procedure |
302 | | * on the e-stack. |
303 | | */ |
304 | 44 | make_op_estack(esp + 1, cont); |
305 | 44 | make_istruct(esp + 2, 0, pstate); |
306 | 44 | ppcproc = dict_find_name(esp + 3); |
307 | 44 | if (ppcproc == 0) { |
308 | | /* |
309 | | * This can only happen during initialization. |
310 | | * Pop the comment string from the o-stack if needed (see below). |
311 | | */ |
312 | 0 | if (pstate->s_pstack) |
313 | 0 | --osp; |
314 | 0 | esp += 2; /* do run the continuation */ |
315 | 44 | } else { |
316 | | /* |
317 | | * Push the file and comment string on the o-stack. |
318 | | * If we were inside { }, the comment string is already on the stack. |
319 | | */ |
320 | 44 | if (pstate->s_pstack) { |
321 | 0 | op = ++osp; |
322 | 0 | *op = op[-1]; |
323 | 44 | } else { |
324 | 44 | op = osp += 2; |
325 | | /* *op = *ptoken; */ /* saved above */ |
326 | 44 | } |
327 | 44 | op[-1] = pstate->s_file; |
328 | 44 | esp[3] = *ppcproc; |
329 | 44 | esp += 3; |
330 | 44 | } |
331 | 44 | return o_push_estack; |
332 | 44 | } |
333 | | |
334 | | typedef struct named_scanner_option_s { |
335 | | const char *pname; |
336 | | int option; |
337 | | } named_scanner_option_t; |
338 | | static const named_scanner_option_t named_options[] = { |
339 | | {"PDFScanRules", SCAN_PDF_RULES}, |
340 | | {"ProcessComment", SCAN_PROCESS_COMMENTS}, |
341 | | {"ProcessDSCComment", SCAN_PROCESS_DSC_COMMENTS}, |
342 | | {"PDFScanInvNum", SCAN_PDF_INV_NUM}, |
343 | | {"PDFScanUnsigned", SCAN_PDF_UNSIGNED} |
344 | | }; |
345 | | |
346 | | /* |
347 | | * Update the cached scanner_options in the context state after doing a |
348 | | * setuserparams. (We might move this procedure somewhere else eventually.) |
349 | | */ |
350 | | int |
351 | | ztoken_scanner_options(const ref *upref, int old_options) |
352 | 16.3k | { |
353 | 16.3k | int options = old_options; |
354 | 16.3k | int i; |
355 | | |
356 | 98.2k | for (i = 0; i < countof(named_options); ++i) { |
357 | 81.9k | const named_scanner_option_t *pnso = &named_options[i]; |
358 | 81.9k | ref *ppcproc; |
359 | 81.9k | int code = dict_find_string(upref, pnso->pname, &ppcproc); |
360 | | |
361 | | /* Update the options only if the parameter has changed. */ |
362 | 81.9k | if (code > 0) { |
363 | 4.07k | if (r_has_type(ppcproc, t_null)) |
364 | 671 | options &= ~pnso->option; |
365 | 3.40k | else |
366 | 3.40k | options |= pnso->option; |
367 | 4.07k | } |
368 | 81.9k | } |
369 | 16.3k | return options; |
370 | 16.3k | } |
371 | | /* |
372 | | * Get the value for a scanner option. |
373 | | * return -1 if no such option, 1/0 for on/off and option's name in *pname as a C string |
374 | | */ |
375 | | int |
376 | | ztoken_get_scanner_option(const ref *psref, int options, const char **pname) |
377 | 4.78k | { |
378 | 4.78k | const named_scanner_option_t *pnso; |
379 | | |
380 | 28.6k | for (pnso = named_options + countof(named_options); pnso-- != named_options;) { |
381 | 23.9k | if (!bytes_compare((const byte *)pnso->pname, strlen(pnso->pname), |
382 | 23.9k | psref->value.const_bytes, r_size(psref))) { |
383 | 0 | *pname = pnso->pname; |
384 | 0 | return (options & pnso->option ? 1 : 0); |
385 | 0 | } |
386 | 23.9k | } |
387 | 4.78k | return -1; |
388 | 4.78k | } |
389 | | |
390 | | /* ------ Initialization procedure ------ */ |
391 | | |
392 | | const op_def ztoken_op_defs[] = |
393 | | { |
394 | | {"1token", ztoken}, |
395 | | {"1.tokenexec", ztokenexec}, |
396 | | /* Internal operators */ |
397 | | {"2%ztoken_continue", ztoken_continue}, |
398 | | {"2%ztokenexec_continue", ztokenexec_continue}, |
399 | | op_def_end(0) |
400 | | }; |