Coverage Report

Created: 2026-09-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/psi/zmisc.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
/* Miscellaneous operators */
18
19
#include "errno_.h"
20
#include "memory_.h"
21
#include "string_.h"
22
#include "ctype_.h"
23
#include "ghost.h"
24
#include "gp.h"
25
#include "oper.h"
26
#include "ialloc.h"
27
#include "idict.h"
28
#include "dstack.h"   /* for name lookup in bind */
29
#include "iname.h"
30
#include "ipacked.h"
31
#include "ivmspace.h"
32
#include "store.h"
33
#include "igstate.h"            /* for gs_currentcpsimode */
34
#include "memento.h"
35
#include "iscan.h"
36
37
/**********************************************************************/
38
39
/* <proc> bind <proc> */
40
static inline bool
41
r_is_ex_oper(const ref *rp)
42
2.94G
{
43
2.94G
    return (r_has_attr(rp, a_executable) &&
44
2.66G
            (r_btype(rp) == t_operator || r_type(rp) == t_oparray));
45
2.94G
}
46
static int
47
zbind(i_ctx_t *i_ctx_p)
48
224M
{
49
224M
    os_ptr op = osp;
50
224M
    uint depth = 1;
51
224M
    ref defn;
52
224M
    register os_ptr bsp;
53
54
224M
    check_op(1);
55
224M
    switch (r_type(op)) {
56
1.19M
        case t_array:
57
1.19M
            if (!r_has_attr(op, a_write)) {
58
3.33k
                return 0; /* per PLRM3 */
59
3.33k
            }
60
106M
        case t_mixedarray:
61
129M
        case t_shortarray:
62
129M
            defn = *op;
63
129M
            break;
64
20.4M
        case t_oparray:
65
20.4M
            defn = *op->value.const_refs;
66
20.4M
            break;
67
74.0M
        default:
68
74.0M
            return_op_typecheck(op);
69
224M
    }
70
224M
    push(1);
71
150M
    *op = defn;
72
150M
    bsp = op;
73
    /*
74
     * We must not make the top-level procedure read-only,
75
     * but we must bind it even if it is read-only already.
76
     *
77
     * Here are the invariants for the following loop:
78
     *      `depth' elements have been pushed on the ostack;
79
     *      For i < depth, p = ref_stack_index(&o_stack, i):
80
     *        *p is an array (or packedarray) ref.
81
     */
82
2.08G
    while (depth) {
83
17.6G
        while (r_size(bsp)) {
84
15.7G
            ref_packed *const tpp = (ref_packed *)bsp->value.packed; /* break const */
85
86
15.7G
            r_dec_size(bsp, 1);
87
15.7G
            if (r_is_packed(tpp)) {
88
                /* Check for a packed executable name */
89
10.0G
                ushort elt = *tpp;
90
91
10.0G
                if (r_packed_is_exec_name(&elt)) {
92
2.28G
                    ref nref;
93
2.28G
                    ref *pvalue;
94
95
2.28G
                    name_index_ref(imemory, packed_name_index(&elt),
96
2.28G
                                   &nref);
97
2.28G
                    if ((pvalue = dict_find_name(&nref)) != 0 &&
98
2.23G
                        r_is_ex_oper(pvalue)
99
2.28G
                        ) {
100
1.88G
                        store_check_dest(bsp, pvalue);
101
                        /*
102
                         * Always save the change, since this can only
103
                         * happen once.
104
                         */
105
1.88G
                        ref_do_save(bsp, tpp, "bind");
106
1.88G
                        *tpp = pt_tag(pt_executable_operator) +
107
1.88G
                            op_index(pvalue);
108
1.88G
                    }
109
2.28G
                }
110
10.0G
                bsp->value.packed = tpp + 1;
111
10.0G
            } else {
112
5.73G
                ref *const tp = bsp->value.refs++;
113
114
5.73G
                switch (r_type(tp)) {
115
1.51G
                    case t_name:  /* bind the name if an operator */
116
1.51G
                        if (r_has_attr(tp, a_executable)) {
117
1.03G
                            ref *pvalue;
118
119
1.03G
                            if ((pvalue = dict_find_name(tp)) != 0 &&
120
708M
                                r_is_ex_oper(pvalue)
121
1.03G
                                ) {
122
525M
                                store_check_dest(bsp, pvalue);
123
525M
                                ref_assign_old(bsp, tp, pvalue, "bind");
124
525M
                            }
125
1.03G
                        }
126
1.51G
                        break;
127
1.51G
                    case t_array: /* push into array if writable */
128
19.4M
                        if (!r_has_attr(tp, a_write))
129
3.68M
                            break;
130
1.28G
                    case t_mixedarray:
131
1.79G
                    case t_shortarray:
132
1.79G
                        if (r_has_attr(tp, a_executable)) {
133
                            /* Make reference read-only */
134
1.78G
                            r_clear_attrs(tp, a_write);
135
1.78G
                            if (bsp >= ostop) {
136
                                /* Push a new stack block. */
137
0
                                ref temp;
138
0
                                int code;
139
140
0
                                temp = *tp;
141
0
                                osp = bsp;
142
0
                                code = ref_stack_push(&o_stack, 1);
143
0
                                if (code < 0) {
144
0
                                    ref_stack_pop(&o_stack, depth);
145
0
                                    return_error(code);
146
0
                                }
147
0
                                bsp = osp;
148
0
                                *bsp = temp;
149
0
                            } else
150
1.78G
                                *++bsp = *tp;
151
1.78G
                            depth++;
152
1.78G
                        }
153
5.73G
                }
154
5.73G
            }
155
15.7G
        }
156
1.93G
        bsp--;
157
1.93G
        depth--;
158
1.93G
        if (bsp < osbot) { /* Pop back to the previous stack block. */
159
0
            osp = bsp;
160
0
            ref_stack_pop_block(&o_stack);
161
0
            bsp = osp;
162
0
        }
163
1.93G
    }
164
150M
    osp = bsp;
165
150M
    return 0;
166
150M
}
167
168
/* - serialnumber <int> */
169
static int
170
zserialnumber(i_ctx_t *i_ctx_p)
171
22
{
172
22
    os_ptr op = osp;
173
174
22
    push(1);
175
22
    make_int(op, gp_serialnumber());
176
22
    return 0;
177
22
}
178
179
/* - realtime <int> */
180
static int
181
zrealtime(i_ctx_t *i_ctx_p)
182
11
{
183
11
    os_ptr op = osp;
184
11
    long secs_ns[2];
185
11
    gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(imemory);
186
187
11
    gp_get_realtime(secs_ns);
188
11
    secs_ns[1] -= libctx->real_time_0[1];
189
11
    secs_ns[0] -= libctx->real_time_0[0];
190
11
    push(1);
191
11
    make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
192
11
    return 0;
193
11
}
194
195
/* - usertime <int> */
196
static int
197
zusertime(i_ctx_t *i_ctx_p)
198
1.21k
{
199
1.21k
    gs_context_state_t *current = (gs_context_state_t *)i_ctx_p;
200
1.21k
    os_ptr op = osp;
201
1.21k
    long secs_ns[2];
202
203
1.21k
    gp_get_usertime(secs_ns);
204
1.21k
    if (!current->usertime_inited) {
205
428
        current->usertime_inited = true;
206
428
        current->usertime_0[0] = secs_ns[0];
207
428
        current->usertime_0[1] = secs_ns[1];
208
428
    }
209
1.21k
    secs_ns[0] -= current->usertime_0[0];
210
1.21k
    secs_ns[1] -= current->usertime_0[1];
211
1.21k
    push(1);
212
1.21k
    make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
213
1.21k
    return 0;
214
1.21k
}
215
216
/* ---------------- Non-standard operators ---------------- */
217
218
/* <string> getenv <value_string> true */
219
/* <string> getenv false */
220
static int
221
zgetenv(i_ctx_t *i_ctx_p)
222
589k
{
223
589k
    os_ptr op = osp;
224
589k
    char *str;
225
589k
    byte *value;
226
589k
    int len = 0;
227
228
589k
    check_op(1);
229
589k
    check_read_type(*op, t_string);
230
589k
    str = ref_to_string(op, imemory, "getenv key");
231
589k
    if (str == 0)
232
0
        return_error(gs_error_VMerror);
233
234
589k
    if (gs_is_path_control_active(imemory)) {
235
0
        ifree_string((byte *) str, r_size(op) + 1, "getenv key");
236
0
        make_false(op);
237
0
        return 0;
238
0
    }
239
240
589k
    if (gp_getenv(str, (char *)0, &len) > 0) { /* key missing */
241
589k
        ifree_string((byte *) str, r_size(op) + 1, "getenv key");
242
589k
        make_false(op);
243
589k
        return 0;
244
589k
    }
245
0
    value = ialloc_string(len, "getenv value");
246
0
    if (value == 0) {
247
0
        ifree_string((byte *) str, r_size(op) + 1, "getenv key");
248
0
        return_error(gs_error_VMerror);
249
0
    }
250
0
    DISCARD(gp_getenv(str, (char *)value, &len));  /* can't fail */
251
0
    ifree_string((byte *) str, r_size(op) + 1, "getenv key");
252
    /* Delete the stupid C string terminator. */
253
0
    value = iresize_string(value, len, len - 1,
254
0
                           "getenv value"); /* can't fail */
255
0
    push(1);
256
0
    make_string(op - 1, a_all | icurrent_space, len - 1, value);
257
0
    make_true(op);
258
0
    return 0;
259
0
}
260
261
/* - .defaultpapersize <string> true */
262
/* - .defaultpapersize false */
263
static int
264
zdefaultpapersize(i_ctx_t *i_ctx_p)
265
147k
{
266
147k
    os_ptr op = osp;
267
147k
    byte *value;
268
147k
    int len = 0, i;
269
270
147k
    if (gp_defaultpapersize((char *)0, &len) > 0) {
271
        /* no default paper size */
272
147k
        push(1);
273
147k
        make_false(op);
274
147k
        return 0;
275
147k
    }
276
277
0
    value = ialloc_string(len, "defaultpapersize value");
278
0
    if (value == 0) {
279
0
        return_error(gs_error_VMerror);
280
0
    }
281
0
    DISCARD(gp_defaultpapersize((char *)value, &len)); /* can't fail */
282
    /* Note 'len' includes the NULL terminator, which we can ignore */
283
0
    for (i = 0;i < (len - 1); i++)
284
0
        value[i] = tolower(value[i]);
285
286
    /* Delete the stupid C string terminator. */
287
0
    value = iresize_string(value, len, len - 1,
288
0
                           "defaultpapersize value"); /* can't fail */
289
0
    push(2);
290
0
    make_string(op - 1, a_all | icurrent_space, len - 1, value);
291
0
    make_true(op);
292
0
    return 0;
293
0
}
294
295
/* <name> <proc> .makeoperator <oper> */
296
static int
297
zmakeoperator(i_ctx_t *i_ctx_p)
298
26.6M
{
299
26.6M
    os_ptr op = osp;
300
26.6M
    op_array_table *opt;
301
26.6M
    uint count;
302
26.6M
    ref *tab;
303
304
26.6M
    check_op(2);
305
26.6M
    check_type(op[-1], t_name);
306
26.6M
    check_proc(*op);
307
26.6M
    switch (r_space(op)) {
308
26.6M
        case avm_global:
309
26.6M
            opt = &i_ctx_p->op_array_table_global;
310
26.6M
            break;
311
0
        case avm_local:
312
0
            opt = &i_ctx_p->op_array_table_local;
313
0
            break;
314
0
        default:
315
0
            return_error(gs_error_invalidaccess);
316
26.6M
    }
317
26.6M
    count = opt->count;
318
26.6M
    tab = opt->table.value.refs;
319
    /*
320
     * restore doesn't reset op_array_table.count, but it does
321
     * remove entries from op_array_table.table.  Since we fill
322
     * the table in order, we can detect that a restore has occurred
323
     * by checking whether what should be the most recent entry
324
     * is occupied.  If not, we scan backwards over the vacated entries
325
     * to find the true end of the table.
326
     */
327
26.6M
    while (count > 0 && r_has_type(&tab[count - 1], t_null))
328
0
        --count;
329
26.6M
    if (count == r_size(&opt->table))
330
0
        return_error(gs_error_limitcheck);
331
26.6M
    ref_assign_old(&opt->table, &tab[count], op, "makeoperator");
332
26.6M
    opt->nx_table[count] = name_index(imemory, op - 1);
333
26.6M
    op_index_ref(imemory, opt->base_index + count, op - 1);
334
26.6M
    opt->count = count + 1;
335
26.6M
    pop(1);
336
26.6M
    return 0;
337
26.6M
}
338
339
/* - .oserrno <int> */
340
static int
341
zoserrno(i_ctx_t *i_ctx_p)
342
35.0k
{
343
35.0k
    os_ptr op = osp;
344
345
35.0k
    push(1);
346
35.0k
    make_int(op, errno);
347
35.0k
    return 0;
348
35.0k
}
349
350
/* <int> .setoserrno - */
351
static int
352
zsetoserrno(i_ctx_t *i_ctx_p)
353
329k
{
354
329k
    os_ptr op = osp;
355
356
329k
    check_op(1);
357
329k
    check_type(*op, t_integer);
358
329k
    errno = op->value.intval;
359
329k
    pop(1);
360
329k
    return 0;
361
329k
}
362
363
/* <int> .oserrorstring <string> true */
364
/* <int> .oserrorstring false */
365
static int
366
zoserrorstring(i_ctx_t *i_ctx_p)
367
29.7k
{
368
29.7k
    os_ptr op = osp;
369
29.7k
    const char *str;
370
29.7k
    int code;
371
29.7k
    uint len;
372
29.7k
    byte ch;
373
374
29.7k
    check_op(1);
375
29.7k
    check_type(*op, t_integer);
376
29.7k
    str = gp_strerror((int)op->value.intval);
377
29.7k
    if (str == 0 || (len = strlen(str)) == 0) {
378
0
        make_false(op);
379
0
        return 0;
380
0
    }
381
29.7k
    check_ostack(1);
382
29.7k
    code = string_to_ref(str, op, iimemory, ".oserrorstring");
383
29.7k
    if (code < 0)
384
0
        return code;
385
    /* Strip trailing end-of-line characters. */
386
29.7k
    while ((len = r_size(op)) != 0 &&
387
29.7k
           ((ch = op->value.bytes[--len]) == '\r' || ch == '\n')
388
29.7k
        )
389
0
        r_dec_size(op, 1);
390
29.7k
    push(1);
391
29.7k
    make_true(op);
392
29.7k
    return 0;
393
29.7k
}
394
395
/* <string> <bool> .setdebug - */
396
static int
397
zsetdebug(i_ctx_t *i_ctx_p)
398
0
{
399
0
    os_ptr op = osp;
400
0
    check_op(2);
401
0
    check_read_type(op[-1], t_string);
402
0
    check_type(*op, t_boolean);
403
0
    {
404
0
        int i;
405
406
0
        for (i = 0; i < r_size(op - 1); i++)
407
0
            gs_debug[op[-1].value.bytes[i] & 127] =
408
0
                op->value.boolval;
409
0
    }
410
0
    pop(2);
411
0
    return 0;
412
0
}
413
414
/* <int> internaldict <dict> */
415
static int
416
zinternaldict(i_ctx_t *i_ctx_p)
417
1.19M
{
418
1.19M
    os_ptr op = osp;
419
1.19M
    int code = 0;
420
421
1.19M
    check_op(1);
422
1.19M
    check_type(*op, t_integer);
423
1.19M
    if (op->value.intval == 1183615869) {
424
1.19M
        *op = i_ctx_p->internaldict;
425
1.19M
    } else {
426
0
        code = gs_note_error(gs_error_invalidaccess);
427
0
    }
428
1.19M
    return code;
429
1.19M
}
430
431
/* .mementolistnew - */
432
static int
433
zmementolistnewblocks(i_ctx_t *i_ctx_p)
434
0
{
435
0
    Memento_listNewBlocks();
436
0
    return 0;
437
0
}
438
439
/* This is used purely to avoid the previous use of .forecput when using DELAYBIND.
440
 * We need to create two arrays in systemdict; .delaybind and .delayinternalbind, and
441
 * they need to be in local VM (which is not strictly legal because systemdict is
442
 * defined in global VM). Previously this was done using .forceput but we want to
443
 * get rid of that.
444
 * This expedient is slightly less unpleasant because it only permits the creation of
445
 * specifically named arrays in systemdict, and they are only used until .bindnow is
446
 * executed. Recreating them after startup might slow thinigs down but we don't expect
447
 * there to be any unfortunate side effects. Of course it shouldn't be possible to
448
 * run this code after startup, but we thought that about forceput as well.
449
 */
