Coverage Report

Created: 2026-10-28 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/psi/psapi.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
18
/* Private internal API to Ghostscript interpreter */
19
20
#include "string_.h"
21
#include "ierrors.h"
22
#include "gscdefs.h"
23
#include "gstypes.h"
24
#include "gdebug.h"
25
#include "psapi.h"  /* Public API */
26
#include "iref.h"
27
#include "iminst.h"
28
#include "imain.h"
29
#include "imainarg.h"
30
#include "gsmemory.h"
31
#include "gsmalloc.h"
32
#include "gslibctx.h"
33
#include "gp.h"
34
#include "gsargs.h"
35
#include "ialloc.h"
36
#include "icstate.h"
37
#include "store.h"
38
#include "iname.h"
39
#include "interp.h"
40
#include "gxgstate.h"
41
42
/* This is the fallback for the number of threads to allow per process; i.e. just one.
43
 * This is only ever used if the gp_get_globals function returns 0 (i.e. only for
44
 * platforms that don't support threading).
45
 */
46
static int gsapi_instance_counter = 0;
47
static const int gsapi_instance_max = 1;
48
49
50
#ifdef METRO
51
static int GSDLLCALL metro_stdin(void *v, char *buf, int len)
52
{
53
        return 0;
54
}
55
56
static int GSDLLCALL metro_stdout(void *v, const char *str, int len)
57
{
58
#ifdef DEBUG
59
        OutputDebugStringWRT(str, len);
60
#endif
61
        return len;
62
}
63
64
static int GSDLLCALL metro_stderr(void *v, const char *str, int len)
65
{
66
        return metro_stdout(v, str, len);
67
}
68
#endif
69
70
/* Create a new instance of Ghostscript.
71
 * First instance per process call with *pinstance == NULL
72
 * next instance in a proces call with *pinstance == copy of valid_instance pointer
73
 * *pinstance is set to a new instance pointer.
74
 */
75
int
76
psapi_new_instance(gs_lib_ctx_t **pinstance,
77
                   void          *caller_handle)
78
162k
{
79
162k
    gs_memory_t *mem = NULL;
80
162k
    gs_main_instance *minst = NULL;
81
82
162k
    if (pinstance == NULL)
83
0
        return gs_error_Fatal;
84
85
162k
    if (gp_get_globals() == NULL) {
86
        /* This platform does not support the thread safe instance
87
         * handling. We'll drop back to the old mechanism we've used
88
         * to handle limiting ourselves to 1 instance in the past,
89
         * despite this being thread-unsafe itself. */
90
0
        if ( gsapi_instance_counter >= gsapi_instance_max )
91
0
            return gs_error_Fatal;
92
0
        ++gsapi_instance_counter;
93
0
    }
94
95
162k
    mem = gs_malloc_init_with_context(*pinstance);
96
162k
    if (mem == NULL)
97
0
        return gs_error_Fatal;
98
162k
    minst = gs_main_alloc_instance(mem);
99
162k
    if (minst == NULL) {
100
0
        gs_malloc_release(mem);
101
0
        return gs_error_Fatal;
102
0
    }
103
162k
    mem->gs_lib_ctx->top_of_system = (void*) minst;
104
162k
    mem->gs_lib_ctx->core->default_caller_handle = caller_handle;
105
162k
    mem->gs_lib_ctx->core->custom_color_callback = NULL;
106
#ifdef METRO
107
    if (mem->gs_lib_ctx->core->stdin_fn == NULL)
108
        mem->gs_lib_ctx->core->stdin_fn = metro_stdin;
109
    if (mem->gs_lib_ctx->core->stdout_fn == NULL)
110
        mem->gs_lib_ctx->core->stdout_fn = metro_stdout;
111
    if (mem->gs_lib_ctx->core->stderr_fn == NULL)
112
        mem->gs_lib_ctx->core->stderr_fn = metro_stderr;
113
#endif
114
162k
    mem->gs_lib_ctx->core->poll_fn = NULL;
115
116
162k
    *pinstance = mem->gs_lib_ctx;
117
162k
    return psapi_set_arg_encoding(*pinstance, PS_ARG_ENCODING_LOCAL);
118
162k
}
119
120
/* Set an instance of Ghostscript to respond to UEL (universal
121
 * exit language) strings in the input. */
