Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/psi/ziodevsc.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
/* %stdxxx IODevice implementation using callouts for PostScript interpreter */
18
#include "stdio_.h"
19
#include "ghost.h"
20
#include "gpcheck.h"
21
#include "gp.h"
22
#include "oper.h"
23
#include "stream.h"
24
#include "gxiodev.h"    /* must come after stream.h */
25
                                /* and before files.h */
26
#include "istream.h"
27
#include "files.h"
28
#include "ifilter.h"
29
#include "store.h"
30
31
/* Define the special devices. */
32
const char iodev_dtype_stdio[] = "Special";
33
#define iodev_special(dname, init, finit, open) {\
34
    dname, iodev_dtype_stdio,\
35
        { init, finit, open, iodev_no_open_file, iodev_no_fopen, iodev_no_fclose,\
36
          iodev_no_delete_file, iodev_no_rename_file, iodev_no_file_status,\
37
          iodev_no_enumerate_files, NULL, NULL,\
38
          iodev_no_get_params, iodev_no_put_params\
39
        }, \
40
        NULL, \
41
        NULL \
42
}
43
44
/*
45
 * We need the current context pointer for accessing / opening the %std
46
 * IODevices.  However, this is not available to the open routine.
47
 * Therefore, we use the hack of storing this pointer in the IODevice state
48
 * pointer just before calling the open routines.  We clear the pointer
49
 * immediately afterwards so as not to wind up with dangling references.
50
 */
51
52
10.8k
#define STDIN_BUF_SIZE 1024
53
static iodev_proc_init(stdin_init);
54
static iodev_proc_finit(stdin_finit);
55
static iodev_proc_open_device(stdin_open);
56
const gx_io_device gs_iodev_stdin =
57
    iodev_special("%stdin%", stdin_init, stdin_finit, stdin_open);
58
59
10.8k
#define STDOUT_BUF_SIZE 128
60
static iodev_proc_open_device(stdout_open);
61
const gx_io_device gs_iodev_stdout =
62
    iodev_special("%stdout%", iodev_no_init, iodev_no_finit, stdout_open);
63
64
10.8k
#define STDERR_BUF_SIZE 128
65
static iodev_proc_open_device(stderr_open);
66
const gx_io_device gs_iodev_stderr =
67
    iodev_special("%stderr%", iodev_no_init, iodev_no_finit, stderr_open);
68
69
/* ------- %stdin, %stdout, and %stderr ------ */
70
71
/*
72
 * According to Adobe, it is legal to close the %std... files and then
73
 * re-open them later.  However, the re-opened file object is not 'eq' to
74
 * the original file object (in our implementation, it has a different
75
 * read_id or write_id). This is performed in 'file_close_file' by the
76
 * call to file_close_disable.
77
 */
78
79
static int
80
stdin_init(gx_io_device * iodev, gs_memory_t * mem)
81
21.6k
{
82
21.6k
    mem->gs_lib_ctx->core->stdin_is_interactive = true;
83
21.6k
    return 0;
84
21.6k
}
85
86
static void
87
stdin_finit(gx_io_device * iodev, gs_memory_t * mem)
88
8.16k
{
89
8.16k
    mem->gs_lib_ctx->core->stdin_is_interactive = false;
90
8.16k
    return;
91
8.16k
}
92
93
/* Read from stdin into the buffer. */
94
/* If interactive, only read one character. */
95
static int
96
s_stdin_read_process(stream_state * st, stream_cursor_read * ignore_pr,
97
                     stream_cursor_write * pw, bool last)
98
60.2k
{
99
60.2k
    int wcount = (int)(pw->limit - pw->ptr);
100
60.2k
    int count;
101
60.2k
    gs_memory_t *mem = st->memory;
102
60.2k
    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
103
104
60.2k
    if (wcount <= 0)
105
0
        return 0;
106
107
    /* do the callout */
108
60.2k
    if (core->stdin_fn)
109
60.2k
        count = (*core->stdin_fn)
110
60.2k
            (core->std_caller_handle, (char *)pw->ptr + 1,
111
60.2k
             core->stdin_is_interactive ? 1 : wcount);
112
0
    else
113
0
        count = gp_stdin_read((char *)pw->ptr + 1, wcount,
114
0
                      core->stdin_is_interactive,
115
0
                      core->fstdin);
116
117
60.2k
    pw->ptr += (count < 0) ? 0 : count;
118
60.2k
    return ((count < 0) ? ERRC : (count == 0) ? EOFC : count);
119
60.2k
}
120
121
static int
122
stdin_open(gx_io_device * iodev, const char *access, stream ** ps,
123
           gs_memory_t * mem)
