Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/gpdl/psitop.c
Line
Count
Source
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
/* psitop.c */
17
/* Top-level API implementation of PS Language Interface */
18
19
#include "stdio_.h"
20
#include "ghost.h"
21
#include "imain.h"
22
#include "imainarg.h"
23
#include "iapi.h"
24
#include "psapi.h"
25
#include "string_.h"
26
#include "gdebug.h"
27
#include "gp.h"
28
#include "gserrors.h"
29
#include "gstypes.h"
30
#include "gsmemory.h"
31
#include "gsmalloc.h"
32
#include "gsstate.h"    /* must precede gsdevice.h */
33
#include "gxdevice.h"   /* must precede gsdevice.h */
34
#include "gsdevice.h"
35
#include "icstate.h"    /* for i_ctx_t */
36
#include "iminst.h"
37
#include "gsstruct.h"   /* for gxalloc.h */
38
#include "gspaint.h"
39
#include "gxalloc.h"
40
#include "gxstate.h"
41
#include "plparse.h"
42
#include "pltop.h"
43
#include "plmain.h"
44
#include "gzstate.h"
45
#include "gsicc_manage.h"
46
47
/* Forward decls */
48
49
/************************************************************/
50
/******** Language wrapper implementation (see pltop.h) *****/
51
/************************************************************/
52
53
/* Import operator procedures */
54
extern int zflush(i_ctx_t *);
55
56
/*
57
 * PS interpreter instance: derived from pl_interp_implementation_t
58
 */
