Coverage Report

Created: 2026-04-01 07:17

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}