124
27.1k
{
125
27.1k
    i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
126
27.1k
    stream *s;
127
128
27.1k
    if (!streq1(access, 'r'))
129
0
        return_error(gs_error_invalidfileaccess);
130
27.1k
    if (file_is_invalid(s, &ref_stdin)) {
131
        /****** stdin SHOULD NOT LINE-BUFFER ******/
132
10.8k
        gs_memory_t *sysmem = imemory_system;
133
10.8k
        byte *buf;
134
10.8k
        static const stream_procs p = {
135
10.8k
            s_std_noavailable, s_std_noseek, s_std_read_reset,
136
10.8k
            s_std_read_flush, file_close_file, s_stdin_read_process
137
10.8k
        };
138
139
10.8k
        s = file_alloc_stream(sysmem, "stdin_open(stream)");
140
141
        /* We want stdin to read only one character at a time, */
142
        /* but it must have a substantial buffer, in case it is used */
143
        /* by a stream that requires more than one input byte */
144
        /* to make progress. */
145
10.8k
        buf = gs_alloc_bytes(sysmem, STDIN_BUF_SIZE, "stdin_open(buffer)");
146
10.8k
        if (s == 0 || buf == 0)
147
0
            return_error(gs_error_VMerror);
148
149
10.8k
        s_std_init(s, buf, STDIN_BUF_SIZE, &p, s_mode_read);
150
10.8k
        s->file = 0;
151
10.8k
        s->file_modes = s->modes;
152
10.8k
        s->file_offset = 0;
153
10.8k
        s->file_limit = S_FILE_LIMIT_MAX;
154
10.8k
        s->save_close = s_std_null;
155
10.8k
        make_file(&ref_stdin, a_readonly | avm_system, s->read_id, s);
156
10.8k
        *ps = s;
157
10.8k
        return 1;
158
10.8k
    }
159
16.3k
    *ps = s;
160
16.3k
    return 0;
161
27.1k
}
162
/* This is the public routine for getting the stdin stream. */
163
int
164
zget_stdin(i_ctx_t *i_ctx_p, stream ** ps)
165
0
{
166
0
    stream *s;
167
0
    gx_io_device *iodev;
168
0
    int code;
169
170
0
    if (file_is_valid(s, &ref_stdin)) {
171
0
        *ps = s;
172
0
        return 0;
173
0
    }
174
0
    iodev = gs_findiodevice(imemory, (const byte *)"%stdin", 6);
175
0
    iodev->state = i_ctx_p;
176
0
    code = (*iodev->procs.open_device)(iodev, "r", ps, imemory_system);
177
0
    iodev->state = NULL;
178
0
    return min(code, 0);
179
0
}
180
181
/* Test whether a stream is stdin. */
182
bool
183
zis_stdin(const stream *s)
184
2
{
185
2
    return (s_is_valid(s) && s->procs.process == s_stdin_read_process);
186
2
}
187
188
/* Write a buffer to stdout, potentially writing to callback */
189
static int
190
s_stdout_write_process(stream_state * st, stream_cursor_read *pr,
191
                     stream_cursor_write *ignore_pw, bool last)
192
1.27M
{
193
1.27M
    uint count = pr->limit - pr->ptr;
194
1.27M
    int written;
195
196
1.27M
    if (count == 0)
197
27.8k
        return 0;
198
1.24M
    written = outwrite(st->memory, (const char *)pr->ptr + 1, count);
199
1.24M
    if (written != count)
200
0
        return ERRC;
201
1.24M
    pr->ptr += written;
202
1.24M
    return 0;
203
1.24M
}
204
205
static int
206
stdout_open(gx_io_device * iodev, const char *access, stream ** ps,
207
            gs_memory_t * mem)
