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