Coverage Report

Created: 2026-04-09 07:06

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
2.34M
{
68
    /* Empty tokens are always OK */
69
2.34M
    if (s == e)
70
1.84M
        return 0;
71
72
503k
    switch (token_type) {
73
3.30k
        case 'a':
74
            /* Angle bracket - mainly to catch << */
75
3.30k
            break;
76
2.80k
        case 'n':
77
            /* Name */
78
            /* FIXME: Check it's a valid name */
79
2.80k
            break;
80
179
        case 'd':
81
            /* Dictionary */
82
179
            break;
83
55.7k
        case 'i':
84
            /* int - ok by construction. */
85
55.7k
            return 0;
86
8.46k
        case 'f':
87
            /* float - ok by construction. */
88
8.46k
            return 0;
89
503k
    }
90
91
7.02M
#define TOKEN_CHECK(n) else if (e-s == strlen(n) && memcmp(s, n, e-s) == 0) { score[0] += e-s; score[1]++; }
92
93
439k
    if (0) { /* Initial block of checking chain */ }
94
439k
    TOKEN_CHECK("dup")
95
439k
    TOKEN_CHECK("exch")
96
438k
    TOKEN_CHECK("grestore")
97
438k
    TOKEN_CHECK("gsave")
98
438k
    TOKEN_CHECK("idiv")
99
438k
    TOKEN_CHECK("lineto")
100
438k
    TOKEN_CHECK("mod")
101
438k
    TOKEN_CHECK("mul")
102
438k
    TOKEN_CHECK("moveto")
103
438k
    TOKEN_CHECK("setflat")
104
438k
    TOKEN_CHECK("setlinecap")
105
438k
    TOKEN_CHECK("setlinejoin")
106
438k
    TOKEN_CHECK("showpage")
107
438k
    TOKEN_CHECK("stroke")
108
438k
    TOKEN_CHECK("translate")
109
438k
    TOKEN_CHECK("systemdict")
110
111
439k
    if (score[0] > 1024 && score[1] >= 3)
112
0
        return 1;
113
439k
    if (score[0] < -1024)
114
553
        return 1;
115
116
438k
    return 0;
117
439k
}
118
119
static int
120
score_comment(const char *s, const char *e, int *score)
121
6.78k
{
122
87.2k
#define COMMENT_CHECK(n) else if (e-s >= strlen(n) && memcmp(s, n, strlen(n)) == 0) { score[0] += 100; score[1]++; }
123
124
6.78k
    if (0) { /* Initial block of checking chain */ }
125
6.78k
    COMMENT_CHECK("!PS")
126
6.78k
    COMMENT_CHECK("%Title:")
127
6.69k
    COMMENT_CHECK("%Version:")
128
6.69k
    COMMENT_CHECK("%Creator:")
129
6.69k
    COMMENT_CHECK("%CreationDate:")
130
6.69k
    COMMENT_CHECK("%Document")
131
6.69k
    COMMENT_CHECK("%BoundingBox:")
132
6.69k
    COMMENT_CHECK("%HiResBoundingBox:")
133
6.69k
    COMMENT_CHECK("%Pages:")
134
6.69k
    COMMENT_CHECK("%+ procset")
135
6.69k
    COMMENT_CHECK("%End")
136
6.69k
    COMMENT_CHECK("%Begin")
137
6.69k
    COMMENT_CHECK("%Copyright")
138
6.69k
    else {
139
6.69k
        score[0]++; score[1]++;
140
6.69k
    }
141
142
6.78k
    if (score[0] > 1024 && score[1] >= 3)
143
1
        return 1;
144
145
6.78k
    return 0;
146
6.78k
}
147
148
static int
149
ps_detect_language(const char *s, int len)
150
19.2k
{
151
    /* For postscript, we look for %! */
152
19.2k
    if (len >= 2) {
153
19.2k
        if (s[0] != '%' || s[1] != '!') {
154
            /* Not what we were looking for */
155
19.1k
        } else if (len >= 12 && memcmp(s+2, "Postscript", 10) == 0) {
156
0
            return 100;
157
40
        } else if (len >= 4 && memcmp(s+2, "PS", 2) == 0) {
158
0
            return 100;
159
40
        } else if (len >= 3 && s[2] == '/') {
160
            /* Looks like a shell script. Don't want that. */
161
0
            return 0;
162
40
        } else {
163
            /* If it begins %!, then it's PROBABLY postscript */
164
40
            return 80;
165
40
        }
166
19.2k
    }
167
    /* For PDF, we allow for leading crap, then a postscript version marker */
168
19.1k
    {
169
19.1k
        const char *t = s;
170
19.1k
        int left = len-22;
171
        /* Search within just the first 4K, plus a bit for the marker length. */
172
19.1k
        if (left > 4096+22)
173
0
            left = 4096+22;
174
23.7M
        while (left > 22) {
175
23.7M
            if (memcmp(t, "%PDF-", 5) == 0 &&
176
151
                t[5] >= '1' && t[5] <= '9' &&
177
47
                t[6] == '.' &&
178
10
                t[7] >= '0' && t[7] <= '9') {
179
10
                return 100;
180
10
            }
181
23.7M
            if (memcmp(t, "%!PS-Adobe-", 11) == 0 &&
182
51
                t[11] >= '0' && t[11] <= '9' &&
183
27
                t[12] == '.' &&
184
23
                t[13] >= '0' && t[13] <= '9' &&
185
15
                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
23.7M
            t++;
192
23.7M
            left--;
193
23.7M
        }
194
19.1k
    }
195
196
    /* Now we do some seriously hairy stuff */
197
19.1k
    {
198
19.1k
        const char *t = s;
199
19.1k
        const char *token = t;
200
19.1k
        int left = len;
201
19.1k
        int token_type = 0;
202
19.1k
        int score[2] = { 0, 0 };
203
204
3.71M
        while (left--) {
205
3.70M
            if (*t == '%') {
206
6.78k
                if (check_token(token_type, token, t, score))
207
2
                    break;
208
                /* Skip to end of line */
209
6.78k
                left--;
210
6.78k
                token = ++t;
211
1.36M
                while (left && *t != '\r' && *t != '\n') {
212
1.35M
                    left--; t++;
213
1.35M
                }
214
6.78k
                if (score_comment(token, t, score))
215
1
                    break;
216
                /* Skip any combination of newlines */
217
13.6k
                while (left && (*t == '\r' || *t == '\n')) {
218
6.87k
                    left--; t++;
219
6.87k
                }
220
6.78k
                token_type = 0;
221
6.78k
                continue;
222
3.70M
            } else if (*t == 27) {
223
                /* Give up if we meet an ESC. It could be a UEL. */
224
13.0k
                break;
225
3.68M
            } else if (*t <= 32 || *(unsigned char *)t > 127) {
226
2.21M
                if (check_token(token_type, token, t, score))
227
500
                    break;
228
2.21M
                if (*t != 9 && *t != 10 && *t != 12 && *t != 13 && *t != 32)
229
1.98M
                    score[0]--;
230
2.21M
                token = t+1;
231
2.21M
                token_type = 0;
232
2.21M
            } else if (*t == '/') {
233
13.2k
                if (check_token(token_type, token, t, score))
234
5
                    break;
235
13.2k
                token = t+1;
236
13.2k
                token_type = 'n';
237
1.46M
            } else if (*t == '[' || *t == ']' || *t == '{' || *t == '}') {
238
31.7k
                if (check_token(token_type, token, t, score))
239
7
                    break;
240
31.7k
                token = t+1;
241
31.7k
                token_type = 0;
242
1.43M
            } else if (*t == '<') {
243
12.3k
                if (token_type == 'a') {
244
                    /* << */
245
206
                    token = t+1;
246
206
                    token_type = 'd';
247
12.1k
                } else if (token_type == 'd') {
248
                    /* <<< ???!? */
249
1.64k
                    score[0] -= 10;
250
10.5k
                } else {
251
10.5k
                    if (check_token(token_type, token, t, score))
252
5
                        break;
253
10.5k
                    token = t+1;
254
10.5k
                    token_type = 'a';
255
10.5k
                }
256
1.41M
            } else if (*t == '>') {
257
4.33k
                if (check_token(token_type, token, t, score))
258
10
                    break;
259
4.32k
                token = t+1;
260
4.32k
                token_type = 0;
261
1.41M
            } else if (*t >= '0' && *t <= '9') {
262
181k
                if (token_type == 'i') {
263
                    /* Keep going */
264
109k
                } else if (token_type == 'f') {
265
                    /* Keep going */
266
62.5k
                } else {
267
62.5k
                    if (check_token(token_type, token, t, score))
268
20
                        break;
269
62.5k
                    token = t;
270
62.5k
                    token_type = 'i';
271
62.5k
                }
272
1.23M
            } else if (*t == '.') {
273
12.9k
                if (token_type == 'f') {
274
                    /* seems unlikely */
275
2.08k
                    score[0]--;
276
2.08k
                    break;
277
10.9k
                } else if (token_type == 'i') {
278
6.23k
                    token = t;
279
6.23k
                    token_type = 'f';
280
6.23k
                } else {
281
4.66k
                    if (check_token(token_type, token, t, score))
282
4
                        break;
283
4.66k
                    token = t;
284
4.66k
                    token_type = 'f';
285
4.66k
                }
286
1.22M
            } else {
287
                /* Assume anything else goes into the token */
288
1.22M
            }
289
3.68M
            t++;
290
3.68M
        }
291
19.1k
        if (score[0] < 0 || score[1] < 3)
292
19.1k
            return 0; /* Unlikely to be PS */
293
60
        else if (score[0] > 0)
294
58
            return 75; /* Could be PS */
295
19.1k
    }
296
297
2
    return 0;
298
19.1k
}
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
40.9k
{
304
    /* version and build date are not currently used */
305
40.9k
  static const pl_interp_characteristics_t ps_characteristics = {
306
40.9k
    "POSTSCRIPT",
307
40.9k
    ps_detect_language,
308
40.9k
  };
309
310
40.9k
  return &ps_characteristics;
311
40.9k
}
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
8.97k
{
317
8.97k
    ps_interp_instance_t *psi
318
8.97k
        = (ps_interp_instance_t *)gs_alloc_bytes( mem,
319
8.97k
                                                  sizeof(ps_interp_instance_t),
320
8.97k
                                                  "ps_impl_allocate_interp_instance");
321
322
8.97k
    int code;
323
8.97k
#define GS_MAX_NUM_ARGS 10
324
8.97k
    const char *gsargs[GS_MAX_NUM_ARGS] = {0};
325
8.97k
    int nargs = 0;
326
327
8.97k
    if (!psi)
328
0
        return gs_error_VMerror;
329
330
8.97k
    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
8.97k
    gsargs[nargs++] = "-sDEVICE=nullpage";
335
    /* As we're "printer targetted, use a jobserver */
336
8.97k
    gsargs[nargs++] = "-dJOBSERVER";
337
338
8.97k
    psi->memory = mem;
339
8.97k
    psi->bytes_fed = 0;
340
8.97k
    psi->psapi_instance = gs_lib_ctx_get_interp_instance(mem);
341
8.97k
    code = psapi_new_instance(&psi->psapi_instance, NULL);
342
8.97k
    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
8.97k
    psapi_set_arg_encoding(psi->psapi_instance, PS_ARG_ENCODING_UTF8);
353
354
8.97k
    impl->interp_client_data = psi;
355
356
    /* Tell gs not to ignore a UEL, but do an interpreter exit */
357
8.97k
    psapi_act_on_uel(psi->psapi_instance);
358
359
8.97k
    code = psapi_init_with_args01(psi->psapi_instance, nargs, (char **)gsargs);
360
8.97k
    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
8.97k
    return code;
366
8.97k
}
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
8.97k
{
374
8.97k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
375
376
8.97k
    return psapi_get_device_memory(psi->psapi_instance);
377
8.97k
}
378
379
static int
380
ps_impl_set_param(pl_interp_implementation_t *impl,
381
                  gs_param_list *plist)
382
35.8k
{
383
35.8k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
384
385
35.8k
    return psapi_set_param(psi->psapi_instance, plist);
386
35.8k
}
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
8.97k
{
400
8.97k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
401
8.97k
    const float *resolutions;
402
8.97k
    const long *page_sizes;
403
404
8.97k
    pl_main_get_forced_geometry(psi->memory, &resolutions, &page_sizes);
405
8.97k
    psapi_force_geometry(psi->psapi_instance, resolutions, page_sizes);
406
407
8.97k
    return psapi_init_with_args2(psi->psapi_instance);
408
8.97k
}
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
68
{
415
68
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
416
68
    int exit_code;
417
68
    int code;
418
419
    /* Any error after here *must* reset the device to null */
420
68
    code = psapi_set_device(psi->psapi_instance, device);
421
422
68
    if (code >= 0)
423
68
        code = psapi_run_string(psi->psapi_instance, "erasepage", 0, &exit_code);
424
425
68
    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
68
    {
432
68
        gs_c_param_list* params;
433
68
        int page_spot_colors = -1;
434
68
        int code2;
435
436
68
        params = gs_c_param_list_alloc(psi->memory, "ps_impl_init_job");
437
68
        if (params == NULL)
438
0
            return_error(gs_error_VMerror);
439
440
68
        gs_c_param_list_write(params, psi->memory);
441
68
        gs_param_list_set_persistent_keys((gs_param_list*)params, false);
442
443
68
        code2 = param_write_int((gs_param_list*)params, "PageSpotColors", &(page_spot_colors));
444
68
        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
68
        gs_c_param_list_read(params);
450
451
68
        code2 = psapi_set_device_param(psi->psapi_instance, (gs_param_list*)params);
452
68
        gs_c_param_list_free(psi->memory, params, "ps_impl_init_job");
453
68
        if (code2 < 0)
454
0
            return code2;
455
68
    }
456
68
    return code;
457
68
}
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
45
{
484
45
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
485
45
    int exit_code;
486
487
45
    return psapi_run_file(psi->psapi_instance, filename, 0, &exit_code);
488
45
}
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
23
{
494
23
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
495
23
    int exit_code;
496
497
23
    psi->bytes_fed = 0;
498
23
    return psapi_run_string_begin(psi->psapi_instance, 0, &exit_code);
499
23
}
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
23
{
507
23
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
508
23
    unsigned int len;
509
23
    int code, exit_code = 0;
510
511
23
    if (psi->bytes_fed == 0)
512
23
    {
513
        /* Skip over whitespace/comments looking for a PDF marker. */
514
161
        while (pr->ptr < pr->limit)
515
161
        {
516
161
            int i;
517
518
            /* Skip over whitespace (as defined in PLRM) */
519
161
            if (pr->ptr[1] == 0 ||
520
161
                pr->ptr[1] == 9 ||
521
161
                pr->ptr[1] == 10 ||
522
161
                pr->ptr[1] == 12 ||
523
161
                pr->ptr[1] == 13 ||
524
161
                pr->ptr[1] == 32) {
525
138
                pr->ptr++;
526
138
                continue;
527
138
            }
528
529
            /* If we're not starting a comment, exit. */
530
23
            if (pr->ptr[1] != '%')
531
23
                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
23
    }
567
568
23
    len = pr->limit - pr->ptr;
569
23
    code = psapi_run_string_continue(psi->psapi_instance, (const char *)pr->ptr + 1, len, 0, &exit_code);
570
23
    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
23
    else {
587
23
        pr->ptr = pr->limit;
588
23
        psi->bytes_fed += len;
589
23
    }
590
23
    return code;
591
23
}
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
23
{
596
23
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
597
23
    int exit_code, code;
598
599
23
    code = psapi_run_string_end(psi->psapi_instance, 0, &exit_code);
600
601
23
    if (exit_code == gs_error_InterpreterExit || code == gs_error_NeedInput)
602
0
        code = 0;
603
604
23
    return code;
605
23
}
606
607
/* Not implemented */
608
static int
609
ps_impl_flush_to_eoj(pl_interp_implementation_t *impl, stream_cursor_read *cursor)
610
30
{
611
30
    const byte *p = cursor->ptr;
612
30
    const byte *rlimit = cursor->limit;
613
614
    /* Skip to, but leave UEL in buffer for PJL to find later */
615
9.61k
    for (; p < rlimit; ++p)
616
9.58k
        if (p[1] == '\033') {
617
12
            uint avail = rlimit - p;
618
619
12
            if (memcmp(p + 1, "\033%-12345X", min(avail, 9)))
620
8
                continue;
621
4
            if (avail < 9)
622
1
                break;
623
3
            cursor->ptr = p;
624
3
            return 1;           /* found eoj */
625
4
        }
626
27
    cursor->ptr = p;
627
27
    return 0;                   /* need more */
628
30
}
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
23
{
645
23
    return 0;
646
23
}
647
648
/* Wrap up interp instance after a "job" */
649
static int
650
ps_impl_dnit_job(pl_interp_implementation_t *impl)
651
68
{
652
68
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
653
654
68
    return psapi_set_device(psi->psapi_instance, NULL);
655
68
}
656
657
/* Deallocate a interpreter instance */
658
static int
659
ps_impl_deallocate_interp_instance(pl_interp_implementation_t *impl)
660
8.97k
{
661
8.97k
    ps_interp_instance_t *psi = (ps_interp_instance_t *)impl->interp_client_data;
662
8.97k
    int                   code;
663
664
8.97k
    if (psi == NULL)
665
0
        return 0;
666
8.97k
    code = psapi_exit(psi->psapi_instance);
667
8.97k
    psapi_delete_instance(psi->psapi_instance);
668
8.97k
    gs_free_object(psi->memory, psi, "ps_impl_allocate_interp_instance");
669
    impl->interp_client_data = NULL;
670
8.97k
    return code;
671
8.97k
}
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
};