Coverage Report

Created: 2025-06-10 07:26

/src/ghostpdl/psi/psapi.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2025 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
9.22k
{
79
9.22k
    gs_memory_t *mem = NULL;
80
9.22k
    gs_main_instance *minst = NULL;
81
82
9.22k
    if (pinstance == NULL)
83
0
        return gs_error_Fatal;
84
85
9.22k
    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
9.22k
    mem = gs_malloc_init_with_context(*pinstance);
96
9.22k
    if (mem == NULL)
97
0
        return gs_error_Fatal;
98
9.22k
    minst = gs_main_alloc_instance(mem);
99
9.22k
    if (minst == NULL) {
100
0
        gs_malloc_release(mem);
101
0
        return gs_error_Fatal;
102
0
    }
103
9.22k
    mem->gs_lib_ctx->top_of_system = (void*) minst;
104
9.22k
    mem->gs_lib_ctx->core->default_caller_handle = caller_handle;
105
9.22k
    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
9.22k
    mem->gs_lib_ctx->core->poll_fn = NULL;
115
116
9.22k
    *pinstance = mem->gs_lib_ctx;
117
9.22k
    return psapi_set_arg_encoding(*pinstance, PS_ARG_ENCODING_LOCAL);
118
9.22k
}
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
0
{
125
0
    ctx->core->act_on_uel = 1;
126
0
}
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
9.22k
{
135
9.22k
    gs_memory_t *mem;
136
9.22k
    gs_main_instance *minst;
137
138
9.22k
    if (ctx == NULL)
139
0
        return;
140
141
9.22k
    mem = (gs_memory_t *)(ctx->memory);
142
9.22k
    minst = get_minst_from_memory(ctx->memory);
143
144
9.22k
    ctx->core->default_caller_handle = NULL;
145
9.22k
    ctx->core->stdin_fn = NULL;
146
9.22k
    ctx->core->stdout_fn = NULL;
147
9.22k
    ctx->core->stderr_fn = NULL;
148
9.22k
    ctx->core->poll_fn = NULL;
149
9.22k
    minst->display = NULL;
150
151
9.22k
    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
9.22k
    gs_c_param_list_release(&minst->enum_params);
157
9.22k
    gs_free_object(minst->heap, minst->enum_keybuf, "psapi_delete_instance");
158
159
9.22k
    gs_free_object(mem, minst, "init_main_instance");
160
161
    /* Release the memory (frees up everything) */
162
9.22k
    gs_malloc_release(mem);
163
164
9.22k
    if (gp_get_globals() == NULL)
165
0
        --gsapi_instance_counter;
166
9.22k
}
167
168
static int ascii_get_codepoint(stream *s, const char **astr)
169
0
{
170
0
    if (s)
171
0
        return spgetc(s);
172
0
    else {
173
0
        int rune = **astr;
174
0
        (*astr)++;
175
0
        if (rune != 0)
176
0
            return rune;
177
0
    }
178
0
    return EOF;
179
0
}
180
181
static int utf16le_get_codepoint(stream *s, const char **astr)
182
0
{
183
0
    int c;
184
0
    int rune;
185
0
    int trail;
186
187
    /* This code spots the BOM for 16bit LE and ignores it. Strictly speaking
188
     * this may be wrong, as we are only supposed to ignore it at the beginning
189
     * of the string, but if anyone is stupid enough to use ZWNBSP (zero width
190
     * non breaking space) in the middle of their strings, then they deserve
191
     * what they get. */
192
    /* We spot the BOM for 16bit BE and treat that as EOF. We'd rather give
193
     * up on dealing with a broken file than try to run something we know to
194
     * be wrong. */
195
196
0
    do {
197
0
        if (s) {
198
0
            rune = spgetc(s);
199
0
            if (rune == EOF)
200
0
                return EOF;
201
0
            c = spgetc(s);
202
0
            if (c == EOF)
203
0
                return EOF;
204
0
            rune += c<<8;
205
0
        } else {
206
0
            rune = (*astr)[0] | ((*astr)[1]<<8);
207
0
            if (rune != 0)
208
0
                (*astr) += 2;
209
0
            else
210
0
                return EOF;
211
0
        }
212
0
        if (rune == 0xFEFF) /* BOM - ignore it */
213
0
            continue;
214
0
        if (rune == 0xFFFE) /* BOM for BE - hopelessly broken */
215
0
            return EOF;
216
0
        if (rune < 0xD800 || rune >= 0xE000)
217
0
            return rune;
218
0
        if (rune >= 0xDC00) /* Found a trailing surrogate pair. Skip it */
219
0
            continue;
220
0
lead: /* We've just read a leading surrogate */
221
0
        rune -= 0xD800;
222
0
        rune <<= 10;
223
0
        if (s) {
224
0
            trail = spgetc(s);
225
0
            if (trail == EOF)
226
0
                return EOF;
227
0
            c = spgetc(s);
228
0
            if (c == EOF)
229
0
                return EOF;
230
0
            trail += c<<8;
231
0
        } else {
232
0
            trail = (*astr)[0] | ((*astr)[1]<<8);
233
0
            if (trail != 0)
234
0
                (*astr) += 2;
235
0
            else
236
0
                return EOF;
237
0
        }
238
0
        if (trail < 0xd800 || trail >= 0xE000) {
239
0
            if (rune == 0xFEFF) /* BOM - ignore it. */
240
0
                continue;
241
0
            if (rune == 0xFFFE) /* BOM for BE - hopelessly broken. */
242
0
                return EOF;
243
            /* No trail surrogate was found, so skip the lead surrogate and
244
             * return the rune we landed on. */
245
0
            return trail;
246
0
        }
247
0
        if (trail < 0xdc00) {
248
            /* We found another leading surrogate. */
249
0
            rune = trail;
250
0
            goto lead;
251
0
        }
252
0
        break;
253
0
    } while (1);
254
255
0
    return rune + (trail-0xDC00) + 0x10000;
256
0
}
257
258
/* Initialise the interpreter */
259
int
260
psapi_set_arg_encoding(gs_lib_ctx_t *ctx, int encoding)
261
18.4k
{
262
18.4k
    if (ctx == NULL)
263
0
        return gs_error_Fatal;
264
265
18.4k
    if (encoding == PS_ARG_ENCODING_LOCAL) {
266
#if defined(__WIN32__) && !defined(METRO)
267
        /* For windows, we need to set it up so that we convert from 'local'
268
         * format (in this case whatever codepage is set) to utf8 format. At
269
         * the moment, all the other OS we care about provide utf8 anyway.
270
         */
271
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), gp_local_arg_encoding_get_codepoint);
272
#else
273
9.22k
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), ascii_get_codepoint);
274
9.22k
#endif /* WIN32 */
275
9.22k
        return 0;
