Coverage Report

Created: 2026-04-01 07:17

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