Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/psi/zmisc.c
Line
Count
Source (jump to first uncovered line)
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
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
3.30G
{
43
3.30G
    return (r_has_attr(rp, a_executable) &&
44
3.30G
            (r_btype(rp) == t_operator || r_type(rp) == t_oparray));
45
3.30G
}
46
static int
47
zbind(i_ctx_t *i_ctx_p)
48
245M
{
49
245M
    os_ptr op = osp;
50
245M
    uint depth = 1;
51
245M
    ref defn;
52
245M
    register os_ptr bsp;
53
54
245M
    check_op(1);
55
245M
    switch (r_type(op)) {
56
1.29M
        case t_array:
57
1.29M
            if (!r_has_attr(op, a_write)) {
58
269
                return 0; /* per PLRM3 */
59
269
            }
60
118M
        case t_mixedarray:
61
143M
        case t_shortarray:
62
143M
            defn = *op;
63
143M
            break;
64
22.7M
        case t_oparray:
65
22.7M
            defn = *op->value.const_refs;
66
22.7M
            break;
67
79.8M
        default:
68
79.8M
            return_op_typecheck(op);
69
245M
    }
70
245M
    push(1);
71
165M
    *op = defn;
72
165M
    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.31G
    while (depth) {
83
19.6G
        while (r_size(bsp)) {
84
17.4G
            ref_packed *const tpp = (ref_packed *)bsp->value.packed; /* break const */
85
86
17.4G
            r_dec_size(bsp, 1);
87
17.4G
            if (r_is_packed(tpp)) {
88
                /* Check for a packed executable name */
89
11.0G
                ushort elt = *tpp;
90
91
11.0G
                if (r_packed_is_exec_name(&elt)) {
92
2.56G
                    ref nref;
93
2.56G
                    ref *pvalue;
94
95
2.56G
                    name_index_ref(imemory, packed_name_index(&elt),
96
2.56G
                                   &nref);
97
2.56G
                    if ((pvalue = dict_find_name(&nref)) != 0 &&
98
2.56G
                        r_is_ex_oper(pvalue)
99
2.56G
                        ) {
100
2.11G
                        store_check_dest(bsp, pvalue);
101
                        /*
102
                         * Always save the change, since this can only
103
                         * happen once.
104
                         */
105
2.11G
                        ref_do_save(bsp, tpp, "bind");
106
2.11G
                        *tpp = pt_tag(pt_executable_operator) +
107
2.11G
                            op_index(pvalue);
108
2.11G
                    }
109
2.56G
                }
110
11.0G
                bsp->value.packed = tpp + 1;
111
11.0G
            } else {
112
6.41G
                ref *const tp = bsp->value.refs++;
113
114
6.41G
                switch (r_type(tp)) {
115
1.71G
                    case t_name:  /* bind the name if an operator */
116
1.71G
                        if (r_has_attr(tp, a_executable)) {
117
1.17G
                            ref *pvalue;
118
119
1.17G
                            if ((pvalue = dict_find_name(tp)) != 0 &&
120
1.17G
                                r_is_ex_oper(pvalue)
121
1.17G
                                ) {
122
601M
                                store_check_dest(bsp, pvalue);
123
601M
                                ref_assign_old(bsp, tp, pvalue, "bind");
124
601M
                            }
125
1.17G
                        }
126
1.71G
                        break;
127
1.71G
                    case t_array: /* push into array if writable */
128
24.8M
                        if (!r_has_attr(tp, a_write))
129
3.99M
                            break;
130
1.42G
                    case t_mixedarray:
131
1.99G
                    case t_shortarray:
132
1.99G
                        if (r_has_attr(tp, a_executable)) {
133
                            /* Make reference read-only */
134
1.98G
                            r_clear_attrs(tp, a_write);
135
1.98G
                            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.98G
                                *++bsp = *tp;
151
1.98G
                            depth++;
152
1.98G
                        }
153
6.41G
                }
154
6.41G
            }
155
17.4G
        }
156
2.15G
        bsp--;
157
2.15G
        depth--;
158
2.15G
        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
2.15G
    }
164
165M
    osp = bsp;
165
165M
    return 0;
166
165M
}
167
168
/* - serialnumber <int> */
169
static int
170
zserialnumber(i_ctx_t *i_ctx_p)
171
28
{
172
28
    os_ptr op = osp;
173
174
28
    push(1);
175
28
    make_int(op, gp_serialnumber());
176
28
    return 0;
177
28
}
178
179
/* - realtime <int> */
180
static int
181
zrealtime(i_ctx_t *i_ctx_p)
182
12
{
183
12
    os_ptr op = osp;
184
12
    long secs_ns[2];
185
12
    gs_lib_ctx_t *libctx = gs_lib_ctx_get_interp_instance(imemory);
186
187
12
    gp_get_realtime(secs_ns);
188
12
    secs_ns[1] -= libctx->real_time_0[1];
189
12
    secs_ns[0] -= libctx->real_time_0[0];
190
12
    push(1);
191
12
    make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
192
12
    return 0;
193
12
}
194
195
/* - usertime <int> */
196
static int
197
zusertime(i_ctx_t *i_ctx_p)
198
988
{
199
988
    gs_context_state_t *current = (gs_context_state_t *)i_ctx_p;
200
988
    os_ptr op = osp;
201
988
    long secs_ns[2];
202
203
988
    gp_get_usertime(secs_ns);
204
988
    if (!current->usertime_inited) {
205
254
        current->usertime_inited = true;
206
254
        current->usertime_0[0] = secs_ns[0];
207
254
        current->usertime_0[1] = secs_ns[1];
208
254
    }
209
988
    secs_ns[0] -= current->usertime_0[0];
210
988
    secs_ns[1] -= current->usertime_0[1];
211
988
    push(1);
212
988
    make_int(op, secs_ns[0] * 1000 + secs_ns[1] / 1000000);
213
988
    return 0;
214
988
}
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
1.11M
{
223
1.11M
    os_ptr op = osp;
224
1.11M
    char *str;
225
1.11M
    byte *value;
226
1.11M
    int len = 0;
227
228
1.11M
    check_op(1);
229
1.11M
    check_read_type(*op, t_string);
230
1.11M
    str = ref_to_string(op, imemory, "getenv key");
231
1.11M
    if (str == 0)
232
0
        return_error(gs_error_VMerror);
233
1.11M
    if (gp_getenv(str, (char *)0, &len) > 0) { /* key missing */
234
1.11M
        ifree_string((byte *) str, r_size(op) + 1, "getenv key");
235
1.11M
        make_false(op);
236
1.11M
        return 0;
237
1.11M
    }
238
0
    value = ialloc_string(len, "getenv value");
239
0
    if (value == 0) {
240
0
        ifree_string((byte *) str, r_size(op) + 1, "getenv key");
241
0
        return_error(gs_error_VMerror);
242
0
    }
243
0
    DISCARD(gp_getenv(str, (char *)value, &len));  /* can't fail */
244
0
    ifree_string((byte *) str, r_size(op) + 1, "getenv key");
245
    /* Delete the stupid C string terminator. */
246
0
    value = iresize_string(value, len, len - 1,
247
0
                           "getenv value"); /* can't fail */
248
0
    push(1);
249
0
    make_string(op - 1, a_all | icurrent_space, len - 1, value);
250
0
    make_true(op);
251
0
    return 0;
252
0
}
253
254
/* - .defaultpapersize <string> true */
255
/* - .defaultpapersize false */
256
static int
257
zdefaultpapersize(i_ctx_t *i_ctx_p)
258
159k
{
259
159k
    os_ptr op = osp;
260
159k
    byte *value;
261
159k
    int len = 0, i;
262
263
159k
    if (gp_defaultpapersize((char *)0, &len) > 0) {
264
        /* no default paper size */
265
159k
        push(1);
266
159k
        make_false(op);
267
159k
        return 0;
268
159k
    }
269
270
0
    value = ialloc_string(len, "defaultpapersize value");
271
0
    if (value == 0) {
272
0
        return_error(gs_error_VMerror);
273
0
    }
274
0
    DISCARD(gp_defaultpapersize((char *)value, &len)); /* can't fail */
275
    /* Note 'len' includes the NULL terminator, which we can ignore */
276
0
    for (i = 0;i < (len - 1); i++)
277
0
        value[i] = tolower(value[i]);
278
279
    /* Delete the stupid C string terminator. */
280
0
    value = iresize_string(value, len, len - 1,
281
0
                           "defaultpapersize value"); /* can't fail */
282
0
    push(2);
283
0
    make_string(op - 1, a_all | icurrent_space, len - 1, value);
284
0
    make_true(op);
285
0
    return 0;
286
0
}
287
288
/* <name> <proc> .makeoperator <oper> */
289
static int
290
zmakeoperator(i_ctx_t *i_ctx_p)
291
29.4M
{
292
29.4M
    os_ptr op = osp;
293
29.4M
    op_array_table *opt;
294
29.4M
    uint count;
295
29.4M
    ref *tab;
296
297
29.4M
    check_op(2);
298
29.4M
    check_type(op[-1], t_name);
299
29.4M
    check_proc(*op);
300
29.4M
    switch (r_space(op)) {
301
29.4M
        case avm_global:
302
29.4M
            opt = &i_ctx_p->op_array_table_global;
303
29.4M
            break;
304
0
        case avm_local:
305
0
            opt = &i_ctx_p->op_array_table_local;
306
0
            break;
307
0
        default:
308
0
            return_error(gs_error_invalidaccess);
309
29.4M
    }
310
29.4M
    count = opt->count;
311
29.4M
    tab = opt->table.value.refs;
312
    /*
313
     * restore doesn't reset op_array_table.count, but it does
314
     * remove entries from op_array_table.table.  Since we fill
315
     * the table in order, we can detect that a restore has occurred
316
     * by checking whether what should be the most recent entry
317
     * is occupied.  If not, we scan backwards over the vacated entries
318
     * to find the true end of the table.
319
     */
320
29.4M
    while (count > 0 && r_has_type(&tab[count - 1], t_null))
321
0
        --count;
322
29.4M
    if (count == r_size(&opt->table))
323
0
        return_error(gs_error_limitcheck);
324
29.4M
    ref_assign_old(&opt->table, &tab[count], op, "makeoperator");
325
29.4M
    opt->nx_table[count] = name_index(imemory, op - 1);
326
29.4M
    op_index_ref(imemory, opt->base_index + count, op - 1);
327
29.4M
    opt->count = count + 1;
328
29.4M
    pop(1);
329
29.4M
    return 0;
330
29.4M
}
331
332
/* - .oserrno <int> */
333
static int
334
zoserrno(i_ctx_t *i_ctx_p)
335
43.7k
{
336
43.7k
    os_ptr op = osp;
337
338
43.7k
    push(1);
339
43.7k
    make_int(op, errno);
340
43.7k
    return 0;
341
43.7k
}
342
343
/* <int> .setoserrno - */
344
static int
345
zsetoserrno(i_ctx_t *i_ctx_p)
346
363k
{
347
363k
    os_ptr op = osp;
348
349
363k
    check_op(1);
350
363k
    check_type(*op, t_integer);
351
363k
    errno = op->value.intval;
352
363k
    pop(1);
353
363k
    return 0;
354
363k
}
355
356
/* <int> .oserrorstring <string> true */
357
/* <int> .oserrorstring false */
358
static int
359
zoserrorstring(i_ctx_t *i_ctx_p)
360
38.7k
{
361
38.7k
    os_ptr op = osp;
362
38.7k
    const char *str;
363
38.7k
    int code;
364
38.7k
    uint len;
365
38.7k
    byte ch;
366
367
38.7k
    check_op(1);
368
38.7k
    check_type(*op, t_integer);
369
38.7k
    str = gp_strerror((int)op->value.intval);
370
38.7k
    if (str == 0 || (len = strlen(str)) == 0) {
371
0
        make_false(op);
372
0
        return 0;
373
0
    }
374
38.7k
    check_ostack(1);
375
38.7k
    code = string_to_ref(str, op, iimemory, ".oserrorstring");
376
38.7k
    if (code < 0)
377
0
        return code;
378
    /* Strip trailing end-of-line characters. */
379
38.7k
    while ((len = r_size(op)) != 0 &&
380
38.7k
           ((ch = op->value.bytes[--len]) == '\r' || ch == '\n')
381
38.7k
        )
382
0
        r_dec_size(op, 1);
383
38.7k
    push(1);
384
38.7k
    make_true(op);
385
38.7k
    return 0;
386
38.7k
}
387
388
/* <string> <bool> .setdebug - */
389
static int
390
zsetdebug(i_ctx_t *i_ctx_p)
391
0
{
392
0
    os_ptr op = osp;
393
0
    check_op(2);
394
0
    check_read_type(op[-1], t_string);
395
0
    check_type(*op, t_boolean);
396
0
    {
397
0
        int i;
398
399
0
        for (i = 0; i < r_size(op - 1); i++)
400
0
            gs_debug[op[-1].value.bytes[i] & 127] =
401
0
                op->value.boolval;
402
0
    }
403
0
    pop(2);
404
0
    return 0;
405
0
}
406
407
/* .mementolistnew - */
408
static int
409
zmementolistnewblocks(i_ctx_t *i_ctx_p)
410
0
{
411
0
    Memento_listNewBlocks();
412
0
    return 0;
413
0
}
414
415
/* There are a few cases where a customer/user might want CPSI behavior
416
 * instead of the GS default behavior. cmyk_to_rgb and Type 1 char fill
417
 * method are two that have come up so far. This operator allows a PS
418
 * program to control the behavior without needing to recompile.
419
 */
