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