276
9.22k
    }
277
9.22k
    if (encoding == PS_ARG_ENCODING_UTF8) {
278
9.22k
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), NULL);
279
9.22k
        return 0;
280
9.22k
    }
281
0
    if (encoding == PS_ARG_ENCODING_UTF16LE) {
282
0
        gs_main_inst_arg_decode(get_minst_from_memory(ctx->memory), utf16le_get_codepoint);
283
0
        return 0;
284
0
    }
285
0
    return gs_error_Fatal;
286
0
}
287
288
int
289
psapi_init_with_args(gs_lib_ctx_t *ctx, int argc, char **argv)
290
9.22k
{
291
9.22k
    if (ctx == NULL)
292
0
        return gs_error_Fatal;
293
294
9.22k
    return gs_main_init_with_args(get_minst_from_memory(ctx->memory), argc, argv);
295
9.22k
}
296
297
int
298
psapi_init_with_args01(gs_lib_ctx_t *ctx, int argc, char **argv)
299
0
{
300
0
    if (ctx == NULL)
301
0
        return gs_error_Fatal;
302
303
0
    return gs_main_init_with_args01(get_minst_from_memory(ctx->memory), argc, argv);
304
0
}
305
306
int
307
psapi_init_with_args2(gs_lib_ctx_t *ctx)
308
0
{
309
0
    if (ctx == NULL)
310
0
        return gs_error_Fatal;
311
312
0
    return gs_main_init_with_args2(get_minst_from_memory(ctx->memory));
313
0
}
314
315
int
316
psapi_set_device_param(gs_lib_ctx_t *ctx,
317
                       gs_param_list *plist)
