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