/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 | } |