318
0
{
319
0
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
320
321
0
    return gs_putdeviceparams(minst->i_ctx_p->pgs->device, plist);
322
0
}
323
324
int
325
psapi_get_device_params(gs_lib_ctx_t *ctx,
326
                        gs_param_list *plist)
327
0
{
328
0
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
329
330
0
    if (minst->i_ctx_p->pgs->device == NULL)
331
0
        return 0;
332
0
    return gs_getdeviceparams(minst->i_ctx_p->pgs->device, plist);
333
0
}
334
335
int
336
psapi_set_param(gs_lib_ctx_t *ctx,
337
                gs_param_list *plist)
338
0
{
339
0
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
340
341
0
    return gs_main_set_language_param(minst, plist);
342
0
}
343
344
int
345
psapi_add_path(gs_lib_ctx_t *ctx,
346
               const char   *path)
347
0
{
348
0
    gs_main_instance *minst = get_minst_from_memory(ctx->memory);
349
350
0
    return gs_main_add_lib_path(minst, path);
351
0
}
352
353
/* The gsapi_run_* functions are like gs_main_run_* except
354
 * that the error_object is omitted.
355
 * An error object in minst is used instead.
356
 */
357
358
/* Setup up a suspendable run_string */
359
int
360
psapi_run_string_begin(gs_lib_ctx_t *ctx,
361
                       int           user_errors,
362
                       int          *pexit_code)
363
0
{
364
0
    gs_main_instance *minst;
365
0
    int code;
366
367
0
    if (ctx == NULL)
368
0
        return gs_error_Fatal;
369
0
    minst = get_minst_from_memory(ctx->memory);
370
371
0
    if (minst->mid_run_string == 1)
372
0
        return -1;
373
0
    minst->mid_run_string = 1;
374
375
0
    code = gs_main_run_string_begin(minst, user_errors, pexit_code,
376
0
                                    &(minst->error_object));
377
0
    if (code < 0)
378
0
        minst->mid_run_string = 0;
379
380
0
    return code;
381
0
}
382
383
int
384
psapi_run_string_continue(gs_lib_ctx_t *ctx,
385
                          const char   *str,
386
                          unsigned int  length,
387
                          int         user_errors,
388
                          int        *pexit_code)
389
0
{
390
0
    gs_main_instance *minst;
391
0
    int code;
392
393
0
    if (ctx == NULL)
394
0
        return gs_error_Fatal;
395
0
    minst = get_minst_from_memory(ctx->memory);
396
397
0
    code = gs_main_run_string_continue(minst, str, length, user_errors,
398
0
                                       pexit_code,
399
0
                                       &(minst->error_object));
400
0
    if (code < 0)
401
0
        minst->mid_run_string = 0;
402
403
0
    return code;
404
0
}
405
406
uint
407
psapi_get_uel_offset(gs_lib_ctx_t *ctx)
408
0
{
409
0
    if (ctx == NULL)
410
0
        return 0;
411
412
0
    return gs_main_get_uel_offset(get_minst_from_memory(ctx->memory));
413
0
}
414
415
int
416
psapi_run_string_end(gs_lib_ctx_t *ctx,
417
                     int           user_errors,
418
                     int          *pexit_code)
419
0
{
420
0
    int code;
421
0
    gs_main_instance *minst;
422
423
0
    if (ctx == NULL)
424
0
        return gs_error_Fatal;
425
0
    minst = get_minst_from_memory(ctx->memory);
426
427
0
    code = gs_main_run_string_end(minst, user_errors, pexit_code,
428
0
                                  &(minst->error_object));
429
430
0
    minst->mid_run_string = 0;
431
0
    return code;
432
0
}
433
434
int
435
psapi_run_string_with_length(gs_lib_ctx_t *ctx,
436
                             const char   *str,
437
                             unsigned int  length,
438
                             int           user_errors,
439
                             int          *pexit_code)
