Coverage Report

Created: 2025-06-24 07:01

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