1 /* Copyright (C) 1994, 2000 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: sfxfd.c,v 1.10 2004/08/05 17:02:36 stefan Exp $ */
18 /* File stream implementation using direct OS calls */
19 /******
20 ****** NOTE: THIS FILE MAY NOT COMPILE ON NON-UNIX PLATFORMS, AND MAY
21 ****** REQUIRE EDITING ON SOME UNIX PLATFORMS.
22 ******/
23 #include "stdio_.h" /* includes std.h */
24 #include "errno_.h"
25 #include "memory_.h"
26 #include "unistd_.h" /* for read, write, fsync, lseek */
27
28 #include "gdebug.h"
29 #include "gpcheck.h"
30 #include "stream.h"
31 #include "strimpl.h"
32
33 /*
34 * This is an alternate implementation of file streams. It still uses
35 * FILE * in the interface, but it uses direct OS calls for I/O.
36 * It also includes workarounds for the nasty habit of System V Unix
37 * of breaking out of read and write operations with EINTR, EAGAIN,
38 * and/or EWOULDBLOCK "errors".
39 *
40 * The interface should be identical to that of sfxstdio.c. However, in
41 * order to allow both implementations to exist in the same executable, we
42 * optionally use different names for sread_file, swrite_file, and
43 * sappend_file, and omit sread_subfile (the public procedures).
44 * See sfxboth.c.
45 */
46 #ifdef KEEP_FILENO_API
47 /* Provide prototypes to avoid compiler warnings. */
48 void
49 sread_fileno(stream *, FILE *, byte *, uint),
50 swrite_fileno(stream *, FILE *, byte *, uint),
51 sappend_fileno(stream *, FILE *, byte *, uint);
52 #else
53 # define sread_fileno sread_file
54 # define swrite_fileno swrite_file
55 # define sappend_fileno sappend_file
56 #endif
57
58 /* Forward references for file stream procedures */
59 private int
60 s_fileno_available(stream *, long *),
61 s_fileno_read_seek(stream *, long),
62 s_fileno_read_close(stream *),
63 s_fileno_read_process(stream_state *, stream_cursor_read *,
64 stream_cursor_write *, bool);
65 private int
66 s_fileno_write_seek(stream *, long),
67 s_fileno_write_flush(stream *),
68 s_fileno_write_close(stream *),
69 s_fileno_write_process(stream_state *, stream_cursor_read *,
70 stream_cursor_write *, bool);
71 private int
72 s_fileno_switch(stream *, bool);
73
74 /* Get the file descriptor number of a stream. */
75 inline private int
sfileno(const stream * s)76 sfileno(const stream *s)
77 {
78 return fileno(s->file);
79 }
80
81 /* Get the current position of a file descriptor. */
82 inline private long
ltell(int fd)83 ltell(int fd)
84 {
85 return lseek(fd, 0L, SEEK_CUR);
86 }
87
88 /* Define the System V interrupts that require retrying a call. */
89 private bool
errno_is_retry(int errn)90 errno_is_retry(int errn)
91 {
92 switch (errn) {
93 #ifdef EINTR
94 case EINTR: return true;
95 #endif
96 #if defined(EAGAIN) && (!defined(EINTR) || EAGAIN != EINTR)
97 case EAGAIN: return true;
98 #endif
99 #if defined(EWOULDBLOCK) && (!defined(EINTR) || EWOULDBLOCK != EINTR) && (!defined(EAGAIN) || EWOULDBLOCK != EAGAIN)
100 case EWOULDBLOCK: return true;
101 #endif
102 default: return false;
103 }
104 }
105
106 /* ------ File reading ------ */
107
108 /* Initialize a stream for reading an OS file. */
109 void
sread_fileno(register stream * s,FILE * file,byte * buf,uint len)110 sread_fileno(register stream * s, FILE * file, byte * buf, uint len)
111 {
112 static const stream_procs p = {
113 s_fileno_available, s_fileno_read_seek, s_std_read_reset,
114 s_std_read_flush, s_fileno_read_close, s_fileno_read_process,
115 s_fileno_switch
116 };
117 /*
118 * There is no really portable way to test seekability,
119 * but this should work on most systems.
120 */
121 int fd = fileno(file);
122 long curpos = ltell(fd);
123 bool seekable = (curpos != -1L && lseek(fd, curpos, SEEK_SET) != -1L);
124
125 s_std_init(s, buf, len, &p,
126 (seekable ? s_mode_read + s_mode_seek : s_mode_read));
127 if_debug2('s', "[s]read file=0x%lx, fd=%d\n", (ulong) file,
128 fileno(file));
129 s->file = file;
130 s->file_modes = s->modes;
131 s->file_offset = 0;
132 s->file_limit = max_long;
133 }
134
135 /* Confine reading to a subfile. This is primarily for reusable streams. */
136 /*
137 * We omit this procedure if we are also include sfxstdio.c, which has an
138 * identical definition.
139 */
140 #ifndef KEEP_FILENO_API
141 int
sread_subfile(stream * s,long start,long length)142 sread_subfile(stream *s, long start, long length)
143 {
144 if (s->file == 0 || s->modes != s_mode_read + s_mode_seek ||
145 s->file_offset != 0 || s->file_limit != max_long ||
146 ((s->position < start || s->position > start + length) &&
147 sseek(s, start) < 0)
148 )
149 return ERRC;
150 s->position -= start;
151 s->file_offset = start;
152 s->file_limit = length;
153 return 0;
154 }
155 #endif
156
157 /* Procedures for reading from a file */
158 private int
s_fileno_available(register stream * s,long * pl)159 s_fileno_available(register stream * s, long *pl)
160 {
161 long max_avail = s->file_limit - stell(s);
162 long buf_avail = sbufavailable(s);
163 int fd = sfileno(s);
164
165 *pl = min(max_avail, buf_avail);
166 if (sseekable(s)) {
167 long pos, end;
168
169 pos = ltell(fd);
170 if (pos < 0)
171 return ERRC;
172 end = lseek(fd, 0L, SEEK_END);
173 if (lseek(fd, pos, SEEK_SET) < 0 || end < 0)
174 return ERRC;
175 buf_avail += end - pos;
176 *pl = min(max_avail, buf_avail);
177 if (*pl == 0)
178 *pl = -1; /* EOF */
179 } else {
180 if (*pl == 0)
181 *pl = -1; /* EOF */
182 }
183 return 0;
184 }
185 private int
s_fileno_read_seek(register stream * s,long pos)186 s_fileno_read_seek(register stream * s, long pos)
187 {
188 uint end = s->srlimit - s->cbuf + 1;
189 long offset = pos - s->position;
190
191 if (offset >= 0 && offset <= end) { /* Staying within the same buffer */
192 s->srptr = s->cbuf + offset - 1;
193 return 0;
194 }
195 if (pos < 0 || pos > s->file_limit ||
196 lseek(sfileno(s), s->file_offset + pos, SEEK_SET) < 0
197 )
198 return ERRC;
199 s->srptr = s->srlimit = s->cbuf - 1;
200 s->end_status = 0;
201 s->position = pos;
202 return 0;
203 }
204 private int
s_fileno_read_close(stream * s)205 s_fileno_read_close(stream * s)
206 {
207 FILE *file = s->file;
208
209 if (file != 0) {
210 s->file = 0;
211 return (fclose(file) ? ERRC : 0);
212 }
213 return 0;
214 }
215
216 /* Process a buffer for a file reading stream. */
217 /* This is the first stream in the pipeline, so pr is irrelevant. */
218 private int
s_fileno_read_process(stream_state * st,stream_cursor_read * ignore_pr,stream_cursor_write * pw,bool last)219 s_fileno_read_process(stream_state * st, stream_cursor_read * ignore_pr,
220 stream_cursor_write * pw, bool last)
221 {
222 stream *s = (stream *)st; /* no separate state */
223 int fd = sfileno(s);
224 uint max_count;
225 int nread, status;
226
227 again:
228 max_count = pw->limit - pw->ptr;
229 status = 1;
230 if (s->file_limit < max_long) {
231 long limit_count = s->file_offset + s->file_limit - ltell(fd);
232
233 if (max_count > limit_count)
234 max_count = limit_count, status = EOFC;
235 }
236 /*
237 * In the Mac MetroWerks compiler, the prototype for read incorrectly
238 * declares the second argument of read as char * rather than void *.
239 * Work around this here.
240 */
241 nread = read(fd, (void *)(pw->ptr + 1), max_count);
242 if (nread > 0)
243 pw->ptr += nread;
244 else if (nread == 0)
245 status = EOFC;
246 else if (errno_is_retry(errno)) /* Handle System V interrupts */
247 goto again;
248 else
249 status = ERRC;
250 process_interrupts(s->memory);
251 return status;
252 }
253
254 /* ------ File writing ------ */
255
256 /* Initialize a stream for writing an OS file. */
257 void
swrite_fileno(register stream * s,FILE * file,byte * buf,uint len)258 swrite_fileno(register stream * s, FILE * file, byte * buf, uint len)
259 {
260 static const stream_procs p = {
261 s_std_noavailable, s_fileno_write_seek, s_std_write_reset,
262 s_fileno_write_flush, s_fileno_write_close, s_fileno_write_process,
263 s_fileno_switch
264 };
265
266 s_std_init(s, buf, len, &p,
267 (file == stdout ? s_mode_write : s_mode_write + s_mode_seek));
268 if_debug2('s', "[s]write file=0x%lx, fd=%d\n", (ulong) file,
269 fileno(file));
270 s->file = file;
271 s->file_modes = s->modes;
272 s->file_offset = 0; /* in case we switch to reading later */
273 s->file_limit = max_long; /* ibid. */
274 }
275 /* Initialize for appending to an OS file. */
276 void
sappend_fileno(register stream * s,FILE * file,byte * buf,uint len)277 sappend_fileno(register stream * s, FILE * file, byte * buf, uint len)
278 {
279 swrite_fileno(s, file, buf, len);
280 s->modes = s_mode_write + s_mode_append; /* no seek */
281 s->file_modes = s->modes;
282 s->position = lseek(fileno(file), 0L, SEEK_END);
283 }
284 /* Procedures for writing on a file */
285 private int
s_fileno_write_seek(stream * s,long pos)286 s_fileno_write_seek(stream * s, long pos)
287 {
288 /* We must flush the buffer to reposition. */
289 int code = sflush(s);
290
291 if (code < 0)
292 return code;
293 if (lseek(sfileno(s), pos, SEEK_SET) < 0)
294 return ERRC;
295 s->position = pos;
296 return 0;
297 }
298 private int
s_fileno_write_flush(register stream * s)299 s_fileno_write_flush(register stream * s)
300 {
301 int result = s_process_write_buf(s, false);
302
303 discard(fsync(sfileno(s)));
304 return result;
305 }
306 private int
s_fileno_write_close(register stream * s)307 s_fileno_write_close(register stream * s)
308 {
309 s_process_write_buf(s, true);
310 return s_fileno_read_close(s);
311 }
312
313 /* Process a buffer for a file writing stream. */
314 /* This is the last stream in the pipeline, so pw is irrelevant. */
315 private int
s_fileno_write_process(stream_state * st,stream_cursor_read * pr,stream_cursor_write * ignore_pw,bool last)316 s_fileno_write_process(stream_state * st, stream_cursor_read * pr,
317 stream_cursor_write * ignore_pw, bool last)
318 {
319 int nwrite, status;
320 uint count;
321
322 again:
323 count = pr->limit - pr->ptr;
324 /* Some versions of the DEC C library on AXP architectures */
325 /* give an error on write if the count is zero! */
326 if (count == 0) {
327 process_interrupts((stream*)st->memory);
328 return 0;
329 }
330 /* See above regarding the Mac MetroWorks compiler. */
331 nwrite = write(sfileno((stream *)st), (const void *)(pr->ptr + 1), count);
332 if (nwrite >= 0) {
333 pr->ptr += nwrite;
334 status = 0;
335 } else if (errno_is_retry(errno)) /* Handle System V interrupts */
336 goto again;
337 else
338 status = ERRC;
339 process_interrupts((stream *)st->memory);
340 return status;
341 }
342
343 /* ------ File switching ------ */
344
345 /* Switch a file stream to reading or writing. */
346 private int
s_fileno_switch(stream * s,bool writing)347 s_fileno_switch(stream * s, bool writing)
348 {
349 uint modes = s->file_modes;
350 int fd = sfileno(s);
351 long pos;
352
353 if (writing) {
354 if (!(s->file_modes & s_mode_write))
355 return ERRC;
356 pos = stell(s);
357 if_debug2('s', "[s]switch 0x%lx to write at %ld\n",
358 (ulong) s, pos);
359 lseek(fd, pos, SEEK_SET); /* pacify OS */
360 if (modes & s_mode_append) {
361 sappend_file(s, s->file, s->cbuf, s->cbsize); /* sets position */
362 } else {
363 swrite_file(s, s->file, s->cbuf, s->cbsize);
364 s->position = pos;
365 }
366 s->modes = modes;
367 } else {
368 if (!(s->file_modes & s_mode_read))
369 return ERRC;
370 pos = stell(s);
371 if_debug2('s', "[s]switch 0x%lx to read at %ld\n",
372 (ulong) s, pos);
373 if (sflush(s) < 0)
374 return ERRC;
375 lseek(fd, 0L, SEEK_CUR); /* pacify OS */
376 sread_file(s, s->file, s->cbuf, s->cbsize);
377 s->modes |= modes & s_mode_append; /* don't lose append info */
378 s->position = pos;
379 }
380 s->file_modes = modes;
381 return 0;
382 }
383