450
static int zdelaybindparam(i_ctx_t *i_ctx_p)
451
0
{
452
0
    os_ptr op = osp;
453
0
    ref key_name;
454
0
    int64_t size;
455
0
    int code = 0, i = 0;
456
0
    const char *names_array[2] = {".delaybind", ".delayinternalbind"};
457
0
    uint space = r_space(&idict_stack.system_dict);
458
459
0
    check_op(1);
460
0
    check_type(*op, t_integer);
461
462
0
    size = op->value.intval;
463
0
    if (size < 0)
464
0
        return_error(gs_error_rangecheck);
465
466
0
    for (i=0;i < 2;i++) {
467
0
        code = gs_alloc_ref_array((gs_ref_memory_t *)imemory_local, (ref *)op, a_all, size, "zdelaybindparam");
468
0
        if (code < 0)
469
0
            return code;
470
471
        /* Create an array of the requested size in local VM */
472
0
        code = name_ref(imemory_local, (const byte *)names_array[i], strlen(names_array[i]), &key_name, 0);
473
0
        if (code < 0)
474
0
            return code; /* error */
475
476
        /* Hack; pretend system dict is defined in local VM so that 'put' won't complain (borrowed from .forceput) */
477
0
        r_set_space(&idict_stack.system_dict, avm_local);
478
0
        code = dict_put(&idict_stack.system_dict, (const ref *)&key_name, op, &idict_stack);
479
        /* Put systemdict VM back to global */
480
0
        r_set_space(&idict_stack.system_dict, space);
481
0
        if (code < 0)
482
0
            return code; /* error */
483
0
    }
484
0
    pop(1);
485
0
    return 0;
486
0
}
487
488
/* There are a few cases where a customer/user might want CPSI behavior
489
 * instead of the GS default behavior. cmyk_to_rgb and Type 1 char fill
490
 * method are two that have come up so far. This operator allows a PS
491
 * program to control the behavior without needing to recompile.
492
 */
