/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 | 151M | { |
44 | 151M | os_ptr op = osp; |
45 | | |
46 | 151M | check_op(1); |
47 | 151M | switch (r_type(op)) { |
48 | 8 | default: |
49 | 8 | return_op_typecheck(op); |
50 | 151M | case t_file: { |
51 | 151M | stream *s; |
52 | 151M | scanner_state state; |
53 | | |
54 | 151M | check_read_file(i_ctx_p, s, op); |
55 | 151M | check_ostack(1); |
56 | 151M | gs_scanner_init(&state, op); |
57 | 151M | return token_continue(i_ctx_p, &state, true); |
58 | 151M | } |
59 | 4.17k | case t_string: { |
60 | 4.17k | ref token; |
61 | | /* -1 is to remove the string operand in case of error. */ |
62 | 4.17k | int orig_ostack_depth = ref_stack_count(&o_stack) - 1; |
63 | 4.17k | int code; |
64 | | |
65 | | /* Don't pop the operand in case of invalidaccess. */ |
66 | 4.17k | if (!r_has_attr(op, a_read)) |
67 | 0 | return_error(gs_error_invalidaccess); |
68 | 4.17k | 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 | 4.17k | op = osp; |
74 | 4.17k | switch (code) { |
75 | 117 | case scan_EOF: /* no tokens */ |
76 | 117 | make_false(op); |
77 | 117 | return 0; |
78 | 4.05k | default: |
79 | 4.05k | if (code < 0) { |
80 | | /* |
81 | | * Clear anything that may have been left on the ostack, |
82 | | * including the string operand. |
83 | | */ |
84 | 17 | if (orig_ostack_depth < ref_stack_count(&o_stack)) |
85 | 17 | pop(ref_stack_count(&o_stack)- orig_ostack_depth); |
86 | 17 | return code; |
87 | 17 | } |
88 | 4.17k | } |
89 | 4.17k | push(2); |
90 | 4.03k | op[-1] = token; |
91 | 4.03k | make_true(op); |
92 | 4.03k | return 0; |
93 | 4.03k | } |
94 | 151M | } |
95 | 151M | } |
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 | 151M | { |
113 | 151M | os_ptr op = osp; |
114 | 151M | int code; |
115 | 151M | ref token; |
116 | 151M | avm_space usevm, currentvm = ialloc_space(idmemory); |
117 | 151M | 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 | 151M | 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 | 151M | make_null(osp); |
131 | | /* Note that gs_scan_token may change osp! */ |
132 | 151M | pop(1); /* remove the file or scanner state */ |
133 | 151M | again: |
134 | 151M | code = gs_scan_token(i_ctx_p, &token, pstate); |
135 | 151M | op = osp; |
136 | 151M | 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 | 151M | case 0: /* read a token */ |
146 | 151M | push(2); |
147 | 151M | ref_assign(op - 1, &token); |
148 | 151M | make_true(op); |
149 | 151M | break; |
150 | 334k | case scan_EOF: /* no tokens */ |
151 | 334k | push(1); |
152 | 334k | make_false(op); |
153 | 334k | code = 0; |
154 | 334k | 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 | 151M | } |
168 | 151M | if (code <= 0 && !save) { /* Deallocate the scanner state record. */ |
169 | 0 | ifree_object(pstate, "token_continue"); |
170 | 0 | } |
171 | 151M | if (code < 0) |
172 | 151M | make_op_estack(esp + 1, ztoken); |
173 | 151M | return code; |
174 | 151M | } |
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 | 3.30M | { |
202 | 3.30M | os_ptr op = osp; |
203 | 3.30M | scanner_state *pstate; |
204 | | |
205 | 3.30M | check_op(1); |
206 | 3.30M | check_stype(*op, st_scanner_state_dynamic); |
207 | 3.30M | pstate = r_ptr(op, scanner_state); |
208 | 3.30M | return tokenexec_continue(i_ctx_p, pstate, false); |
209 | 3.30M | } |
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 | 3.30M | { |
214 | 3.30M | os_ptr op = osp; |
215 | 3.30M | int code; |
216 | 3.30M | avm_space usevm, currentvm = ialloc_space(idmemory); |
217 | 3.30M | 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 | 3.30M | 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 | 3.30M | make_null(osp); |
231 | | /* Note that gs_scan_token may change osp! */ |
232 | 3.30M | pop(1); |
233 | 3.31M | again: |
234 | 3.31M | check_estack(1); |
235 | 3.31M | code = gs_scan_token(i_ctx_p, (ref *) (esp + 1), pstate); |
236 | 3.31M | op = osp; |
237 | 3.31M | switch (code) { |
238 | 1.72M | case 0: |
239 | 1.72M | if (r_is_proc(esp + 1)) { /* Treat procedure as a literal. */ |
240 | 1.57k | push(1); |
241 | 1.57k | ref_assign(op, esp + 1); |
242 | 1.57k | code = 0; |
243 | 1.57k | break; |
244 | 1.57k | } |
245 | | /* falls through */ |
246 | 1.72M | case scan_BOS: |
247 | 1.72M | ++esp; |
248 | 1.72M | code = o_push_estack; |
249 | 1.72M | break; |
250 | 4.55k | case scan_EOF: /* no tokens */ |
251 | 4.55k | code = 0; |
252 | 4.55k | break; |
253 | 166k | case scan_Refill: /* need more data */ |
254 | 166k | ialloc_set_space(idmemory, usevm); |
255 | 166k | code = gs_scan_handle_refill(i_ctx_p, pstate, save, |
256 | 166k | ztokenexec_continue); |
257 | 166k | ialloc_set_space(idmemory, currentvm); |
258 | 166k | switch (code) { |
259 | 3.51k | case 0: /* state is not copied to the heap */ |
260 | 3.51k | goto again; |
261 | 162k | case o_push_estack: |
262 | 162k | return code; |
263 | 166k | } |
264 | 121 | break; /* error */ |
265 | 121 | case scan_Comment: |
266 | 1.41M | case scan_DSC_Comment: |
267 | 1.41M | return ztoken_handle_comment(i_ctx_p, pstate, esp + 1, code, |
268 | 1.41M | save, true, ztokenexec_continue); |
269 | 1.35k | default: /* error */ |
270 | 1.35k | gs_scanner_error_object(i_ctx_p, pstate, &i_ctx_p->error_object); |
271 | 1.35k | break; |
272 | 3.31M | } |
273 | 1.72M | if (!save) { /* Deallocate the scanner state record. */ |
274 | 1.72M | gs_free_object(((scanner_state_dynamic *)pstate)->mem, pstate, "token_continue"); |
275 | 1.72M | } |
276 | 1.72M | return code; |
277 | 3.31M | } |
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 | 1.52M | { |
289 | 1.52M | const char *proc_name; |
290 | 1.52M | scanner_state *pstate; |
291 | 1.52M | os_ptr op; |
292 | 1.52M | ref *ppcproc; |
293 | 1.52M | int code; |
294 | | |
295 | 1.52M | switch (scan_code) { |
296 | 0 | case scan_Comment: |
297 | 0 | proc_name = "%ProcessComment"; |
298 | 0 | break; |
299 | 1.52M | case scan_DSC_Comment: |
300 | 1.52M | proc_name = "%ProcessDSCComment"; |
301 | 1.52M | break; |
302 | 0 | default: |
303 | 0 | return_error(gs_error_Fatal); /* can't happen */ |
304 | 1.52M | } |
305 | | /* |
306 | | * We can't use check_ostack here, because it returns on overflow. |
307 | | */ |
308 | | /*check_ostack(2);*/ |
309 | 1.52M | if (ostop - osp < 2) { |
310 | 78 | code = ref_stack_extend(&o_stack, 2); |
311 | 78 | if (code < 0) |
312 | 0 | return code; |
313 | 78 | } |
314 | 1.52M | check_estack(3); |
315 | 1.52M | code = name_enter_string(imemory, proc_name, esp + 3); |
316 | 1.52M | if (code < 0) |
317 | 0 | return code; |
318 | 1.52M | if (save) { |
319 | 104k | pstate = (scanner_state *)ialloc_struct(scanner_state_dynamic, &st_scanner_state_dynamic, |
320 | 104k | "ztoken_handle_comment"); |
321 | 104k | if (pstate == 0) |
322 | 0 | return_error(gs_error_VMerror); |
323 | 104k | ((scanner_state_dynamic *)pstate)->mem = imemory; |
324 | 104k | *pstate = *sstate; |
325 | 104k | } else |
326 | 1.41M | pstate = sstate; |
327 | | /* Save the token now -- it might be on the e-stack. */ |
328 | 1.52M | if (!pstate->s_pstack) |
329 | 1.35M | osp[2] = *ptoken; |
330 | | /* |
331 | | * Push the continuation, scanner state, file, and callout procedure |
332 | | * on the e-stack. |
333 | | */ |
334 | 1.52M | make_op_estack(esp + 1, cont); |
335 | 1.52M | make_istruct(esp + 2, 0, pstate); |
336 | 1.52M | ppcproc = dict_find_name(esp + 3); |
337 | 1.52M | 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 | 1.52M | } 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 | 1.52M | if (pstate->s_pstack) { |
351 | 164k | op = ++osp; |
352 | 164k | *op = op[-1]; |
353 | 1.35M | } else { |
354 | 1.35M | op = osp += 2; |
355 | | /* *op = *ptoken; */ /* saved above */ |
356 | 1.35M | } |
357 | 1.52M | op[-1] = pstate->s_file; |
358 | 1.52M | esp[3] = *ppcproc; |
359 | 1.52M | esp += 3; |
360 | 1.52M | } |
361 | 1.52M | return o_push_estack; |
362 | 1.52M | } |
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 | 3.94M | { |
383 | 3.94M | int options = old_options; |
384 | 3.94M | int i; |
385 | | |
386 | 23.6M | for (i = 0; i < countof(named_options); ++i) { |
387 | 19.7M | const named_scanner_option_t *pnso = &named_options[i]; |
388 | 19.7M | ref *ppcproc; |
389 | 19.7M | int code = dict_find_string(upref, pnso->pname, &ppcproc); |
390 | | |
391 | | /* Update the options only if the parameter has changed. */ |
392 | 19.7M | if (code > 0) { |
393 | 1.38M | if (r_has_type(ppcproc, t_null)) |
394 | 203k | options &= ~pnso->option; |
395 | 1.17M | else |
396 | 1.17M | options |= pnso->option; |
397 | 1.38M | } |
398 | 19.7M | } |
399 | 3.94M | return options; |
400 | 3.94M | } |
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 | 974k | { |
408 | 974k | const named_scanner_option_t *pnso; |
409 | | |
410 | 5.84M | for (pnso = named_options + countof(named_options); pnso-- != named_options;) { |
411 | 4.87M | if (!bytes_compare((const byte *)pnso->pname, strlen(pnso->pname), |
412 | 4.87M | psref->value.const_bytes, r_size(psref))) { |
413 | 0 | *pname = pnso->pname; |
414 | 0 | return (options & pnso->option ? 1 : 0); |
415 | 0 | } |
416 | 4.87M | } |
417 | 974k | return -1; |
418 | 974k | } |
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 | | }; |