122
void
123
psapi_act_on_uel(gs_lib_ctx_t *ctx)
124
16.0k
{
125
16.0k
    ctx->core->act_on_uel = 1;
126
16.0k
}
127
128
/* Destroy an instance of Ghostscript */
129
/* We do not support multiple instances, so make sure
130
 * we use the default instance only once.
131
 */
132
void
133
psapi_delete_instance(gs_lib_ctx_t *ctx)
134
162k
{
135
162k
    gs_memory_t *mem;
136
162k
    gs_main_instance *minst;
137
138
162k
    if (ctx == NULL)
139
0
        return;
140
141
162k
    mem = (gs_memory_t *)(ctx->memory);
142
162k
    minst = get_minst_from_memory(ctx->memory);
143
144
162k
    ctx->core->default_caller_handle = NULL;
145
162k
    ctx->core->stdin_fn = NULL;
146
162k
    ctx->core->stdout_fn = NULL;
147
162k
    ctx->core->stderr_fn = NULL;
148
162k
    ctx->core->poll_fn = NULL;
149
162k
    minst->display = NULL;
150
151
162k
    if (minst->param_list) {
152
0
        gs_c_param_list_release(minst->param_list);
153
0
        gs_free_object(minst->heap, minst->param_list, "psapi_delete_instance");
154
0
    }
155
156
162k
    gs_c_param_list_release(&minst->enum_params);
157
162k
    gs_free_object(minst->heap, minst->enum_keybuf, "psapi_delete_instance");
158
159
162k
    gp_alarm_destroy(minst->timeout);
160
161
162k
    gs_free_object(mem, minst, "init_main_instance");
162
163
    /* Release the memory (frees up everything) */
164
162k
    gs_malloc_release(mem);
165
166
162k
    if (gp_get_globals() == NULL)
167
0
        --gsapi_instance_counter;
168
162k
}
169
170
static int ascii_get_codepoint(stream *s, const char **astr)
171
0
{
172
0
    if (s)
173
0
        return spgetc(s);
174
0
    else {
175
0
        int rune = **astr;
176
0
        (*astr)++;
177
0
        if (rune != 0)
178
0
            return rune;
179
0
    }
180
0
    return EOF;
181
0
}
182
183
static int utf16le_get_codepoint(stream *s, const char **astr)
184
0
{
185
0
    int c;
186
0
    int rune;
187
0
    int trail;
188
189
    /* This code spots the BOM for 16bit LE and ignores it. Strictly speaking
190
     * this may be wrong, as we are only supposed to ignore it at the beginning
191
     * of the string, but if anyone is stupid enough to use ZWNBSP (zero width
192
     * non breaking space) in the middle of their strings, then they deserve
193
     * what they get. */
194
    /* We spot the BOM for 16bit BE and treat that as EOF. We'd rather give
195
     * up on dealing with a broken file than try to run something we know to
196
     * be wrong. */
197
198
0
    do {
199
0
        if (s) {
200
0
            rune = spgetc(s);
201
0
            if (rune == EOF)
202
0
                return EOF;
203
0
            c = spgetc(s);
204
0
            if (c == EOF)
205
0
                return EOF;
206
0
            rune += c<<8;
207
0
        } else {
208
0
            rune = (*astr)[0] | ((*astr)[1]<<8);
209
0
            if (rune != 0)
210
0
                (*astr) += 2;
211
0
            else
212
0
                return EOF;
213
0
        }
214
0
        if (rune == 0xFEFF) /* BOM - ignore it */
215
0
            continue;
216
0
        if (rune == 0xFFFE) /* BOM for BE - hopelessly broken */
217
0
            return EOF;
218
0
        if (rune < 0xD800 || rune >= 0xE000)
219
0
            return rune;
220
0
        if (rune >= 0xDC00) /* Found a trailing surrogate pair. Skip it */
221
0
            continue;
222
0
lead: /* We've just read a leading surrogate */
223
0
        rune -= 0xD800;
224
0
        rune <<= 10;
225
0
        if (s) {
226
0
            trail = spgetc(s);
227
0
            if (trail == EOF)
228
0
                return EOF;
229
0
            c = spgetc(s);
230
0
            if (c == EOF)
231
0
                return EOF;
232
0
            trail += c<<8;
233
0
        } else {
234
0
            trail = (*astr)[0] | ((*astr)[1]<<8);
235
0
            if (trail != 0)
236
0
                (*astr) += 2;
237
0
            else
238
0
                return EOF;
239
0
        }
240
0
        if (trail < 0xd800 || trail >= 0xE000) {
241
0
            if (rune == 0xFEFF) /* BOM - ignore it. */
242
0
                continue;
243
0
            if (rune == 0xFFFE) /* BOM for BE - hopelessly broken. */
244
0
                return EOF;
245
            /* No trail surrogate was found, so skip the lead surrogate and
246
             * return the rune we landed on. */
247
0
            return trail;
248
0
        }
249
0
        if (trail < 0xdc00) {
250
            /* We found another leading surrogate. */
251
0
            rune = trail;
252
0
            goto lead;
253
0
        }
254
0
        break;
255
0
    } while (1);
256
257
0
    return rune + (trail-0xDC00) + 0x10000;
258
0
}
259
260
/* Initialise the interpreter */
261
int
262
psapi_set_arg_encoding(gs_lib_ctx_t *ctx, int encoding)
263
324k
{
264
324k
    if (ctx == NULL)
265
0
        return gs_error_Fatal;
266
267
324k
    if (encoding == PS_ARG_ENCODING_LOCAL) {
268
#if defined(__WIN32__) && !defined(METRO)
269
        /* For windows, we need to set it up so that we convert from 'local'
270
         * format (in this case whatever codepage is set) to utf8 format. At
271
         * the moment, all the other OS we care about provide utf8 anyway.
272
         */
273
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), gp_local_arg_encoding_get_codepoint);
274
#else
275
162k
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), ascii_get_codepoint);
276
162k
#endif /* WIN32 */
277
162k
        return 0;
