Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
}