Coverage Report

Created: 2025-06-10 07:15

/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
12.7k
#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
12.7k
#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
12.7k
#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
25.4k
{
82
25.4k
    mem->gs_lib_ctx->core->stdin_is_interactive = true;
83
25.4k
    return 0;
84
25.4k
}
85
86
static void
87
stdin_finit(gx_io_device * iodev, gs_memory_t * mem)
88
14.8k
{
89
14.8k
    mem->gs_lib_ctx->core->stdin_is_interactive = false;
90
14.8k
    return;
91
14.8k
}
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
119k
{
99
119k
    int wcount = (int)(pw->limit - pw->ptr);
100
119k
    int count;
101
119k
    gs_memory_t *mem = st->memory;
102
119k
    gs_lib_ctx_core_t *core = mem->gs_lib_ctx->core;
103
104
119k
    if (wcount <= 0)
105
0
        return 0;
106
107
    /* do the callout */
108
119k
    if (core->stdin_fn)
109
119k
        count = (*core->stdin_fn)
110
119k
            (core->std_caller_handle, (char *)pw->ptr + 1,
111
119k
             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
119k
    pw->ptr += (count < 0) ? 0 : count;
118
119k
    return ((count < 0) ? ERRC : (count == 0) ? EOFC : count);
119
119k
}
120
121
static int
122
stdin_open(gx_io_device * iodev, const char *access, stream ** ps,
123
           gs_memory_t * mem)
124
33.2k
{
125
33.2k
    i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
126
33.2k
    stream *s;
127
128
33.2k
    if (!streq1(access, 'r'))
129
0
        return_error(gs_error_invalidfileaccess);
130
33.2k
    if (file_is_invalid(s, &ref_stdin)) {
131
        /****** stdin SHOULD NOT LINE-BUFFER ******/
132
12.7k
        gs_memory_t *sysmem = imemory_system;
133
12.7k
        byte *buf;
134
12.7k
        static const stream_procs p = {
135
12.7k
            s_std_noavailable, s_std_noseek, s_std_read_reset,
136
12.7k
            s_std_read_flush, file_close_file, s_stdin_read_process
137
12.7k
        };
138
139
12.7k
        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
12.7k
        buf = gs_alloc_bytes(sysmem, STDIN_BUF_SIZE, "stdin_open(buffer)");
146
12.7k
        if (s == 0 || buf == 0)
147
0
            return_error(gs_error_VMerror);
148
149
12.7k
        s_std_init(s, buf, STDIN_BUF_SIZE, &p, s_mode_read);
150
12.7k
        s->file = 0;
151
12.7k
        s->file_modes = s->modes;
152
12.7k
        s->file_offset = 0;
153
12.7k
        s->file_limit = S_FILE_LIMIT_MAX;
154
12.7k
        s->save_close = s_std_null;
155
12.7k
        make_file(&ref_stdin, a_readonly | avm_system, s->read_id, s);
156
12.7k
        *ps = s;
157
12.7k
        return 1;
158
12.7k
    }
159
20.4k
    *ps = s;
160
20.4k
    return 0;
161
33.2k
}
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
8
{
185
8
    return (s_is_valid(s) && s->procs.process == s_stdin_read_process);
186
8
}
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
805k
{
193
805k
    uint count = pr->limit - pr->ptr;
194
805k
    int written;
195
196
805k
    if (count == 0)
197
31.8k
        return 0;
198
773k
    written = outwrite(st->memory, (const char *)pr->ptr + 1, count);
199
773k
    if (written != count)
200
0
        return ERRC;
201
773k
    pr->ptr += written;
202
773k
    return 0;
203
773k
}
204
205
static int
206
stdout_open(gx_io_device * iodev, const char *access, stream ** ps,
207
            gs_memory_t * mem)
208
3.51M
{
209
3.51M
    i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
210
3.51M
    stream *s;
211
212
3.51M
    if (!streq1(access, 'w'))
213
0
        return_error(gs_error_invalidfileaccess);
214
3.51M
    if (file_is_invalid(s, &ref_stdout)) {
215
12.7k
        gs_memory_t *sysmem = imemory_system;
216
12.7k
        byte *buf;
217
12.7k
        static const stream_procs p = {
218
12.7k
            s_std_noavailable, s_std_noseek, s_std_write_reset,
219
12.7k
            s_std_write_flush, file_close_file, s_stdout_write_process
220
12.7k
        };
221
222
12.7k
        s = file_alloc_stream(sysmem, "stdout_open(stream)");
223
12.7k
        buf = gs_alloc_bytes(sysmem, STDOUT_BUF_SIZE, "stdout_open(buffer)");
224
12.7k
        if (s == 0 || buf == 0)
225
0
            return_error(gs_error_VMerror);
226
12.7k
        s_std_init(s, buf, STDOUT_BUF_SIZE, &p, s_mode_write);
227
12.7k
        s->file = 0;
228
12.7k
        s->file_modes = s->modes;
229
12.7k
        s->file_offset = 0;   /* in case we switch to reading later */
230
12.7k
        s->file_limit = S_FILE_LIMIT_MAX;
231
12.7k
        s->save_close = s->procs.flush;
232
12.7k
        make_file(&ref_stdout, a_write | avm_system, s->write_id, s);
233
12.7k
        *ps = s;
234
12.7k
        return 1;
235
12.7k
    }
236
3.50M
    *ps = s;
237
3.50M
    return 0;
238
3.51M
}
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
3.54M
{
244
3.54M
    stream *s;
245
3.54M
    gx_io_device *iodev;
246
3.54M
    int code;
247
248
3.54M
    if (file_is_valid(s, &ref_stdout)) {
249
3.54M
        *ps = s;
250
3.54M
        return 0;
251
3.54M
    }
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
3.54M
}
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
13.7k
{
264
13.7k
    uint count = pr->limit - pr->ptr;
265
13.7k
    int written;
266
267
13.7k
    if (count == 0)
268
12.7k
        return 0;
269
1.04k
    written = errwrite(st->memory, (const char *)(pr->ptr + 1), count);
270
1.04k
    if (written < count)
271
0
        return ERRC;
272
1.04k
    pr->ptr += written;
273
1.04k
    return 0;
274
1.04k
}
275
276
static int
277
stderr_open(gx_io_device * iodev, const char *access, stream ** ps,
278
            gs_memory_t * mem)
279
26.5k
{
280
26.5k
    i_ctx_t *i_ctx_p = (i_ctx_t *)iodev->state; /* see above */
281
26.5k
    stream *s;
282
283
26.5k
    if (!streq1(access, 'w'))
284
0
        return_error(gs_error_invalidfileaccess);
285
26.5k
    if (file_is_invalid(s, &ref_stderr)) {
286
12.7k
        gs_memory_t *sysmem = imemory_system;
287
12.7k
        byte *buf;
288
12.7k
        static const stream_procs p = {
289
12.7k
            s_std_noavailable, s_std_noseek, s_std_write_reset,
290
12.7k
            s_std_write_flush, file_close_file, s_stderr_write_process
291
12.7k
        };
292
293
12.7k
        s = file_alloc_stream(sysmem, "stderr_open(stream)");
294
12.7k
        buf = gs_alloc_bytes(sysmem, STDERR_BUF_SIZE, "stderr_open(buffer)");
295
12.7k
        if (s == 0 || buf == 0)
296
0
            return_error(gs_error_VMerror);
297
12.7k
        s_std_init(s, buf, STDERR_BUF_SIZE, &p, s_mode_write);
298
12.7k
        s->file = 0;
299
12.7k
        s->file_modes = s->modes;
300
12.7k
        s->file_offset = 0;   /* in case we switch to reading later */
301
12.7k
        s->file_limit = S_FILE_LIMIT_MAX;
302
12.7k
        s->save_close = s->procs.flush;
303
12.7k
        make_file(&ref_stderr, a_write | avm_system, s->write_id, s);
304
12.7k
        *ps = s;
305
12.7k
        return 1;
306
12.7k
    }
307
13.7k
    *ps = s;
308
13.7k
    return 0;
309
26.5k
}
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
}