278
162k
    }
279
162k
    if (encoding == PS_ARG_ENCODING_UTF8) {
280
162k
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), NULL);
281
162k
        return 0;
282
162k
    }
283
0
    if (encoding == PS_ARG_ENCODING_UTF16LE) {
284
0
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), utf16le_get_codepoint);
285
0
        return 0;
286
0
    }
287
0
    return gs_error_Fatal;
288
0
}
289
290
int
291
psapi_init_with_args(gs_lib_ctx_t *ctx, int argc, char **argv)
292
146k
{
293
146k
    if (ctx == NULL)
294
0
        return gs_error_Fatal;
295
296
146k
    return gs_main_init_with_args(get_minst_from_memory(ctx->memory), argc, argv);
297
146k
}
298
299
int
300
psapi_init_with_args01(gs_lib_ctx_t *ctx, int argc, char **argv)
301
16.0k
{
302
16.0k
    if (ctx == NULL)
303
0
        return gs_error_Fatal;
304
305
16.0k
    return gs_main_init_with_args01(get_minst_from_memory(ctx->memory), argc, argv);
306
16.0k
}
307
308
int
309
psapi_init_with_args2(gs_lib_ctx_t *ctx)
310
16.0k
{
311
16.0k
    if (ctx == NULL)
312
0
        return gs_error_Fatal;
313
314
16.0k
    return gs_main_init_with_args2(get_minst_from_memory(ctx->memory));
315
16.0k
}
316
317
int
318
psapi_set_device_param(gs_lib_ctx_t *ctx,
319
                       gs_param_list *plist)
320
1.11k
{
321
1.11k
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
322
323
1.11k
    return gs_putdeviceparams(minst->i_ctx_p->pgs->device, plist);
324
1.11k
}
325
326
int
327
psapi_get_device_params(gs_lib_ctx_t *ctx,
328
                        gs_param_list *plist)
