/src/ghostpdl/base/sfxstdio.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 | | /* File stream implementation using stdio */ |
18 | | #include "stdio_.h" /* includes std.h */ |
19 | | #include "memory_.h" |
20 | | #include "unistd_.h" |
21 | | #include "gsmemory.h" |
22 | | #include "gdebug.h" |
23 | | #include "gpcheck.h" |
24 | | #include "gp.h" |
25 | | #include "gserrors.h" |
26 | | #include "stream.h" |
27 | | #include "strimpl.h" |
28 | | |
29 | | /* Forward references for file stream procedures */ |
30 | | static int |
31 | | s_file_available(stream *, gs_offset_t *), |
32 | | s_file_read_seek(stream *, gs_offset_t), |
33 | | s_file_read_close(stream *), |
34 | | s_file_read_process(stream_state *, stream_cursor_read *, |
35 | | stream_cursor_write *, bool); |
36 | | static int |
37 | | s_file_write_seek(stream *, gs_offset_t), |
38 | | s_file_write_flush(stream *), |
39 | | s_file_write_close(stream *), |
40 | | s_file_write_process(stream_state *, stream_cursor_read *, |
41 | | stream_cursor_write *, bool); |
42 | | static int |
43 | | s_file_switch(stream *, bool); |
44 | | |
45 | | /* ------ File reading ------ */ |
46 | | |
47 | | /* Initialize a stream for reading an OS file. */ |
48 | | void |
49 | | sread_file(register stream * s, gp_file * file, byte * buf, uint len) |
50 | 5.12k | { |
51 | 5.12k | static const stream_procs p = { |
52 | 5.12k | s_file_available, s_file_read_seek, s_std_read_reset, |
53 | 5.12k | s_std_read_flush, s_file_read_close, s_file_read_process, |
54 | 5.12k | s_file_switch |
55 | 5.12k | }; |
56 | | /* |
57 | | * There is no really portable way to test seekability, but this should |
58 | | * work on most systems. Note that if our probe sets the ferror bit for |
59 | | * the stream, we have to clear it again to avoid trouble later. |
60 | | */ |
61 | 5.12k | int had_error = gp_ferror(file); |
62 | 5.12k | gs_offset_t curpos = gp_ftell(file); |
63 | 5.12k | bool seekable = (curpos != -1L && gp_fseek(file, curpos, SEEK_SET) == 0); |
64 | | |
65 | 5.12k | if (!had_error) |
66 | 5.12k | gp_clearerr(file); |
67 | 5.12k | s_std_init(s, buf, len, &p, |
68 | 5.12k | (seekable ? s_mode_read + s_mode_seek : s_mode_read)); |
69 | 5.12k | if_debug1m('s', s->memory, "[s]read file="PRI_INTPTR"\n", (intptr_t)file); |
70 | 5.12k | s->file = file; |
71 | 5.12k | s->file_modes = s->modes; |
72 | 5.12k | s->file_offset = 0; |
73 | 5.12k | s->file_limit = (sizeof(gs_offset_t) > 4 ? max_int64_t : max_long); |
74 | 5.12k | } |
75 | | |
76 | | /* Confine reading to a subfile. This is primarily for reusable streams. */ |
77 | | int |
78 | | sread_subfile(stream *s, gs_offset_t start, gs_offset_t length) |
79 | 0 | { |
80 | 0 | if (s->file == 0 || s->modes != s_mode_read + s_mode_seek || |
81 | 0 | s->file_offset != 0 || |
82 | 0 | s->file_limit != S_FILE_LIMIT_MAX || |
83 | 0 | ((s->position < start || s->position > start + length) && sseek(s, start) < 0) |
84 | 0 | ) |
85 | 0 | return ERRC; |
86 | 0 | s->position -= start; |
87 | 0 | s->file_offset = start; |
88 | 0 | s->file_limit = length; |
89 | 0 | return 0; |
90 | 0 | } |
91 | | |
92 | | /* Procedures for reading from a file */ |
93 | | static int |
94 | | s_file_available(register stream * s, gs_offset_t *pl) |
95 | 10.2k | { |
96 | 10.2k | gs_offset_t max_avail = s->file_limit - stell(s); |
97 | 10.2k | gs_offset_t buf_avail = sbufavailable(s); |
98 | | |
99 | 10.2k | *pl = min(max_avail, buf_avail); |
100 | 10.2k | if (sseekable(s)) { |
101 | 10.2k | gs_offset_t pos, end; |
102 | | |
103 | 10.2k | pos = gp_ftell(s->file); |
104 | 10.2k | if (gp_fseek(s->file, 0, SEEK_END)) |
105 | 0 | return ERRC; |
106 | 10.2k | end = gp_ftell(s->file); |
107 | 10.2k | if (gp_fseek(s->file, pos, SEEK_SET)) |
108 | 0 | return ERRC; |
109 | 10.2k | buf_avail += end - pos; |
110 | 10.2k | *pl = min(max_avail, buf_avail); |
111 | 10.2k | if (*pl == 0) |
112 | 1.47k | *pl = -1; /* EOF */ |
113 | 10.2k | } else { |
114 | | /* s->end_status == EOFC may indicate the stream is disabled |
115 | | * or that the underlying gp_file * has reached EOF. |
116 | | */ |
117 | 0 | if (*pl == 0 && (s->end_status == EOFC || gp_feof(s->file))) |
118 | 0 | *pl = -1; /* EOF */ |
119 | 0 | } |
120 | 10.2k | return 0; |
121 | 10.2k | } |
122 | | static int |
123 | | s_file_read_seek(register stream * s, gs_offset_t pos) |
124 | 711k | { |
125 | 711k | gs_offset_t end = s->cursor.r.limit - s->cbuf + 1; |
126 | 711k | gs_offset_t offset = pos - s->position; |
127 | | |
128 | 711k | if (offset >= 0 && offset <= end) { /* Staying within the same buffer */ |
129 | 302k | s->cursor.r.ptr = s->cbuf + offset - 1; |
130 | 302k | return 0; |
131 | 302k | } |
132 | 408k | if (pos < 0 || pos > s->file_limit || s->file == NULL || |
133 | 408k | gp_fseek(s->file, s->file_offset + pos, SEEK_SET) != 0 |
134 | 408k | ) |
135 | 1.47k | return ERRC; |
136 | 406k | s->cursor.r.ptr = s->cursor.r.limit = s->cbuf - 1; |
137 | 406k | s->end_status = 0; |
138 | 406k | s->position = pos; |
139 | 406k | return 0; |
140 | 408k | } |
141 | | static int |
142 | | s_file_read_close(stream * s) |
143 | 5.12k | { |
144 | 5.12k | gp_file *file = s->file; |
145 | | |
146 | 5.12k | if (file != 0) { |
147 | 5.12k | s->file = 0; |
148 | 5.12k | return (gp_fclose(file) ? ERRC : 0); |
149 | 5.12k | } |
150 | 0 | return 0; |
151 | 5.12k | } |
152 | | |
153 | | /* |
154 | | * Process a buffer for a file reading stream. |
155 | | * This is the first stream in the pipeline, so pr is irrelevant. |
156 | | */ |
157 | | static int |
158 | | s_file_read_process(stream_state * st, stream_cursor_read * ignore_pr, |
159 | | stream_cursor_write * pw, bool last) |
160 | 618k | { |
161 | 618k | stream *s = (stream *)st; /* no separate state */ |
162 | 618k | gp_file *file = s->file; |
163 | 618k | gs_offset_t max_count = pw->limit - pw->ptr; |
164 | 618k | int status = 1; |
165 | 618k | int count; |
166 | | |
167 | 618k | if (s->file_limit < S_FILE_LIMIT_MAX) { |
168 | 0 | gs_offset_t limit_count = s->file_offset + s->file_limit - gp_ftell(file); |
169 | |
|
170 | 0 | if (max_count > limit_count) |
171 | 0 | max_count = limit_count, status = EOFC; |
172 | 0 | } |
173 | 618k | count = gp_fread(pw->ptr + 1, 1, max_count, file); |
174 | 618k | if (count < 0) |
175 | 0 | count = 0; |
176 | 618k | pw->ptr += count; |
177 | 618k | process_interrupts(s->memory); |
178 | 618k | return (gp_ferror(file) ? ERRC : gp_feof(file) ? EOFC : status); |
179 | 618k | } |
180 | | |
181 | | /* ------ File writing ------ */ |
182 | | |
183 | | /* Initialize a stream for writing an OS file. */ |
184 | | void |
185 | | swrite_file(register stream * s, gp_file * file, byte * buf, uint len) |
186 | 14.2k | { |
187 | 14.2k | static const stream_procs p = { |
188 | 14.2k | s_std_noavailable, s_file_write_seek, s_std_write_reset, |
189 | 14.2k | s_file_write_flush, s_file_write_close, s_file_write_process, |
190 | 14.2k | s_file_switch |
191 | 14.2k | }; |
192 | | |
193 | 14.2k | s_std_init(s, buf, len, &p, |
194 | 14.2k | (gp_get_file(file) == stdout ? s_mode_write : s_mode_write + s_mode_seek)); |
195 | 14.2k | if_debug1m('s', s->memory, "[s]write file="PRI_INTPTR"\n", (intptr_t) file); |
196 | 14.2k | s->file = file; |
197 | 14.2k | s->file_modes = s->modes; |
198 | 14.2k | s->file_offset = 0; /* in case we switch to reading later */ |
199 | 14.2k | s->file_limit = S_FILE_LIMIT_MAX; |
200 | 14.2k | } |
201 | | /* Initialize for appending to an OS file. */ |
202 | | int |
203 | | sappend_file(register stream * s, gp_file * file, byte * buf, uint len) |
204 | 0 | { |
205 | 0 | swrite_file(s, file, buf, len); |
206 | 0 | s->modes = s_mode_write + s_mode_append; /* no seek */ |
207 | 0 | s->file_modes = s->modes; |
208 | 0 | if (gp_fseek(file, 0L, SEEK_END) != 0) |
209 | 0 | return ERRC; |
210 | 0 | s->position = gp_ftell(file); |
211 | 0 | return 0; |
212 | 0 | } |
213 | | /* Procedures for writing on a file */ |
214 | | static int |
215 | | s_file_write_seek(stream * s, gs_offset_t pos) |
216 | 5.12k | { |
217 | | /* We must flush the buffer to reposition. */ |
218 | 5.12k | int code = sflush(s); |
219 | | |
220 | 5.12k | if (code < 0) |
221 | 0 | return code; |
222 | 5.12k | if (gp_fseek(s->file, pos, SEEK_SET) != 0) |
223 | 0 | return ERRC; |
224 | 5.12k | s->position = pos; |
225 | 5.12k | return 0; |
226 | 5.12k | } |
227 | | static int |
228 | | s_file_write_flush(register stream * s) |
229 | 19.4k | { |
230 | 19.4k | int result = s_process_write_buf(s, false); |
231 | | |
232 | 19.4k | gp_fflush(s->file); |
233 | 19.4k | return result; |
234 | 19.4k | } |
235 | | static int |
236 | | s_file_write_close(register stream * s) |
237 | 0 | { |
238 | 0 | s_process_write_buf(s, true); |
239 | 0 | return s_file_read_close(s); |
240 | 0 | } |
241 | | |
242 | | /* |
243 | | * Process a buffer for a file writing stream. |
244 | | * This is the last stream in the pipeline, so pw is irrelevant. |
245 | | */ |
246 | | static int |
247 | | s_file_write_process(stream_state * st, stream_cursor_read * pr, |
248 | | stream_cursor_write * ignore_pw, bool last) |
249 | 3.23M | { |
250 | 3.23M | uint count = pr->limit - pr->ptr; |
251 | | |
252 | | /* |
253 | | * The DEC C library on AXP architectures gives an error on |
254 | | * fwrite if the count is zero! |
255 | | */ |
256 | 3.23M | if (count != 0) { |
257 | 3.23M | gp_file *file = ((stream *) st)->file; |
258 | 3.23M | int written = gp_fwrite(pr->ptr + 1, 1, count, file); |
259 | | |
260 | 3.23M | if (written < 0) |
261 | 0 | written = 0; |
262 | 3.23M | pr->ptr += written; |
263 | 3.23M | process_interrupts(st->memory); |
264 | 3.23M | return (gp_ferror(file) ? ERRC : 0); |
265 | 3.23M | } else { |
266 | 5.12k | process_interrupts(st->memory); |
267 | 5.12k | return 0; |
268 | 5.12k | } |
269 | 3.23M | } |
270 | | |
271 | | /* ------ File switching ------ */ |
272 | | |
273 | | /* Switch a file stream to reading or writing. */ |
274 | | static int |
275 | | s_file_switch(stream * s, bool writing) |
276 | 5.12k | { |
277 | 5.12k | uint modes = s->file_modes; |
278 | 5.12k | gp_file *file = s->file; |
279 | 5.12k | gs_offset_t pos; |
280 | | |
281 | 5.12k | if (writing) { |
282 | 0 | if (!(s->file_modes & s_mode_write)) |
283 | 0 | return ERRC; |
284 | 0 | pos = stell(s); |
285 | 0 | if_debug2m('s', s->memory, "[s]switch 0x%"PRIx64" to write at %"PRId64"\n", |
286 | 0 | (uint64_t) s, (int64_t)pos); |
287 | 0 | if (gp_fseek(file, pos, SEEK_SET) != 0) |
288 | 0 | return ERRC; |
289 | 0 | if (modes & s_mode_append) { |
290 | 0 | if (sappend_file(s, file, s->cbuf, s->cbsize)!= 0) /* sets position */ |
291 | 0 | return ERRC; |
292 | 0 | } else { |
293 | 0 | swrite_file(s, file, s->cbuf, s->cbsize); |
294 | 0 | s->position = pos; |
295 | 0 | } |
296 | 0 | s->modes = modes; |
297 | 5.12k | } else { |
298 | 5.12k | if (!(s->file_modes & s_mode_read)) |
299 | 0 | return ERRC; |
300 | 5.12k | pos = stell(s); |
301 | 5.12k | if_debug2m('s', s->memory, "[s]switch 0x%"PRIu64" to read at %"PRId64"\n", |
302 | 5.12k | (uint64_t) s, (int64_t)pos); |
303 | 5.12k | if (sflush(s) < 0) |
304 | 0 | return ERRC; |
305 | 5.12k | if (gp_fseek(file, 0L, SEEK_CUR) != 0) |
306 | 0 | return ERRC; |
307 | 5.12k | sread_file(s, file, s->cbuf, s->cbsize); |
308 | 5.12k | s->modes |= modes & s_mode_append; /* don't lose append info */ |
309 | 5.12k | s->position = pos; |
310 | 5.12k | } |
311 | 5.12k | s->file_modes = modes; |
312 | 5.12k | return 0; |
313 | 5.12k | } |