59
typedef struct ps_interp_instance_s {
60
    gs_memory_t  *memory;
61
    uint          bytes_fed;
62
    gs_lib_ctx_t *psapi_instance;
63
} ps_interp_instance_t;
64
65
static int
66
check_token(int token_type, const char *s, const char *e, int *score)
67
3.35M
{
68
    /* Empty tokens are always OK */
69
3.35M
    if (s == e)
70
2.75M
        return 0;
71
72
602k
    switch (token_type) {
73
3.13k
        case 'a':
74
            /* Angle bracket - mainly to catch << */
75
3.13k
            break;
76
5.43k
        case 'n':
77
            /* Name */
78
            /* FIXME: Check it's a valid name */
79
5.43k
            break;
80
227
        case 'd':
81
            /* Dictionary */
82
227
            break;
83
68.3k
        case 'i':
84
            /* int - ok by construction. */
85
68.3k
            return 0;
86
9.82k
        case 'f':
87
            /* float - ok by construction. */
88
9.82k
            return 0;
89
602k
    }
90
91
8.38M
#define TOKEN_CHECK(n) else if (e-s == strlen(n) && memcmp(s, n, e-s) == 0) { score[0] += e-s; score[1]++; }
92
93
524k
    if (0) { /* Initial block of checking chain */ }
94
524k
    TOKEN_CHECK("dup")
95
524k
    TOKEN_CHECK("exch")
96
524k
    TOKEN_CHECK("grestore")
97
524k
    TOKEN_CHECK("gsave")
98
524k
    TOKEN_CHECK("idiv")
99
524k
    TOKEN_CHECK("lineto")
100
524k
    TOKEN_CHECK("mod")
101
524k
    TOKEN_CHECK("mul")
102
524k
    TOKEN_CHECK("moveto")
103
524k
    TOKEN_CHECK("setflat")
104
524k
    TOKEN_CHECK("setlinecap")
105
524k
    TOKEN_CHECK("setlinejoin")
106
524k
    TOKEN_CHECK("showpage")
107
524k
    TOKEN_CHECK("stroke")
108
524k
    TOKEN_CHECK("translate")
109
524k
    TOKEN_CHECK("systemdict")
110
111
524k
    if (score[0] > 1024 && score[1] >= 3)
112
0
        return 1;
113
524k
    if (score[0] < -1024)
114
603
        return 1;
115
116
523k
    return 0;
117
524k
}
118
119
static int
120
score_comment(const char *s, const char *e, int *score)
121
10.0k
{
122
125k
#define COMMENT_CHECK(n) else if (e-s >= strlen(n) && memcmp(s, n, strlen(n)) == 0) { score[0] += 100; score[1]++; }
123
124
10.0k
    if (0) { /* Initial block of checking chain */ }
125
10.0k
    COMMENT_CHECK("!PS")
126
10.0k
    COMMENT_CHECK("%Title:")
127
9.65k
    COMMENT_CHECK("%Version:")
128
9.64k
    COMMENT_CHECK("%Creator:")
129
9.64k
    COMMENT_CHECK("%CreationDate:")
130
9.64k
    COMMENT_CHECK("%Document")
131
9.63k
    COMMENT_CHECK("%BoundingBox:")
132
9.58k
    COMMENT_CHECK("%HiResBoundingBox:")
133
9.58k
    COMMENT_CHECK("%Pages:")
134
9.57k
    COMMENT_CHECK("%+ procset")
135
9.57k
    COMMENT_CHECK("%End")
136
9.57k
    COMMENT_CHECK("%Begin")
137
9.54k
    COMMENT_CHECK("%Copyright")
138
9.53k
    else {
139
9.53k
        score[0]++; score[1]++;
140
9.53k
    }
141
142
10.0k
    if (score[0] > 1024 && score[1] >= 3)
143
4
        return 1;
144
145
10.0k
    return 0;
146
10.0k
}
147
148
static int
149
ps_detect_language(const char *s, int len)
150
28.0k
{
151
    /* For postscript, we look for %! */
152
28.0k
    if (len >= 2) {
153
28.0k
        if (s[0] != '%' || s[1] != '!') {
154
            /* Not what we were looking for */
155
27.4k
        } else if (len >= 12 && memcmp(s+2, "Postscript", 10) == 0) {
156
0
            return 100;
157
637
        } else if (len >= 4 && memcmp(s+2, "PS", 2) == 0) {
158
147
            return 100;
159
490
        } else if (len >= 3 && s[2] == '/') {
160
            /* Looks like a shell script. Don't want that. */
161
0
            return 0;
162
490
        } else {
163
            /* If it begins %!, then it's PROBABLY postscript */
164
490
            return 80;
165
490
        }
166
28.0k
    }
167
    /* For PDF, we allow for leading crap, then a postscript version marker */
168
27.4k
    {
169
27.4k
        const char *t = s;
170
27.4k
        int left = len-22;
171
        /* Search within just the first 4K, plus a bit for the marker length. */
172
27.4k
        if (left > 4096+22)
173
0
            left = 4096+22;
174
28.2M
        while (left > 22) {
175
28.2M
            if (memcmp(t, "%PDF-", 5) == 0 &&
176
1.11k
                t[5] >= '1' && t[5] <= '9' &&
177
867
                t[6] == '.' &&
178
813
                t[7] >= '0' && t[7] <= '9') {
179
795
                return 100;
180
795
            }
181
28.2M
            if (memcmp(t, "%!PS-Adobe-", 11) == 0 &&
182
784
                t[11] >= '0' && t[11] <= '9' &&
183
749
                t[12] == '.' &&
184
695
                t[13] >= '0' && t[13] <= '9' &&
185
304
                memcmp(t+14, " PDF-", 5) == 0 &&
186
0
                t[19] >= '0' && t[19] <= '9' &&
187
0
                t[20] == '.' &&
188
0
                t[21] >= '0' && t[21] <= '9') {
189
0
                return 100;
190
0
            }
191
28.2M
            t++;
192
28.2M
            left--;
193
28.2M
        }
194
27.4k
    }
195
196
    /* Now we do some seriously hairy stuff */
197
26.6k
    {
198
26.6k
        const char *t = s;
199
26.6k
        const char *token = t;
200
26.6k
        int left = len;
201
26.6k
        int token_type = 0;
202
26.6k
        int score[2] = { 0, 0 };
203
204
5.08M
        while (left--) {
205
5.07M
            if (*t == '%') {
206
10.0k
                if (check_token(token_type, token, t, score))
207
2
                    break;
208
                /* Skip to end of line */
209
10.0k
                left--;
210
10.0k
                token = ++t;
211
1.55M
                while (left && *t != '\r' && *t != '\n') {
212
1.54M
                    left--; t++;
213
1.54M
                }
214
10.0k
                if (score_comment(token, t, score))
215
4
                    break;
216
                /* Skip any combination of newlines */
217
21.0k
                while (left && (*t == '\r' || *t == '\n')) {
218
11.0k
                    left--; t++;
219
11.0k
                }
220
10.0k
                token_type = 0;
221
10.0k
                continue;
222
5.06M
            } else if (*t == 27) {
223
                /* Give up if we meet an ESC. It could be a UEL. */
224
15.6k
                break;
225
5.04M
            } else if (*t <= 32 || *(unsigned char *)t > 127) {
226
3.20M
                if (check_token(token_type, token, t, score))
227
560
                    break;
228
3.20M
                if (*t != 9 && *t != 10 && *t != 12 && *t != 13 && *t != 32)
229
2.75M
                    score[0]--;
230
3.20M
                token = t+1;
231
3.20M
                token_type = 0;
232
3.20M
            } else if (*t == '/') {
233
15.8k
                if (check_token(token_type, token, t, score))
234
5
                    break;
235
15.8k
                token = t+1;
236
15.8k
                token_type = 'n';
237
1.82M
            } else if (*t == '[' || *t == ']' || *t == '{' || *t == '}') {
238
32.2k
                if (check_token(token_type, token, t, score))
239
2
                    break;
240
32.2k
                token = t+1;
241
32.2k
                token_type = 0;
242
1.79M
            } else if (*t == '<') {
243
16.6k
                if (token_type == 'a') {
244
                    /* << */
245
380
                    token = t+1;
246
380
                    token_type = 'd';
247
16.3k
                } else if (token_type == 'd') {
248
                    /* <<< ???!? */
249
5.50k
                    score[0] -= 10;
250
10.8k
                } else {
251
10.8k
                    if (check_token(token_type, token, t, score))
252
11
                        break;
253
10.7k
                    token = t+1;
254
10.7k
                    token_type = 'a';
255
10.7k
                }
256
1.78M
            } else if (*t == '>') {
257
4.95k
                if (check_token(token_type, token, t, score))
258
5
                    break;
259
4.95k
                token = t+1;
260
4.95k
                token_type = 0;
261
1.77M
            } else if (*t >= '0' && *t <= '9') {
262
200k
                if (token_type == 'i') {
263
                    /* Keep going */
264
114k
                } else if (token_type == 'f') {
265
                    /* Keep going */
266
75.2k
                } else {
267
75.2k
                    if (check_token(token_type, token, t, score))
268
16
                        break;
269
75.2k
                    token = t;
270
75.2k
                    token_type = 'i';
271
75.2k
                }
272
1.57M
            } else if (*t == '.') {
273
13.9k
                if (token_type == 'f') {
274
                    /* seems unlikely */
275
1.80k
                    score[0]--;
276
1.80k
                    break;
277
12.1k
                } else if (token_type == 'i') {
278
6.06k
                    token = t;
279
6.06k
                    token_type = 'f';
280
6.10k
                } else {
281
6.10k
                    if (check_token(token_type, token, t, score))
282
2
                        break;
283
6.09k
                    token = t;
284
6.09k
                    token_type = 'f';
285
6.09k
                }
286
1.56M
            } else {
287
                /* Assume anything else goes into the token */
288
1.56M
            }
289
5.04M
            t++;
290
5.04M
        }
291
26.6k
        if (score[0] < 0 || score[1] < 3)
292
26.4k
            return 0; /* Unlikely to be PS */
293
153
        else if (score[0] > 0)
294
139
            return 75; /* Could be PS */
295
26.6k
    }
296
297
14
    return 0;
298
26.6k
}
299
300
/* Get implementation's characteristics */
301
static const pl_interp_characteristics_t * /* always returns a descriptor */
302
ps_impl_characteristics(const pl_interp_implementation_t *impl)     /* implementation of interpreter to alloc */
303
59.1k
{
304
    /* version and build date are not currently used */
305
59.1k
  static const pl_interp_characteristics_t ps_characteristics = {
306
59.1k
    "POSTSCRIPT",
307
59.1k
    ps_detect_language,
308
59.1k
  };
309
310
59.1k
  return &ps_characteristics;
311
59.1k
}
312
313
/* Do per-instance interpreter allocation/init. */
314
static int
315
ps_impl_allocate_interp_instance(pl_interp_implementation_t *impl, gs_memory_t *mem)
316
16.1k
{
317
16.1k
    ps_interp_instance_t *psi
318
16.1k
        = (ps_interp_instance_t *)gs_alloc_bytes( mem,
319
16.1k
                                                  sizeof(ps_interp_instance_t),
320
16.1k
                                                  "ps_impl_allocate_interp_instance");
321
322
16.1k
    int code;
323
16.1k
#define GS_MAX_NUM_ARGS 10
324
16.1k
    const char *gsargs[GS_MAX_NUM_ARGS] = {0};
325
16.1k
    int nargs = 0;
326
327
16.1k
    if (!psi)
328
0
        return gs_error_VMerror;
329
330
16.1k
    gsargs[nargs++] = "gpdl";
331
    /* We start gs with the nullpage device, and replace the device with the
332
     * set_device call from the language independent code.
333
     */
334
16.1k
    gsargs[nargs++] = "-sDEVICE=nullpage";
335
    /* As we're "printer targetted, use a jobserver */
336
16.1k
    gsargs[nargs++] = "-dJOBSERVER";
337
338
16.1k
    psi->memory = mem;
339
16.1k
    psi->bytes_fed = 0;
340
16.1k
    psi->psapi_instance = gs_lib_ctx_get_interp_instance(mem);
341
16.1k
    code = psapi_new_instance(&psi->psapi_instance, NULL);
342
16.1k
    if (code < 0) {
343
0
        gs_free_object(mem, psi, "ps_impl_allocate_interp_instance");
344
0
        return code;
345
0
    }
346
347
    /* The above call to psapi_new_instance will have set the ps interpreter
348
     * to expect 'local' encoding. When running under PL, this means we'll
349
     * end up decoding the input stuff to utf8, and then feed that into the
350
     * gs instance, where it will decode it again! Avoid this, by setting
351
     * gs to expect UTF8 input. */
352
16.1k
    psapi_set_arg_encoding(psi->psapi_instance, PS_ARG_ENCODING_UTF8);
353
354
16.1k
    impl->interp_client_data = psi;
355
356
    /* Tell gs not to ignore a UEL, but do an interpreter exit */
357
16.1k
    psapi_act_on_uel(psi->psapi_instance);
358
359
16.1k
    code = psapi_init_with_args01(psi->psapi_instance, nargs, (char **)gsargs);
360
16.1k
    if (code < 0) {
361
0
        (void)psapi_exit(psi->psapi_instance);
362
0
        psapi_delete_instance(psi->psapi_instance);
363
0
        gs_free_object(mem, psi, "ps_impl_allocate_interp_instance");
364
0
    }
365
16.1k
    return code;
366
16.1k
}
367
368
/*
369
 * Get the allocator with which to allocate a device
370
 */