493
/* <bool> .setCPSImode - */
494
static int
495
zsetCPSImode(i_ctx_t *i_ctx_p)
496
0
{
497
0
    os_ptr op = osp;
498
0
    check_op(1);
499
0
    check_type(*op, t_boolean);
500
0
    gs_setcpsimode(imemory, op->value.boolval);
501
0
    if (op->value.boolval) {
502
0
        i_ctx_p->scanner_options |= SCAN_CPSI_MODE;
503
0
    }
504
0
    else {
505
0
        i_ctx_p->scanner_options &= ~(int)SCAN_CPSI_MODE;
506
0
    }
507
0
    pop(1);
508
0
    return 0;
509
0
}
510
511
/* - .getCPSImode <bool> */
512
static int
513
zgetCPSImode(i_ctx_t *i_ctx_p)
514
27
{
515
27
    os_ptr op = osp;
516
517
27
    push(1);
518
27
    make_bool(op, gs_currentcpsimode(imemory));
519
27
    return 0;
520
27
}
521
522
/* <int> .setscanconverter - */
523
static int
524
zsetscanconverter(i_ctx_t *i_ctx_p)
525
0
{
526
0
    int val;
527
528
0
    os_ptr op = osp;
529
0
    check_op(1);
530
0
    if (r_has_type(op, t_boolean))
531
0
        val = (int)op->value.boolval;
532
0
    else if (r_has_type(op, t_integer))
533
0
        val = op->value.intval;
534
0
    else
535
0
        return_op_typecheck(op);
536
537
0
    gs_setscanconverter(igs, val);
538
0
    pop(1);
539
0
    return 0;
540
0
}
541
542
/* - .getscanconverter <int> */
543
static int
544
zgetscanconverter(i_ctx_t *i_ctx_p)
545
0
{
546
0
    os_ptr op = osp;
547
548
0
    push(1);
549
0
    make_int(op, gs_getscanconverter(imemory));
550
0
    return 0;
551
0
}
552
553
static int
554
zsetFormImplementation(i_ctx_t *i_ctx_p)
555
0
{
556
0
    os_ptr op = osp;
557
0
    ref *FormDict, *PaintProc;
558
0
    int code = 0;
559
560
0
    check_op(2);
561
0
    check_type(*(op - 1), t_dictionary);
562
563
0
    if (!r_has_type(op, t_null)) {
564
0
        if (!r_has_type(op, t_dictionary))
565
0
            return_error(gs_error_typecheck);
566
0
        else {
567
0
            if (dict_find_string(op, "FormID", &PaintProc) <= 0 || dict_maxlength(op) != 1)
568
0
                return_error(gs_error_typecheck);
569
0
        }
570
0
    }
571
572
0
    FormDict = op - 1;
573
0
    if (dict_find_string(FormDict, "PaintProc", &PaintProc) <= 0) {
574
0
        return_error(gs_error_typecheck);
575
0
    }
576
577
0
    code = dict_put_string(FormDict, "Implementation", op, &idict_stack);
578
0
    if (code < 0)
579
0
        return code;
580
581
0
    pop(2);
582
0
    return code;
583
0
}
584
585
static int
586
zsetInstances(i_ctx_t *i_ctx_p)
587
900k
{
588
900k
    os_ptr op = osp;
589
900k
    ref *CategoryDict, *InstancesDict;
590
900k
    int code = 0;
591
592
900k
    check_op(2);
593
900k
    check_type(*op, t_dictionary);
594
900k
    check_type(*(op - 1), t_dictionary);
595
596
900k
    CategoryDict = op - 1;
597
900k
    if (!r_is_local(CategoryDict)) {
598
900k
        if (r_is_local(op))
599
0
            return_error(gs_error_invalidaccess);
600
900k
    }
601
602
900k
    if (dict_find_string(CategoryDict, ".Instances", &InstancesDict) <= 0)
603
0
        return_error(gs_error_typecheck);
604
605
900k
    code = dict_put_string(CategoryDict, ".Instances", op, &idict_stack);
606
900k
    if (code < 0)
607
0
        return code;
608
609
900k
    pop(2);
610
900k
    return code;
611
900k
}
612
613
/* ------ Initialization procedure ------ */
614
615
const op_def zmisc_a_op_defs[] =
616
{
617
    {"1bind", zbind},
618
    {"1getenv", zgetenv},
619
    {"0.defaultpapersize", zdefaultpapersize},
620
    {"2.makeoperator", zmakeoperator},
621
    {"0.oserrno", zoserrno},
622
    {"1.oserrorstring", zoserrorstring},
623
    {"0realtime", zrealtime},
624
    {"0serialnumber", zserialnumber},
625
    {"2.setdebug", zsetdebug},
626
    {"1internaldict", zinternaldict},
627
    {"1.setoserrno", zsetoserrno},
628
    {"0usertime", zusertime},
629
    {"1.delaybindparam", zdelaybindparam},
630
    op_def_end(0)
631
};
632
633
const op_def zmisc_b_op_defs[] =
634
{
635
    {"1.setCPSImode", zsetCPSImode},
636
    {"0.getCPSImode", zgetCPSImode},
637
    {"1.setscanconverter", zsetscanconverter},
638
    {"0.getscanconverter", zgetscanconverter},
639
    {"0.mementolistnewblocks", zmementolistnewblocks},
640
    {"2.setFormImplementation", zsetFormImplementation},
641
    {"2.setInstances", zsetInstances},
642
    op_def_end(0)
643
};