440
0
{
441
0
    if (ctx == NULL)
442
0
        return gs_error_Fatal;
443
444
0
    return gs_main_run_string_with_length(get_minst_from_memory(ctx->memory),
445
0
                                          str, length, user_errors, pexit_code,
446
0
                                          &(get_minst_from_memory(ctx->memory)->error_object));
447
0
}
448
449
int
450
psapi_run_string(gs_lib_ctx_t *ctx,
451
                 const char   *str,
452
                 int           user_errors,
453
                 int          *pexit_code)
454
0
{
455
0
    if (ctx == NULL)
456
0
        return gs_error_Fatal;
457
458
0
    return gs_main_run_string(get_minst_from_memory(ctx->memory),
459
0
                              str, user_errors, pexit_code,
460
0
                              &(get_minst_from_memory(ctx->memory)->error_object));
461
0
}
462
463
int
464
psapi_run_file(gs_lib_ctx_t *ctx,
465
               const char   *file_name,
466
               int           user_errors,
467
               int          *pexit_code)
468
0
{
469
0
    char *d, *temp;
470
0
    const char *c = file_name;
471
0
    char dummy[6];
472
0
    int rune, code, len;
473
0
    gs_main_instance *minst;
474
0
    if (ctx == NULL)
475
0
        return gs_error_Fatal;
476
0
    minst = get_minst_from_memory(ctx->memory);
477
478
0
    if (minst->mid_run_string == 1)
479
0
        return -1;
480
481
    /* Convert the file_name to utf8 */
482
0
    if (minst->get_codepoint) {
483
0
        len = 1;
484
0
        while ((rune = minst->get_codepoint(NULL, &c)) >= 0)
485
0
            len += codepoint_to_utf8(dummy, rune);
486
0
        temp = (char *)gs_alloc_bytes_immovable(ctx->memory, len, "gsapi_run_file");
487
0
        if (temp == NULL)
488
0
            return 0;
489
0
        c = file_name;
490
0
        d = temp;
491
0
        while ((rune = minst->get_codepoint(NULL, &c)) >= 0)
492
0
           d += codepoint_to_utf8(d, rune);
493
0
        *d = 0;
494
0
    }
495
0
    else {
496
0
      temp = (char *)file_name;
497
0
    }
498
0
    code = gs_main_run_file2(minst, temp, user_errors, pexit_code,
499
0
                             &(minst->error_object));
500
0
    if (temp != file_name)
501
0
        gs_free_object(ctx->memory, temp, "gsapi_run_file");
502
0
    return code;
503
0
}
504
505
/* Retrieve the memory allocator for the interpreter instance */
506
gs_memory_t *
507
psapi_get_device_memory(gs_lib_ctx_t *ctx)
508
0
{
509
0
    if (ctx == NULL)
510
0
        return NULL;
511
0
    return gs_main_get_device_memory(get_minst_from_memory(ctx->memory));
512
0
}
513
514
/* Retrieve the memory allocator for the interpreter instance */
515
int
516
psapi_set_device(gs_lib_ctx_t *ctx, gx_device *pdev)
517
0
{
518
0
    if (ctx == NULL)
519
0
        return gs_error_Fatal;
520
0
    return gs_main_set_device(get_minst_from_memory(ctx->memory), pdev);
521
0
}
522
523
/* Exit the interpreter */
524
int
525
psapi_exit(gs_lib_ctx_t *ctx)
526
9.22k
{
527
9.22k
    if (ctx == NULL)
528
0
        return gs_error_Fatal;
529
530
9.22k
    return gs_to_exit(ctx->memory, 0);
531
9.22k
}
532
533
int
534
psapi_force_geometry(gs_lib_ctx_t *ctx, const float *resolutions, const long *dimensions)
535
0
{
536
0
    int code;
537
538
0
    if (ctx == NULL)
539
0
        return gs_error_Fatal;
540
0
    code = gs_main_force_resolutions(get_minst_from_memory(ctx->memory), resolutions);
541
0
    if (code < 0)
542
0
        return code;
543
0
    return gs_main_force_dimensions(get_minst_from_memory(ctx->memory), dimensions);
544
0
}