329
0
{
330
0
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
331
332
0
    if (minst->i_ctx_p->pgs->device == NULL)
333
0
        return 0;
334
0
    return gs_getdeviceparams(minst->i_ctx_p->pgs->device, plist);
335
0
}
336
337
int
338
psapi_set_param(gs_lib_ctx_t *ctx,
339
                gs_param_list *plist)
340
64.0k
{
341
64.0k
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
342
343
64.0k
    return gs_main_set_language_param(minst, plist);
344
64.0k
}
345
346
int
347
psapi_add_path(gs_lib_ctx_t *ctx,
348
               const char   *path)
349
0
{
350
0
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
351
352
0
    return gs_main_add_lib_path(minst, path);
353
0
}
354
355
/* The gsapi_run_* functions are like gs_main_run_* except
356
 * that the error_object is omitted.
357
 * An error object in minst is used instead.
358
 */
359
360
/* Setup up a suspendable run_string */
361
int
362
psapi_run_string_begin(gs_lib_ctx_t *ctx,
363
                       int           user_errors,
364
                       int          *pexit_code)
365
116
{
366
116
    gs_main_instance *minst;
367
116
    int code;
368
369
116
    if (ctx == NULL)
370
0
        return gs_error_Fatal;
371
116
    minst = get_minst_from_memory(ctx->memory);
372
373
116
    if (minst->mid_run_string == 1)
374
0
        return -1;
375
116
    minst->mid_run_string = 1;
376
377
116
    code = gs_main_run_string_begin(minst, user_errors, pexit_code,
378
116
                                    &(minst->error_object));
379
116
    if (code < 0)
380
0
        minst->mid_run_string = 0;
381
382
116
    return code;
383
116
}
384
385
int
386
psapi_run_string_continue(gs_lib_ctx_t *ctx,
387
                          const char   *str,
388
                          unsigned int  length,
389
                          int         user_errors,
390
                          int        *pexit_code)
391
115
{
392
115
    gs_main_instance *minst;
393
115
    int code;
394
395
115
    if (ctx == NULL)
396
0
        return gs_error_Fatal;
397
115
    minst = get_minst_from_memory(ctx->memory);
398
399
115
    code = gs_main_run_string_continue(minst, str, length, user_errors,
400
115
                                       pexit_code,
401
115
                                       &(minst->error_object));
402
115
    if (code < 0)
403
115
        minst->mid_run_string = 0;
404
405
115
    return code;
406
115
}
407
408
uint
409
psapi_get_uel_offset(gs_lib_ctx_t *ctx)
410
0
{
411
0
    if (ctx == NULL)
412
0
        return 0;
413
414
0
    return gs_main_get_uel_offset(get_minst_from_memory(ctx->memory));
415
0
}
416
417
int
418
psapi_run_string_end(gs_lib_ctx_t *ctx,
419
                     int           user_errors,
420
                     int          *pexit_code)
421
116
{
422
116
    int code;
423
116
    gs_main_instance *minst;
424
425
116
    if (ctx == NULL)
426
0
        return gs_error_Fatal;
427
116
    minst = get_minst_from_memory(ctx->memory);
428
429
116
    code = gs_main_run_string_end(minst, user_errors, pexit_code,
430
116
                                  &(minst->error_object));
431
432
116
    minst->mid_run_string = 0;
433
116
    return code;
434
116
}
435
436
int
437
psapi_run_string_with_length(gs_lib_ctx_t *ctx,
438
                             const char   *str,
439
                             unsigned int  length,
440
                             int           user_errors,
441
                             int          *pexit_code)
442
0
{
443
0
    if (ctx == NULL)
444
0
        return gs_error_Fatal;
445
446
0
    return gs_main_run_string_with_length(get_minst_from_memory(ctx->memory),
447
0
                                          str, length, user_errors, pexit_code,
448
0
                                          &(get_minst_from_memory(ctx->memory)->error_object));
449
0
}
450
451
int
452
psapi_run_string(gs_lib_ctx_t *ctx,
453
                 const char   *str,
454
                 int           user_errors,
455
                 int          *pexit_code)
