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