371
static gs_memory_t *
372
ps_impl_get_device_memory(pl_interp_implementation_t *impl)
373
16.1k
{
374
16.1k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
375
376
16.1k
    return psapi_get_device_memory(psi->psapi_instance);
377
16.1k
}
378
379
static int
380
ps_impl_set_param(pl_interp_implementation_t *impl,
381
                  gs_param_list *plist)
382
64.6k
{
383
64.6k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
384
385
64.6k
    return psapi_set_param(psi->psapi_instance, plist);
386
64.6k
}
387
388
static int
389
ps_impl_add_path(pl_interp_implementation_t *impl,
390
                 const char                 *path)
391
0
{
392
0
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
393
394
0
    return psapi_add_path(psi->psapi_instance, path);
395
0
}
396
397
static int
398
ps_impl_post_args_init(pl_interp_implementation_t *impl)
399
16.1k
{
400
16.1k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
401
16.1k
    const float *resolutions;
402
16.1k
    const long *page_sizes;
403
404
16.1k
    pl_main_get_forced_geometry(psi->memory, &resolutions, &page_sizes);
405
16.1k
    psapi_force_geometry(psi->psapi_instance, resolutions, page_sizes);
406
407
16.1k
    return psapi_init_with_args2(psi->psapi_instance);
408
16.1k
}
409
410
/* Prepare interp instance for the next "job" */
411
static int
412
ps_impl_init_job(pl_interp_implementation_t *impl,
413
                 gx_device                  *device)