456
1.11k
{
457
1.11k
    if (ctx == NULL)
458
0
        return gs_error_Fatal;
459
460
1.11k
    return gs_main_run_string(get_minst_from_memory(ctx->memory),
461
1.11k
                              str, user_errors, pexit_code,
462
1.11k
                              &(get_minst_from_memory(ctx->memory)->error_object));
463
1.11k
}
464
465
int
466
psapi_run_file(gs_lib_ctx_t *ctx,
467
               const char   *file_name,
468
               int           user_errors,
469
               int          *pexit_code)
470
1.00k
{
471
1.00k
    char *d, *temp;
472
1.00k
    const char *c = file_name;
473
1.00k
    char dummy[6];
474
1.00k
    int rune, code, len;
475
1.00k
    gs_main_instance *minst;
476
1.00k
    if (ctx == NULL)
477
0
        return gs_error_Fatal;
478
1.00k
    minst = get_minst_from_memory(ctx->memory);
479
480
1.00k
    if (minst->mid_run_string == 1)
481
0
        return -1;
482
483
    /* Convert the file_name to utf8 */
484
1.00k
    if (minst->get_codepoint) {
485
0
        len = 1;
486
0
        while ((rune = minst->get_codepoint(NULL, &c)) >= 0)
487
0
            len += codepoint_to_utf8(dummy, rune);
488
0
        temp = (char *)gs_alloc_bytes_immovable(ctx->memory, len, "gsapi_run_file");
489
0
        if (temp == NULL)
490
0
            return 0;
491
0
        c = file_name;
492
0
        d = temp;
493
0
        while ((rune = minst->get_codepoint(NULL, &c)) >= 0)
494
0
           d += codepoint_to_utf8(d, rune);
495
0
        *d = 0;
496
0
    }
497
1.00k
    else {
498
1.00k
      temp = (char *)file_name;
499
1.00k
    }
500
1.00k
    code = gs_main_run_file2(minst, temp, user_errors, pexit_code,
501
1.00k
                             &(minst->error_object));
502
1.00k
    if (temp != file_name)
503
0
        gs_free_object(ctx->memory, temp, "gsapi_run_file");
504
1.00k
    return code;
505
1.00k
}
506
507
/* Retrieve the memory allocator for the interpreter instance */
508
gs_memory_t *
509
psapi_get_device_memory(gs_lib_ctx_t *ctx)
510
16.0k
{
511
16.0k
    if (ctx == NULL)
512
0
        return NULL;
513
16.0k
    return gs_main_get_device_memory(get_minst_from_memory(ctx->memory));
514
16.0k
}
515
516
/* Retrieve the memory allocator for the interpreter instance */
517
int
518
psapi_set_device(gs_lib_ctx_t *ctx, gx_device *pdev)
519
2.23k
{
520
2.23k
    if (ctx == NULL)
521
0
        return gs_error_Fatal;
522
2.23k
    return gs_main_set_device(get_minst_from_memory(ctx->memory), pdev);
523
2.23k
}
524
525
/* Exit the interpreter */
526
int
527
psapi_exit(gs_lib_ctx_t *ctx)
528
162k
{
529
162k
    if (ctx == NULL)
530
0
        return gs_error_Fatal;
531
532
162k
    return gs_to_exit(ctx->memory, 0);
533
162k
}
534
535
int
536
psapi_force_geometry(gs_lib_ctx_t *ctx, const float *resolutions, const long *dimensions)
537
16.0k
{
538
16.0k
    int code;
539
540
16.0k
    if (ctx == NULL)
541
0
        return gs_error_Fatal;
542
16.0k
    code = gs_main_force_resolutions(get_minst_from_memory(ctx->memory), resolutions);
543
16.0k
    if (code < 0)
544
0
        return code;
545
16.0k
    return gs_main_force_dimensions(get_minst_from_memory(ctx->memory), dimensions);
546
16.0k
}