Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/ziodev.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
/* Standard IODevice implementation */
18
#include "memory_.h"
19
#include "stdio_.h"
20
#include "string_.h"
21
#include "ghost.h"
22
#include "gp.h"
23
#include "gpcheck.h"
24
#include "oper.h"
25
#include "stream.h"
26
#include "istream.h"
27
#include "ialloc.h"
28
#include "iscan.h"
29
#include "ivmspace.h"
30
#include "gxiodev.h"            /* must come after stream.h */
31
                                /* and before files.h */
32
#include "files.h"
33
#include "scanchar.h"           /* for char_EOL */
34
#include "store.h"
35
#include "ierrors.h"
36
37
/* Import the dtype of the stdio IODevices. */
38
extern const char iodev_dtype_stdio[];
39
40
/* Define the special devices. */
41
#define iodev_special(dname, init, finit, open) {\
42
    dname, iodev_dtype_stdio,\
43
        { init, finit, open, iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,\
44
          iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,\
45
          iodev_no_enumerate_files, NULL, NULL,\
46
          iodev_no_get_params, iodev_no_put_params\
47
        }, \
48
        NULL, \
49
        NULL \
50
}
51
52
/*
53
 * We need the current context pointer for accessing / opening the %std
54
 * IODevices.  However, this is not available to the open routine.
55
 * Therefore, we use the hack of storing this pointer in the IODevice state
56
 * pointer just before calling the open routines.  We clear the pointer
57
 * immediately afterwards so as not to wind up with dangling references.
58
 */
59
60
0
#define LINEEDIT_BUF_SIZE 20    /* initial size, not fixed size */
61
/*static iodev_proc_open_device(lineedit_open);*/ /* no longer used */
62
const gx_io_device gs_iodev_lineedit =
63
    iodev_special("%lineedit%", iodev_no_init, iodev_no_finit, \
64
                                         iodev_no_open_device);
65
66
0
#define STATEMENTEDIT_BUF_SIZE 50       /* initial size, not fixed size */
67
/*static iodev_proc_open_device(statementedit_open);*/ /* no longer used */
68
const gx_io_device gs_iodev_statementedit =
69
    iodev_special("%statementedit%", iodev_no_init, iodev_no_finit, \
70
                                             iodev_no_open_device);
71
72
/* ------ Operators ------ */
73
74
/* <int> .getiodevice <string|null> */
75
static int
76
zgetiodevice(i_ctx_t *i_ctx_p)
77
2.43M
{
78
2.43M
    os_ptr op = osp;
79
2.43M
    gx_io_device *iodev;
80
2.43M
    const byte *dname;
81
82
2.43M
    check_type(*op, t_integer);
83
2.43M
    iodev = gs_getiodevice(imemory, (int)(op->value.intval));
84
2.43M
    if (iodev == 0)             /* index out of range */
85
162k
        return_error(gs_error_rangecheck);
86
2.27M
    dname = (const byte *)iodev->dname;
87
2.27M
    if (dname == 0)
88
2.27M
        make_null(op);
89
2.27M
    else
90
2.27M
        make_const_string(op, a_readonly | avm_foreign,
91
2.27M
                          strlen((const char *)dname), dname);
92
2.27M
    return 0;
93
2.43M
}
94
95
#define COMPILE_TIME_ASSERT(A,B) typedef char A[(B) ? 1 : -1]
96
97
/* ------ %lineedit and %statementedit ------ */
98
99
/* <file> <bool> <int> <string> .filelineedit <file> */
100
/* This opens %statementedit% or %lineedit% and is also the
101
 * continuation proc for callouts.
102
 * Input:
103
 *  string is the statement/line buffer,
104
 *  int is the write index into string
105
 *  bool is true if %statementedit%
106
 *  file is stdin
107
 * Output:
108
 *  file is a string based stream
109
 * We store the line being read in a PostScript string.
110
 * This limits the size to max_string_size (64k).
111
 * This could be increased by storing the input line in something
112
 * other than a PostScript string.
113
 */