414
1.30k
{
415
1.30k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
416
1.30k
    int exit_code;
417
1.30k
    int code;
418
419
    /* Any error after here *must* reset the device to null */
420
1.30k
    code = psapi_set_device(psi->psapi_instance, device);
421
422
1.30k
    if (code >= 0)
423
1.30k
        code = psapi_run_string(psi->psapi_instance, "erasepage", 0, &exit_code);
424
425
1.30k
    if (code < 0) {
426
0
        int code1 = psapi_set_device(psi->psapi_instance, NULL);
427
0
        (void)code1;
428
0
    }
429
430
    /* Make sure the PageSpotColors is set to -1 for PS */
431
1.30k
    {
432
1.30k
        gs_c_param_list* params;
433
1.30k
        int page_spot_colors = -1;
434
1.30k
        int code2;
435
436
1.30k
        params = gs_c_param_list_alloc(psi->memory, "ps_impl_init_job");
437
1.30k
        if (params == NULL)
438
0
            return_error(gs_error_VMerror);
439
440
1.30k
        gs_c_param_list_write(params, psi->memory);
441
1.30k
        gs_param_list_set_persistent_keys((gs_param_list*)params, false);
442
443
1.30k
        code2 = param_write_int((gs_param_list*)params, "PageSpotColors", &(page_spot_colors));
444
1.30k
        if (code2 < 0) {
445
0
            gs_c_param_list_free(psi->memory, params, "ps_impl_init_job");
446
0
            return code2;
447
0
        }
448
449
1.30k
        gs_c_param_list_read(params);
450
451
1.30k
        code2 = psapi_set_device_param(psi->psapi_instance, (gs_param_list*)params);
452
1.30k
        gs_c_param_list_free(psi->memory, params, "ps_impl_init_job");
453
1.30k
        if (code2 < 0)
454
0
            return code2;
455
1.30k
    }
456
1.30k
    return code;
457
1.30k
}
458
459
static int
460
ps_impl_run_prefix_commands(pl_interp_implementation_t *impl,
461
                            const char                 *prefix)