420
/* <bool> .setCPSImode - */
421
static int
422
zsetCPSImode(i_ctx_t *i_ctx_p)
423
0
{
424
0
    os_ptr op = osp;
425
0
    check_op(1);
426
0
    check_type(*op, t_boolean);
427
0
    gs_setcpsimode(imemory, op->value.boolval);
428
0
    if (op->value.boolval) {
429
0
        i_ctx_p->scanner_options |= SCAN_CPSI_MODE;
430
0
    }
431
0
    else {
432
0
        i_ctx_p->scanner_options &= ~(int)SCAN_CPSI_MODE;
433
0
    }
434
0
    pop(1);
435
0
    return 0;
436
0
}
437
438
/* - .getCPSImode <bool> */
439
static int
440
zgetCPSImode(i_ctx_t *i_ctx_p)
441
54
{
442
54
    os_ptr op = osp;
443
444
54
    push(1);
445
54
    make_bool(op, gs_currentcpsimode(imemory));
446
54
    return 0;
447
54
}
448
449
/* <int> .setscanconverter - */
450
static int
451
zsetscanconverter(i_ctx_t *i_ctx_p)
452
0
{
453
0
    int val;
454
455
0
    os_ptr op = osp;
456
0
    check_op(1);
457
0
    if (r_has_type(op, t_boolean))
458
0
        val = (int)op->value.boolval;
459
0
    else if (r_has_type(op, t_integer))
460
0
        val = op->value.intval;
461
0
    else
462
0
        return_op_typecheck(op);
463
464
0
    gs_setscanconverter(igs, val);
465
0
    pop(1);
466
0
    return 0;
467
0
}
468
469
/* - .getscanconverter <int> */
470
static int
471
zgetscanconverter(i_ctx_t *i_ctx_p)
472
0
{
473
0
    os_ptr op = osp;
474
475
0
    push(1);
476
0
    make_int(op, gs_getscanconverter(imemory));
477
0
    return 0;
478
0
}
479
/* ------ Initialization procedure ------ */
480
481
const op_def zmisc_a_op_defs[] =
482
{
483
    {"1bind", zbind},
484
    {"1getenv", zgetenv},
485
    {"0.defaultpapersize", zdefaultpapersize},
486
    {"2.makeoperator", zmakeoperator},
487
    {"0.oserrno", zoserrno},
488
    {"1.oserrorstring", zoserrorstring},
489
    {"0realtime", zrealtime},
490
    {"0serialnumber", zserialnumber},
491
    {"2.setdebug", zsetdebug},
492
    {"0.mementolistnewblocks", zmementolistnewblocks},
493
    {"1.setoserrno", zsetoserrno},
494
    {"0usertime", zusertime},
495
    op_def_end(0)
496
};
497
498
const op_def zmisc_b_op_defs[] =
499
{
500
    {"1.setCPSImode", zsetCPSImode},
501
    {"0.getCPSImode", zgetCPSImode},
502
    {"1.setscanconverter", zsetscanconverter},
503
    {"0.getscanconverter", zgetscanconverter},
504
    op_def_end(0)
505
};