208
5.71M
{
209
5.71M
    i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
210
5.71M
    stream *s;
211
212
5.71M
    if (!streq1(access, 'w'))
213
0
        return_error(gs_error_invalidfileaccess);
214
5.71M
    if (file_is_invalid(s, &ref_stdout)) {
215
10.8k
        gs_memory_t *sysmem = imemory_system;
216
10.8k
        byte *buf;
217
10.8k
        static const stream_procs p = {
218
10.8k
            s_std_noavailable, s_std_noseek, s_std_write_reset,
219
10.8k
            s_std_write_flush, file_close_file, s_stdout_write_process
220
10.8k
        };
221
222
10.8k
        s = file_alloc_stream(sysmem, "stdout_open(stream)");
223
10.8k
        buf = gs_alloc_bytes(sysmem, STDOUT_BUF_SIZE, "stdout_open(buffer)");
224
10.8k
        if (s == 0 || buf == 0)
225
0
            return_error(gs_error_VMerror);
226
10.8k
        s_std_init(s, buf, STDOUT_BUF_SIZE, &p, s_mode_write);
227
10.8k
        s->file = 0;
228
10.8k
        s->file_modes = s->modes;
229
10.8k
        s->file_offset = 0;   /* in case we switch to reading later */
230
10.8k
        s->file_limit = S_FILE_LIMIT_MAX;
231
10.8k
        s->save_close = s->procs.flush;
232
10.8k
        make_file(&ref_stdout, a_write | avm_system, s->write_id, s);
233
10.8k
        *ps = s;
234
10.8k
        return 1;
235
10.8k
    }
236
5.70M
    *ps = s;
237
5.70M
    return 0;
238
5.71M
}
239
240
/* This is the public routine for getting the stdout stream. */
241
int
242
zget_stdout(i_ctx_t *i_ctx_p, stream ** ps)
243
5.75M
{
244
5.75M
    stream *s;
245
5.75M
    gx_io_device *iodev;
246
5.75M
    int code;
247
248
5.75M
    if (file_is_valid(s, &ref_stdout)) {
249
5.75M
        *ps = s;
250
5.75M
        return 0;
251
5.75M
    }
252
0
    iodev = gs_findiodevice(imemory, (const byte *)"%stdout", 7);
253
0
    iodev->state = i_ctx_p;
254
0
    code = (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
255
0
    iodev->state = NULL;
256
0
    return min(code, 0);
257
5.75M
}
258
259
/* Write a buffer to stderr, potentially writing to callback */
260
static int
261
s_stderr_write_process(stream_state * st, stream_cursor_read *pr,
262
                     stream_cursor_write *ignore_pw, bool last)
263
11.5k
{
264
11.5k
    uint count = pr->limit - pr->ptr;
265
11.5k
    int written;
266
267
11.5k
    if (count == 0)
268
10.8k
        return 0;
269
722
    written = errwrite(st->memory, (const char *)(pr->ptr + 1), count);
270
722
    if (written < count)
271
0
        return ERRC;
272
722
    pr->ptr += written;
273
722
    return 0;
274
722
}
275
276
static int
277
stderr_open(gx_io_device * iodev, const char *access, stream ** ps,
278
            gs_memory_t * mem)
279
22.3k
{
280
22.3k
    i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
281
22.3k
    stream *s;
282
283
22.3k
    if (!streq1(access, 'w'))
284
0
        return_error(gs_error_invalidfileaccess);
285
22.3k
    if (file_is_invalid(s, &ref_stderr)) {
286
10.8k
        gs_memory_t *sysmem = imemory_system;
287
10.8k
        byte *buf;
288
10.8k
        static const stream_procs p = {
289
10.8k
            s_std_noavailable, s_std_noseek, s_std_write_reset,
290
10.8k
            s_std_write_flush, file_close_file, s_stderr_write_process
291
10.8k
        };
292
293
10.8k
        s = file_alloc_stream(sysmem, "stderr_open(stream)");
294
10.8k
        buf = gs_alloc_bytes(sysmem, STDERR_BUF_SIZE, "stderr_open(buffer)");
295
10.8k
        if (s == 0 || buf == 0)
296
0
            return_error(gs_error_VMerror);
297
10.8k
        s_std_init(s, buf, STDERR_BUF_SIZE, &p, s_mode_write);
298
10.8k
        s->file = 0;
299
10.8k
        s->file_modes = s->modes;
300
10.8k
        s->file_offset = 0;   /* in case we switch to reading later */
301
10.8k
        s->file_limit = S_FILE_LIMIT_MAX;
302
10.8k
        s->save_close = s->procs.flush;
303
10.8k
        make_file(&ref_stderr, a_write | avm_system, s->write_id, s);
304
10.8k
        *ps = s;
305
10.8k
        return 1;
306
10.8k
    }
307
11.5k
    *ps = s;
308
11.5k
    return 0;
309
22.3k
}
310
311
/* This is the public routine for getting the stderr stream. */
312
int
313
zget_stderr(i_ctx_t *i_ctx_p, stream ** ps)
314
0
{
315
0
    stream *s;
316
0
    gx_io_device *iodev;
317
0
    int code;
318
319
0
    if (file_is_valid(s, &ref_stderr)) {
320
0
        *ps = s;
321
0
        return 0;
322
0
    }
323
0
    iodev = gs_findiodevice(imemory, (const byte *)"%stderr", 7);
324
0
    iodev->state = i_ctx_p;
325
0
    code = (*iodev->procs.open_device)(iodev, "w", ps, imemory_system);
326
0
    iodev->state = NULL;
327
0
    return min(code, 0);
328
0
}