462
0
{
463
0
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
464
0
    int exit_code;
465
0
    int code = 0;
466
467
0
    if (prefix == NULL)
468
0
        return 0;
469
470
    /* Any error after here *must* reset the device to null */
471
0
    code = psapi_run_string(psi->psapi_instance, prefix, 0, &exit_code);
472
473
0
    if (code < 0) {
474
0
        int code1 = psapi_set_device(psi->psapi_instance, NULL);
475
0
        (void)code1;
476
0
    }
477
478
0
    return code;
479
0
}
480
481
static int
482
ps_impl_process_file(pl_interp_implementation_t *impl, const char *filename)
483
1.26k
{
484
1.26k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
485
1.26k
    int exit_code;
486
487
1.26k
    return psapi_run_file(psi->psapi_instance, filename, 0, &exit_code);
488
1.26k
}
489
490
/* Do any setup for parser per-cursor */
491
static int                      /* ret 0 or +ve if ok, else -ve error code */
492
ps_impl_process_begin(pl_interp_implementation_t * impl)
493
44
{
494
44
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
495
44
    int exit_code;
496
497
44
    psi->bytes_fed = 0;
498
44
    return psapi_run_string_begin(psi->psapi_instance, 0, &exit_code);
499
44
}
500
501
/* TODO: in some fashion have gs pass back how far into the input buffer it
502
 * had read, so we don't need to explicitly search the buffer for the UEL
503
 */
