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