114
COMPILE_TIME_ASSERT(STATEMENTEDIT_SIZE_CHECK, STATEMENTEDIT_BUF_SIZE <= max_string_size);
115
COMPILE_TIME_ASSERT(LINEEDIT_BUF_SIZE_CHECK, LINEEDIT_BUF_SIZE <= max_string_size);
116
117
int
118
zfilelineedit(i_ctx_t *i_ctx_p)
119
0
{
120
0
    uint count = 0;
121
0
    bool in_eol = false;
122
0
    int code;
123
0
    os_ptr op = osp;
124
0
    bool statement;
125
0
    stream *s;
126
0
    stream *ins;
127
0
    gs_string str;
128
0
    uint initial_buf_size;
129
0
    const char *filename;
130
    /*
131
     * buf exists only for stylistic parallelism: all occurrences of
132
     * buf-> could just as well be str. .
133
     */
134
0
    gs_string *const buf = &str;
135
136
0
    check_type(*op, t_string);          /* line assembled so far */
137
0
    buf->data = op->value.bytes;
138
0
    buf->size = op->tas.rsize;
139
0
    check_type(*(op-1), t_integer);     /* index */
140
0
    count = (op-1)->value.intval;
141
0
    check_type(*(op-2), t_boolean);     /* statementedit/lineedit */
142
0
    statement = (op-2)->value.boolval;
143
0
    check_read_file(i_ctx_p, ins, op - 3);      /* %stdin */
144
145
    /* extend string */
146
0
    initial_buf_size = statement ? STATEMENTEDIT_BUF_SIZE : LINEEDIT_BUF_SIZE;
147
0
    if (!buf->data || (buf->size < initial_buf_size)) {
148
0
        count = 0;
149
0
        buf->data = gs_alloc_string(imemory_system, initial_buf_size,
150
0
            "zfilelineedit(buffer)");
151
0
        if (buf->data == 0)
152
0
            return_error(gs_error_VMerror);
153
0
        op->value.bytes = buf->data;
154
0
        op->tas.rsize = buf->size = initial_buf_size;
155
0
    }
156
157
0
rd:
158
0
    code = zreadline_from(ins, buf, imemory_system, &count, &in_eol);
159
0
    if (buf->size > max_string_size) {
160
        /* zreadline_from reallocated the buffer larger than
161
         * is valid for a PostScript string.
162
         * Return an error, but first realloc the buffer
163
         * back to a legal size.
164
         */
165
0
        byte *nbuf = gs_resize_string(imemory_system, buf->data, buf->size,
166
0
                max_string_size, "zfilelineedit(shrink buffer)");
167
0
        if (nbuf == 0)
168
0
            return_error(gs_error_VMerror);
169
0
        op->value.bytes = buf->data = nbuf;
170
0
        op->tas.rsize = buf->size = max_string_size;
171
0
        return_error(gs_error_limitcheck);
172
0
    }
173
174
0
    op->value.bytes = buf->data; /* zreadline_from sometimes resizes the buffer. */
175
0
    op->tas.rsize = buf->size;
176
177
0
    switch (code) {
178
0
        case EOFC:
179
0
            code = gs_note_error(gs_error_undefinedfilename);
180
            /* falls through */
181
0
        case 0:
182
0
            break;
183
0
        default:
184
0
            code = gs_note_error(gs_error_ioerror);
185
0
            break;
186
0
        case CALLC:
187
0
            {
188
0
                ref rfile;
189
0
                (op-1)->value.intval = count;
190
                /* callout is for stdin */
191
0
                make_file(&rfile, a_readonly | avm_system, ins->read_id, ins);
192
0
                code = s_handle_read_exception(i_ctx_p, code, &rfile,
193
0
                    NULL, 0, zfilelineedit);
194
0
            }
195
0
            break;
196
0
        case 1:         /* filled buffer */
197
0
            {
198
0
                uint nsize = buf->size;
199
0
                byte *nbuf;
200
201
0
                if (nsize >= max_string_size) {
202
0
                    code = gs_note_error(gs_error_limitcheck);
203
0
                    break;
204
0
                }
205
0
                else if (nsize >= max_string_size / 2)
206
0
                    nsize= max_string_size;
207
0
                else
208
0
                    nsize = buf->size * 2;
209
0
                nbuf = gs_resize_string(imemory_system, buf->data, buf->size, nsize,
210
0
                                        "zfilelineedit(grow buffer)");
211
0
                if (nbuf == 0) {
212
0
                    code = gs_note_error(gs_error_VMerror);
213
0
                    break;
214
0
                }
215
0
                op->value.bytes = buf->data = nbuf;
216
0
                op->tas.rsize = buf->size = nsize;
217
0
                goto rd;
218
0
            }
219
0
    }
220
0
    if (code != 0)
221
0
        return code;
222
0
    if (statement) {
223
        /* If we don't have a complete token, keep going. */
224
0
        stream st;
225
0
        stream *ts = &st;
226
0
        scanner_state state;
227
0
        ref ignore_value;
228
0
        uint depth = ref_stack_count(&o_stack);
229
0
        int code;
230
231
        /* Add a terminating EOL. */
232
0
        if (count + 1 > buf->size) {
233
0
            uint nsize;
234
0
            byte *nbuf;
235
236
0
            nsize = buf->size + 1;
237
0
            if (nsize > max_string_size) {
238
0
                return_error(gs_note_error(gs_error_limitcheck));
239
0
            }
240
0
            else {
241
0
                nbuf = gs_resize_string(imemory_system, buf->data, buf->size, nsize,
242
0
                                        "zfilelineedit(grow buffer)");
243
0
                if (nbuf == 0) {
244
0
                    code = gs_note_error(gs_error_VMerror);
245
0
                    return_error(code);
246
0
                }
247
0
                op->value.bytes = buf->data = nbuf;
248
0
                op->tas.rsize = buf->size = nsize;
249
0
            }
250
0
        }
251
0
        buf->data[count++] = char_EOL;
252
0
        s_init(ts, NULL);
253
0
        sread_string(ts, buf->data, count);
254
0
sc:
255
0
        gs_scanner_init_stream_options(&state, ts, SCAN_CHECK_ONLY);
256
0
        ialloc_set_space(idmemory, avm_local);
257
0
        code = gs_scan_token(i_ctx_p, &ignore_value, &state);
258
0
        ref_stack_pop_to(&o_stack, depth);
259
0
        if (code < 0)
260
0
            code = scan_EOF;    /* stop on scanner error */
261
0
        switch (code) {
262
0
            case 0:             /* read a token */
263
0
            case scan_BOS:
264
0
                goto sc;        /* keep going until we run out of data */
265
0
            case scan_Refill:
266
0
                goto rd;
267
0
            case scan_EOF:
268
0
                break;
269
0
            default:            /* error */
270
0
                return code;
271
0
        }
272
0
    }
273
0
    buf->data = gs_resize_string(imemory_system, buf->data, buf->size, count,
274
0
                           "zfilelineedit(resize buffer)");
275
0
    if (buf->data == 0)
276
0
        return_error(gs_error_VMerror);
277
0
    op->value.bytes = buf->data;
278
0
    op->tas.rsize = buf->size;
279
280
0
    s = file_alloc_stream(imemory_system, "zfilelineedit(stream)");
281
0
    if (s == 0)
282
0
        return_error(gs_error_VMerror);
283
284
0
    sread_string(s, buf->data, count);
285
0
    s->save_close = s->procs.close;
286
0
    s->procs.close = file_close_disable;
287
288
0
    filename = statement ? gs_iodev_statementedit.dname
289
0
        : gs_iodev_lineedit.dname;
290
0
    code = ssetfilename(s, (const byte *)filename, strlen(filename)+1);
291
0
    if (code < 0) {
292
0
        sclose(s);
293
0
        return_error(gs_error_VMerror);
294
0
    }
295
296
0
    ref_stack_pop(&o_stack, 3);
297
0
    make_stream_file(osp, s, "r");
298
299
0
    return code;
300
0
}
301
302
/* ------ Initialization procedure ------ */
303
304
const op_def ziodev_op_defs[] =
305
{
306
    {"1.getiodevice", zgetiodevice},
307
    op_def_end(0)
308
};