504
static int
505
ps_impl_process(pl_interp_implementation_t * impl, stream_cursor_read * pr)
506
44
{
507
44
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
508
44
    unsigned int len;
509
44
    int code, exit_code = 0;
510
511
44
    if (psi->bytes_fed == 0)
512
44
    {
513
        /* Skip over whitespace/comments looking for a PDF marker. */
514
188
        while (pr->ptr < pr->limit)
515
188
        {
516
188
            int i;
517
518
            /* Skip over whitespace (as defined in PLRM) */
519
188
            if (pr->ptr[1] == 0 ||
520
182
                pr->ptr[1] == 9 ||
521
182
                pr->ptr[1] == 10 ||
522
182
                pr->ptr[1] == 12 ||
523
182
                pr->ptr[1] == 13 ||
524
182
                pr->ptr[1] == 32) {
525
144
                pr->ptr++;
526
144
                continue;
527
144
            }
528
529
            /* If we're not starting a comment, exit. */
530
44
            if (pr->ptr[1] != '%')
531
44
                break;
532
533
            /* If we're starting with a PDF header, swap to file mode. */
534
0
            if (pr->limit - pr->ptr >= 8 &&
535
0
                strncmp((const char *)&pr->ptr[2], "PDF-", 4) == 0 &&
536
0
                (pr->ptr[6] >= '1' && pr->ptr[6] <= '9') &&
537
0
                pr->ptr[7] == '.' &&
538
0
                (pr->ptr[8] >= '0' && pr->ptr[8] <= '9'))
539
0
                return_error(gs_error_NeedFile);
540
541
            /* Check for a historical PDF header. */
542
0
            if (pr->limit - pr->ptr >= 22 &&
543
0
                strncmp((const char *)&pr->ptr[2], "!PS-Adobe-", 10) == 0 &&
544
0
                (pr->ptr[12] >= '0' && pr->ptr[12] <= '9') &&
545
0
                pr->ptr[13] == '.' &&
546
0
                (pr->ptr[14] >= '0' && pr->ptr[14] <= '9') &&
547
0
                strncmp((const char *)&pr->ptr[15], " PDF-", 5) == 0 &&
548
0
                (pr->ptr[20] >= '0' && pr->ptr[20] <= '9') &&
549
0
                pr->ptr[21] == '.' &&
550
0
                (pr->ptr[22] >= '0' && pr->ptr[22] <= '9'))
551
0
                return_error(gs_error_NeedFile);
552
553
            /* Do we have a complete comment that we can skip? */
554
0
            for (i = 1; pr->ptr + i < pr->limit; i++)
555
0
                if (pr->ptr[i+1] == 10 || pr->ptr[i+1] == 13) {
556
0
                    pr->ptr += i;
557
0
                    i = 0; /* Loop again in case there are more comments. */
558
0
                    break;
559
0
                }
560
            /* If we fall out of the loop naturally, then we hit the end
561
             * of the buffer without terminating our comment. We need to
562
             * abort the loop and return. */
563
0
            if (i != 0)
564
0
                return_error(gs_error_NeedInput);
565
0
        }
566
44
    }
567
568
44
    len = pr->limit - pr->ptr;
569
44
    code = psapi_run_string_continue(psi->psapi_instance, (const char *)pr->ptr + 1, len, 0, &exit_code);
570
44
    if (exit_code == gs_error_InterpreterExit) {
571
0
        int64_t offset;
572
573
0
        offset = psapi_get_uel_offset(psi->psapi_instance) - psi->bytes_fed;
574
0
        pr->ptr += offset;
575
0
        psi->bytes_fed += offset + 1;
576
577
#ifdef SEND_CTRLD_AFTER_UEL
578
        {
579
            const char eot[1] = {4};
580
            code = psapi_run_string_continue(psi->psapi_instance, eot, 1, 0, &exit_code);
581
            (void)code; /* Ignoring code here */
582
        }
583
#endif
584
0
        return gs_error_InterpreterExit;
585
0
    }
586
44
    else {
587
44
        pr->ptr = pr->limit;
588
44
        psi->bytes_fed += len;
589
44
    }
590
44
    return code;
591
44
}
592
593
static int                      /* ret 0 or +ve if ok, else -ve error code */
594
ps_impl_process_end(pl_interp_implementation_t * impl)
595
44
{
596
44
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
597
44
    int exit_code, code;
598
599
44
    code = psapi_run_string_end(psi->psapi_instance, 0, &exit_code);
600
601
44
    if (exit_code == gs_error_InterpreterExit || code == gs_error_NeedInput)
602
0
        code = 0;
603
604
44
    return code;
605
44
}
606
607
/* Not implemented */
608
static int
609
ps_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
610
71
{
611
71
    const byte *p = cursor->ptr;
612
71
    const byte *rlimit = cursor->limit;
613
614
    /* Skip to, but leave UEL in buffer for PJL to find later */
615
34.7k
    for (; p < rlimit; ++p)
616
34.6k
        if (p[1] == '\033') {
617
27
            uint avail = rlimit - p;
618
619
27
            if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
620
20
                continue;
621
7
            if (avail < 9)
622
1
                break;
623
6
            cursor->ptr = p;
624
6
            return 1;           /* found eoj */
625
7
        }
626
65
    cursor->ptr = p;
627
65
    return 0;                   /* need more */
628
71
}
629
630
/* Parser action for end-of-file */
631
static int
632
ps_impl_process_eof(pl_interp_implementation_t *impl)
633
0
{
634
0
    return 0;
635
0
}
636
637
/* Report any errors after running a job */
638
static int
639
ps_impl_report_errors(pl_interp_implementation_t *impl,      /* interp instance to wrap up job in */
640
                      int                  code,           /* prev termination status */
641
                      long                 file_position,  /* file position of error, -1 if unknown */
642
                      bool                 force_to_cout    /* force errors to cout */
643
)
644
44
{
645
44
    return 0;
646
44
}
647
648
/* Wrap up interp instance after a "job" */
649
static int
650
ps_impl_dnit_job(pl_interp_implementation_t *impl)
651
1.30k
{
652
1.30k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
653
654
1.30k
    return psapi_set_device(psi->psapi_instance, NULL);
655
1.30k
}
656
657
/* Deallocate a interpreter instance */
658
static int
659
ps_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
660
16.1k
{
661
16.1k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
662
16.1k
    int                   code;
663
664
16.1k
    if (psi == NULL)
665
0
        return 0;
666
16.1k
    code = psapi_exit(psi->psapi_instance);
667
16.1k
    psapi_delete_instance(psi->psapi_instance);
668
16.1k
    gs_free_object(psi->memory, psi, "ps_impl_allocate_interp_instance");
669
    impl->interp_client_data = NULL;
670
16.1k
    return code;
671
16.1k
}
672
673
/* Parser implementation descriptor */
674
const pl_interp_implementation_t ps_implementation = {
675
  ps_impl_characteristics,
676
  ps_impl_allocate_interp_instance,
677
  ps_impl_get_device_memory,
678
  ps_impl_set_param,
679
  ps_impl_add_path,
680
  ps_impl_post_args_init,
681
  ps_impl_init_job,
682
  ps_impl_run_prefix_commands,
683
  ps_impl_process_file,
684
  ps_impl_process_begin,
685
  ps_impl_process,
686
  ps_impl_process_end,
687
  ps_impl_flush_to_eoj,
688
  ps_impl_process_eof,
689
  ps_impl_report_errors,
690
  ps_impl_dnit_job,
691
  ps_impl_deallocate_interp_instance,
692
  NULL, /* ps_impl_reset */
693
  NULL  /* interp_client_data */
694
};