/src/ghostpdl/pcl/pcl/pgparse.c
Line | Count | Source |
1 | | /* Copyright (C) 2001-2026 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 | | /* pgparse.c */ |
18 | | /* HP-GL/2 parser */ |
19 | | #include "math_.h" |
20 | | #include "stdio_.h" |
21 | | #include "gdebug.h" |
22 | | #include "gstypes.h" |
23 | | #include "scommon.h" |
24 | | #include "pgmand.h" |
25 | | #include "setjmp_.h" |
26 | | |
27 | | /* ---------------- Command definition ---------------- */ |
28 | | |
29 | | /* Register a command. Return true if this is a redefinition. */ |
30 | | static bool |
31 | | hpgl_register_command(hpgl_parser_state_t * pgl_parser_state, |
32 | | byte * pindex, const hpgl_command_definition_t * pcmd) |
33 | 607k | { |
34 | 607k | int index = pgl_parser_state->hpgl_command_next_index; |
35 | 607k | byte prev = *pindex; |
36 | | |
37 | 607k | if (prev != 0 && prev <= index && |
38 | 0 | pgl_parser_state->hpgl_command_list[prev] == pcmd) |
39 | 0 | index = prev; |
40 | 607k | else if (index != 0 |
41 | 599k | && pgl_parser_state->hpgl_command_list[index] == pcmd); |
42 | 607k | else |
43 | 607k | pgl_parser_state->hpgl_command_list[pgl_parser_state-> |
44 | 607k | hpgl_command_next_index = |
45 | 607k | ++index] = |
46 | 607k | (hpgl_command_definition_t *) pcmd; |
47 | 607k | *pindex = index; |
48 | 607k | return (prev != 0 && prev != index); |
49 | 607k | } |
50 | | |
51 | | /* Define a list of commands. */ |
52 | | void |
53 | | hpgl_define_commands(const gs_memory_t * mem, |
54 | | const hpgl_named_command_t * pcmds, |
55 | | hpgl_parser_state_t * pgl_parser_state) |
56 | 56.6k | { |
57 | 56.6k | const hpgl_named_command_t *pcmd = pcmds; |
58 | | |
59 | 663k | for (; pcmd->char1; ++pcmd) |
60 | | #ifdef DEBUG |
61 | | if ( |
62 | | #endif |
63 | 607k | hpgl_register_command(pgl_parser_state, |
64 | 607k | &pgl_parser_state->hpgl_command_indices |
65 | 607k | [pcmd->char1 - 'A'][pcmd->char2 - 'A'], |
66 | 607k | &pcmd->defn) |
67 | | #ifdef DEBUG |
68 | | ) |
69 | | dmprintf2(mem, "Redefining command %c%c\n", pcmd->char1, |
70 | | pcmd->char2); |
71 | | #endif |
72 | 56.6k | ; |
73 | 56.6k | } |
74 | | |
75 | | /* ---------------- Parsing ---------------- */ |
76 | | |
77 | | /* Initialize the HP-GL/2 parser state. */ |
78 | | void |
79 | | hpgl_process_init(hpgl_parser_state_t * pst) |
80 | 8.09k | { |
81 | 8.09k | pst->first_letter = -1; |
82 | 8.09k | pst->command = 0; |
83 | 8.09k | } |
84 | | |
85 | | /* Process a buffer of HP-GL/2 commands. */ |
86 | | /* Return 0 if more input needed, 1 if ESC seen, or an error code. */ |
87 | | OPTIMIZE_SETJMP |
88 | | int |
89 | | hpgl_process(hpgl_parser_state_t * pst, hpgl_state_t * pgls, |
90 | | stream_cursor_read * pr) |
91 | 122k | { |
92 | 122k | const byte *p = pr->ptr; |
93 | 122k | const byte *rlimit = pr->limit; |
94 | 122k | int code = 0; |
95 | 122k | jmp_buf exit_to_parser; |
96 | | |
97 | | /* We do this slightly naff setup of the buffer so the |
98 | | * buffer is sure to be currectly aligned - 64 bit MSVS 2010+ |
99 | | * build requires the jmp_buf to be aligned to 16 bytes. |
100 | | */ |
101 | 122k | pst->exit_to_parser = &exit_to_parser; |
102 | | |
103 | 122k | pst->source.limit = rlimit; |
104 | | /* Prepare to catch a longjmp indicating the argument scanner */ |
105 | | /* needs more data, or encountered an error. */ |
106 | 122k | code = setjmp(*(pst->exit_to_parser)); |
107 | 122k | if (code) { |
108 | | /* The command detected an error, or we need to ask */ |
109 | | /* the caller for more data. pst->command != 0. */ |
110 | 724 | pr->ptr = pst->source.ptr; |
111 | 724 | pst->exit_to_parser = NULL; |
112 | 724 | if (code < 0 && code != gs_error_NeedInput) { |
113 | 0 | pst->command = 0; /* cancel command */ |
114 | 0 | if_debug0m('i', pgls->memory, "\n"); |
115 | 0 | return code; |
116 | 0 | } |
117 | 724 | return 0; |
118 | 724 | } |
119 | | /* Check whether we're still feeding data to a command. */ |
120 | 606k | call:if (pst->command) { |
121 | 559k | pst->source.ptr = p; |
122 | 559k | pst->arg.next = 0; |
123 | 559k | code = (*pst->command->proc) (pst, pgls); |
124 | 559k | p = pst->source.ptr; |
125 | | /* cancel the command for any error other than needing |
126 | | more data */ |
127 | 559k | if (code < 0 && code != gs_error_NeedInput) |
128 | 6 | pst->command = 0; |
129 | 559k | if (code < 0) |
130 | 80.8k | goto x; |
131 | 478k | pst->command = 0; |
132 | 478k | if_debug0m('i', pgls->memory, "\n"); |
133 | 478k | } |
134 | 2.48M | while (p < rlimit) { |
135 | 2.47M | byte next = *++p; |
136 | | |
137 | 2.47M | if (next >= 'A' && next <= 'Z') |
138 | 1.04M | next -= 'A'; |
139 | 1.43M | else if (next >= 'a' && next <= 'z') |
140 | 220k | next -= 'a'; |
141 | 1.21M | else if (next == ESC) { |
142 | 39.9k | --p; |
143 | 39.9k | pst->first_letter = -1; |
144 | 39.9k | code = 1; |
145 | 39.9k | break; |
146 | 1.17M | } else { /* ignore everything else */ |
147 | | /* Apparently this is what H-P plotters do.... */ |
148 | 1.17M | if (next > ' ' && next != ',') |
149 | 710k | pst->first_letter = -1; |
150 | 1.17M | continue; |
151 | 1.17M | } |
152 | 1.26M | if (pst->first_letter < 0) { |
153 | 577k | pst->first_letter = next; |
154 | 577k | continue; |
155 | 577k | } |
156 | 685k | { |
157 | 685k | int index = pst->hpgl_command_indices[pst->first_letter][next]; |
158 | | |
159 | | #ifdef DEBUG |
160 | | if (gs_debug_c('i')) { |
161 | | char c = (index ? '-' : '?'); |
162 | | |
163 | | dmprintf4(pgls->memory, "--%c%c%c%c", |
164 | | pst->first_letter + 'A', next + 'A', c, c); |
165 | | } |
166 | | #endif |
167 | 685k | if (index == 0) { /* anomalous, drop 1st letter */ |
168 | 201k | pst->first_letter = next; |
169 | 201k | continue; |
170 | 201k | } |
171 | 483k | pst->first_letter = -1; |
172 | 483k | pst->command = pst->hpgl_command_list[index]; |
173 | 483k | pst->phase = 0; |
174 | 483k | pst->done = false; |
175 | 483k | hpgl_args_init(pst); |
176 | | /* |
177 | | * Only a few commands should be executed while we're in |
178 | | * polygon mode: check for this here. Note that we rely |
179 | | * on the garbage-skipping property of the parser to skip |
180 | | * over any following arguments. This doesn't work for |
181 | | * the few commands with special syntax that should be |
182 | | * ignored in polygon mode (CO, DT, LB, SM); they must be |
183 | | * flagged as executable even in polygon mode, and check |
184 | | * the render_mode themselves. |
185 | | */ |
186 | 483k | { |
187 | 483k | bool ignore_command = false; |
188 | | |
189 | 483k | if ((pgls->g.polygon_mode) && |
190 | 107k | !(pst->command->flags & hpgl_cdf_polygon) |
191 | 483k | ) |
192 | 4.71k | ignore_command = true; |
193 | 479k | else { /* similarly if we are in lost mode we do not |
194 | | execute the commands that are only defined to |
195 | | be used when lost mode is cleared. */ |
196 | 479k | if ((pgls->g.lost_mode == hpgl_lost_mode_entered) && |
197 | 0 | (pst->command->flags & hpgl_cdf_lost_mode_cleared) |
198 | 479k | ) |
199 | 0 | ignore_command = true; |
200 | 479k | } |
201 | | /* Also, check that we have a command that can be executed |
202 | | with the current personality. NB reorganize me. */ |
203 | 483k | if (pgls->personality == rtl) |
204 | 0 | if (!(pst->command->flags & hpgl_cdf_rtl)) /* not rtl pcl only */ |
205 | 0 | ignore_command = true; |
206 | 483k | if ((pgls->personality == pcl5c) |
207 | 483k | || (pgls->personality == pcl5e)) |
208 | 483k | if (!(pst->command->flags & hpgl_cdf_pcl)) /* not pcl rtl only */ |
209 | 406 | ignore_command = true; |
210 | 483k | if (ignore_command) |
211 | 5.08k | pst->command = 0; |
212 | 483k | } |
213 | 483k | goto call; |
214 | 685k | } |
215 | 685k | } |
216 | 121k | x:pr->ptr = p; |
217 | 121k | pst->exit_to_parser = NULL; |
218 | | |
219 | | /* |
220 | | * Within a macro we can't run out of data since all the data is |
221 | | * available in memory. |
222 | | */ |
223 | 121k | if (pgls->macro_level > 0 && (code == gs_error_NeedInput)) |
224 | 0 | code = gs_error_Fatal; |
225 | | |
226 | 121k | return (code == gs_error_NeedInput ? 0 : code); |
227 | 525k | } |
228 | | |
229 | | /* |
230 | | * Get a numeric HP-GL/2 argument from the input stream. Return 0 if no |
231 | | * argument, a pointer to the value if an argument is present, or longjmp if |
232 | | * need more data. Note that no errors are possible. |
233 | | */ |
234 | | #if defined(__clang__) && __clang__==1 |
235 | | __attribute__((optnone)) |
236 | | #endif |
237 | | static const hpgl_value_t * |
238 | | hpgl_arg(const gs_memory_t * mem, hpgl_parser_state_t * pst) |
239 | 3.03M | { |
240 | 3.03M | const byte *p; |
241 | 3.03M | const byte *rlimit; |
242 | 3.03M | hpgl_value_t *pvalue; |
243 | | |
244 | 15.1M | #define parg (&pst->arg) |
245 | 3.03M | if (parg->next < parg->count) { /* We're still replaying already-scanned arguments. */ |
246 | 982k | return &parg->scanned[parg->next++]; |
247 | 982k | } |
248 | 2.05M | if (pst->done) |
249 | 1.33M | return 0; |
250 | 727k | if (parg->count >= 21) |
251 | 0 | longjmp(*(pst->exit_to_parser), gs_error_syntaxerror); |
252 | | |
253 | 727k | p = pst->source.ptr; |
254 | 727k | rlimit = pst->source.limit; |
255 | 727k | pvalue = &parg->scanned[parg->count]; |
256 | 727k | #define check_value()\ |
257 | 727k | if ( parg->have_value ) goto done |
258 | | |
259 | 1.66M | for (; p < rlimit; ++p) { |
260 | 1.66M | byte ch = p[1]; |
261 | | |
262 | 1.66M | switch (ch) { |
263 | 117 | case '+': |
264 | 117 | check_value(); |
265 | 62 | parg->have_value = 1; |
266 | 62 | parg->sign = 1; |
267 | 62 | pvalue->v_n.i = 0; |
268 | 62 | break; |
269 | 1.34k | case '-': |
270 | 1.34k | check_value(); |
271 | 1.31k | parg->have_value = 1; |
272 | 1.31k | parg->sign = -1; |
273 | 1.31k | pvalue->v_n.i = 0; |
274 | 1.31k | break; |
275 | 7.05k | case '.': |
276 | 7.05k | switch (parg->have_value) { |
277 | 6 | default: /* > 1 */ |
278 | 6 | goto out; |
279 | 1.09k | case 0: |
280 | 1.09k | pvalue->v_n.r = 0; |
281 | 1.09k | break; |
282 | 5.96k | case 1: |
283 | 5.96k | { |
284 | | /* Strictly speaking assigning one element of union |
285 | | * to another, overlapping element of a different size is |
286 | | * undefined behavior, hence assign to an intermediate variable |
287 | | */ |
288 | 5.96k | double r = (double)pvalue->v_n.i; |
289 | 5.96k | pvalue->v_n.r = r; |
290 | 5.96k | } |
291 | 7.05k | } |
292 | 7.05k | parg->have_value = 2; |
293 | 7.05k | parg->frac_scale = 1.0; |
294 | 7.05k | break; |
295 | 158k | case ';': |
296 | 158k | pst->done = true; |
297 | 158k | check_value(); |
298 | 38.6k | goto out; |
299 | 38.6k | case HT: |
300 | 239 | case LF: |
301 | 256 | case FF: |
302 | 329 | case CR: |
303 | | /* control charachers are ignored during parsing hpgl |
304 | | */ |
305 | 329 | continue; |
306 | 575 | case SP: |
307 | 389k | case ',': |
308 | | /* |
309 | | * The specification doesn't say what to do with extra |
310 | | * separators; we just ignore them. |
311 | | */ |
312 | 389k | if (!parg->have_value) |
313 | 685 | break; |
314 | 388k | ++p; |
315 | 617k | done:if (parg->sign < 0) { |
316 | 1.31k | if (parg->have_value > 1) |
317 | 2 | pvalue->v_n.r = -pvalue->v_n.r; |
318 | 1.30k | else |
319 | 1.30k | pvalue->v_n.i = -pvalue->v_n.i; |
320 | 1.31k | } |
321 | 617k | goto out; |
322 | 499k | case '0': |
323 | 632k | case '1': |
324 | 709k | case '2': |
325 | 735k | case '3': |
326 | 793k | case '4': |
327 | 861k | case '5': |
328 | 881k | case '6': |
329 | 895k | case '7': |
330 | 912k | case '8': |
331 | 925k | case '9': |
332 | 925k | ch -= '0'; |
333 | 925k | #define max_i 0x7fffffff |
334 | 925k | switch (parg->have_value) { |
335 | 6.15k | default: /* case 2 */ |
336 | 6.15k | pvalue->v_n.r += ch / (parg->frac_scale *= 10); |
337 | 6.15k | break; |
338 | 614k | case 0: |
339 | 614k | parg->have_value = 1; |
340 | 614k | pvalue->v_n.i = ch; |
341 | 614k | break; |
342 | 304k | case 1: |
343 | 304k | if (pvalue->v_n.i >= max_i / 10 && |
344 | 4 | (pvalue->v_n.i > max_i / 10 || ch > max_i % 10) |
345 | 304k | ) |
346 | 4 | return NULL; |
347 | 304k | else |
348 | 304k | pvalue->v_n.i = pvalue->v_n.i * 10 + ch; |
349 | 925k | } |
350 | 925k | break; |
351 | 925k | default: |
352 | 179k | pst->done = true; |
353 | 179k | check_value(); |
354 | 70.6k | goto out; |
355 | 1.66M | } |
356 | 1.66M | } |
357 | | /* We ran out of data before reaching a terminator. */ |
358 | 724 | pst->source.ptr = p; |
359 | 724 | longjmp(*(pst->exit_to_parser), gs_error_NeedInput); |
360 | | /* NOTREACHED */ |
361 | 726k | out:pst->source.ptr = p; |
362 | 726k | switch (parg->have_value) { |
363 | 109k | case 0: /* no argument */ |
364 | 109k | return NULL; |
365 | 610k | case 1: /* integer */ |
366 | 610k | if_debug1m('i', mem, " %ld", (long)pvalue->v_n.i); |
367 | 610k | pvalue->is_real = false; |
368 | 610k | break; |
369 | 7.05k | default /* case 2 */ : /* real */ |
370 | 7.05k | if_debug1m('i', mem, " %g", pvalue->v_n.r); |
371 | 7.05k | pvalue->is_real = true; |
372 | 726k | } |
373 | 617k | hpgl_arg_init(pst); |
374 | 617k | parg->next = ++(parg->count); |
375 | 617k | return pvalue; |
376 | 726k | #undef parg |
377 | 726k | } |
378 | | |
379 | | /* Get a real argument. */ |
380 | | bool |
381 | | hpgl_arg_real(const gs_memory_t * mem, hpgl_args_t * pargs, hpgl_real_t * pr) |
382 | 1.30M | { |
383 | 1.30M | const hpgl_value_t *pvalue = hpgl_arg(mem, pargs); |
384 | | |
385 | 1.30M | if (!pvalue) |
386 | 522k | return false; |
387 | 778k | *pr = (pvalue->is_real ? pvalue->v_n.r : pvalue->v_n.i); |
388 | 778k | return true; |
389 | 1.30M | } |
390 | | |
391 | | /* Get a clamped real argument. */ |
392 | | bool |
393 | | hpgl_arg_c_real(const gs_memory_t * mem, |
394 | | hpgl_args_t * pargs, hpgl_real_t * pr) |
395 | 315k | { |
396 | 315k | const hpgl_value_t *pvalue = hpgl_arg(mem, pargs); |
397 | 315k | hpgl_real_t r; |
398 | | |
399 | 315k | if (!pvalue) |
400 | 229k | return false; |
401 | 86.5k | r = (pvalue->is_real ? pvalue->v_n.r : pvalue->v_n.i); |
402 | 86.5k | *pr = (r < -32768 ? -32768 : r > 32767 ? 32767 : r); |
403 | 86.5k | return true; |
404 | | |
405 | 315k | } |
406 | | |
407 | | /* Get an integer argument. */ |
408 | | bool |
409 | | hpgl_arg_int(const gs_memory_t * mem, hpgl_args_t * pargs, int32 * pi) |
410 | 124k | { |
411 | 124k | const hpgl_value_t *pvalue = hpgl_arg(mem, pargs); |
412 | | |
413 | 124k | if (!pvalue) |
414 | 102k | return false; |
415 | 21.9k | *pi = (pvalue->is_real ? (int32) pvalue->v_n.r : pvalue->v_n.i); |
416 | 21.9k | return true; |
417 | 124k | } |
418 | | |
419 | | /* Get a clamped integer argument. */ |
420 | | bool |
421 | | hpgl_arg_c_int(const gs_memory_t * mem, hpgl_args_t * pargs, int *pi) |
422 | 1.29M | { |
423 | 1.29M | const hpgl_value_t *pvalue = hpgl_arg(mem, pargs); |
424 | 1.29M | int32 i; |
425 | | |
426 | 1.29M | if (!pvalue) |
427 | 585k | return false; |
428 | 712k | i = (pvalue->is_real ? (int32) pvalue->v_n.r : pvalue->v_n.i); |
429 | 712k | *pi = (i < -32768 ? -32768 : i > 32767 ? 32767 : i); |
430 | 712k | return true; |
431 | 1.29M | } |
432 | | |
433 | | /* Get a "current units" argument. */ |
434 | | bool |
435 | | hpgl_arg_units(const gs_memory_t * mem, hpgl_args_t * pargs, hpgl_real_t * pu) |
436 | 1.29M | { /****** PROBABLY WRONG ******/ |
437 | 1.29M | return hpgl_arg_real(mem, pargs, pu); |
438 | 1.29M | } |
439 | | |
440 | | /* initialize the HPGL command counter */ |
441 | | int |
442 | | hpgl_init_command_index(hpgl_parser_state_t ** pgl_parser_state, |
443 | | gs_memory_t * mem) |
444 | 8.09k | { |
445 | 8.09k | hpgl_parser_state_t *pgst = |
446 | 8.09k | (hpgl_parser_state_t *) gs_alloc_bytes(mem, |
447 | 8.09k | sizeof(hpgl_parser_state_t), |
448 | 8.09k | "hpgl_init_command_index"); |
449 | | |
450 | | /* fatal */ |
451 | 8.09k | if (pgst == 0) { |
452 | 0 | *pgl_parser_state = NULL; |
453 | 0 | return -1; |
454 | 0 | } |
455 | | |
456 | 8.09k | pgst->exit_to_parser = NULL; |
457 | 8.09k | pgst->hpgl_command_next_index = 0; |
458 | | /* NB fix me the parser should not depend on this behavior the |
459 | | previous design had these in bss which was automatically |
460 | | cleared to zero. */ |
461 | 8.09k | memset(pgst->hpgl_command_indices, 0, sizeof(pgst->hpgl_command_indices)); |
462 | 8.09k | hpgl_process_init(pgst); |
463 | 8.09k | *pgl_parser_state = pgst; |
464 | 8.09k | return 0; |
465 | 8.09k | } |