sync'ing up for 3.0alpha20 release
[tprouty/samba.git] / source / lib / xfile.c
1 /* 
2    Unix SMB/CIFS implementation.
3    stdio replacement
4    Copyright (C) Andrew Tridgell 2001
5    
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10    
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15    
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 /*
22   stdio is very convenient, but on some systems the file descriptor
23   in FILE* is 8 bits, so it fails when more than 255 files are open. 
24
25   XFILE replaces stdio. It is less efficient, but at least it works
26   when you have lots of files open
27
28   The main restriction on XFILE is that it doesn't support seeking,
29   and doesn't support O_RDWR. That keeps the code simple.
30 */
31
32 #include "includes.h"
33
34 #define XBUFSIZE BUFSIZ
35
36 static XFILE _x_stdin =  { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
37 static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
38 static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
39
40 XFILE *x_stdin = &_x_stdin;
41 XFILE *x_stdout = &_x_stdout;
42 XFILE *x_stderr = &_x_stderr;
43
44 #define X_FLAG_EOF 1
45 #define X_FLAG_ERROR 2
46 #define X_FLAG_EINVAL 3
47
48 /* simulate setvbuf() */
49 int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
50 {
51         x_fflush(f);
52         if (f->bufused) return -1;
53
54         /* on files being read full buffering is the only option */
55         if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
56                 mode = X_IOFBF;
57         }
58
59         /* destroy any earlier buffer */
60         SAFE_FREE(f->buf);
61         f->buf = 0;
62         f->bufsize = 0;
63         f->next = NULL;
64         f->bufused = 0;
65         f->buftype = mode;
66
67         if (f->buftype == X_IONBF) return 0;
68
69         /* if buffering then we need some size */
70         if (size == 0) size = XBUFSIZE;
71
72         f->bufsize = size;
73         f->bufused = 0;
74
75         return 0;
76 }
77
78 /* allocate the buffer */
79 static int x_allocate_buffer(XFILE *f)
80 {
81         if (f->buf) return 1;
82         if (f->bufsize == 0) return 0;
83         f->buf = malloc(f->bufsize);
84         if (!f->buf) return 0;
85         f->next = f->buf;
86         return 1;
87 }
88
89
90 /* this looks more like open() than fopen(), but that is quite deliberate.
91    I want programmers to *think* about O_EXCL, O_CREAT etc not just
92    get them magically added 
93 */
94 XFILE *x_fopen(const char *fname, int flags, mode_t mode)
95 {
96         XFILE *ret;
97
98         ret = (XFILE *)malloc(sizeof(XFILE));
99         if (!ret) return NULL;
100
101         memset(ret, 0, sizeof(XFILE));
102
103         if ((flags & O_ACCMODE) == O_RDWR) {
104                 /* we don't support RDWR in XFILE - use file 
105                    descriptors instead */
106                 errno = EINVAL;
107                 return NULL;
108         }
109
110         ret->open_flags = flags;
111
112         ret->fd = sys_open(fname, flags, mode);
113         if (ret->fd == -1) {
114                 SAFE_FREE(ret);
115                 return NULL;
116         }
117
118         x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
119         
120         return ret;
121 }
122
123 /* simulate fclose() */
124 int x_fclose(XFILE *f)
125 {
126         int ret;
127
128         /* make sure we flush any buffered data */
129         x_fflush(f);
130
131         ret = close(f->fd);
132         f->fd = -1;
133         if (f->buf) {
134                 /* make sure data can't leak into a later malloc */
135                 memset(f->buf, 0, f->bufsize);
136                 SAFE_FREE(f->buf);
137         }
138         SAFE_FREE(f);
139         return ret;
140 }
141
142 /* simulate fwrite() */
143 int x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
144 {
145         int ret, total=0;
146
147         /* we might be writing unbuffered */
148         if (f->buftype == X_IONBF || 
149             (!f->buf && !x_allocate_buffer(f))) {
150                 ret = write(f->fd, p, size*nmemb);
151                 if (ret == -1) return -1;
152                 return ret/size;
153         } 
154
155
156         while (total < size*nmemb) {
157                 int n = f->bufsize - f->bufused;
158                 n = MIN(n, (size*nmemb)-total);
159
160                 if (n == 0) {
161                         /* it's full, flush it */
162                         x_fflush(f);
163                         continue;
164                 }
165
166                 memcpy(f->buf + f->bufused, total+(const char *)p, n);
167                 f->bufused += n;
168                 total += n;
169         }
170
171         /* when line buffered we need to flush at the last linefeed. This can
172            flush a bit more than necessary, but that is harmless */
173         if (f->buftype == X_IOLBF && f->bufused) {
174                 int i;
175                 for (i=(size*nmemb)-1; i>=0; i--) {
176                         if (*(i+(const char *)p) == '\n') {
177                                 x_fflush(f);
178                                 break;
179                         }
180                 }
181         }
182
183         return total/size;
184 }
185
186 /* thank goodness for asprintf() */
187 int x_vfprintf(XFILE *f, const char *format, va_list ap)
188 {
189         char *p;
190         int len, ret;
191         va_list ap2;
192
193         VA_COPY(ap2, ap);
194
195         len = vasprintf(&p, format, ap2);
196         if (len <= 0) return len;
197         ret = x_fwrite(p, 1, len, f);
198         SAFE_FREE(p);
199         return ret;
200 }
201
202 int x_fprintf(XFILE *f, const char *format, ...)
203 {
204         va_list ap;
205         int ret;
206
207         va_start(ap, format);
208         ret = x_vfprintf(f, format, ap);
209         va_end(ap);
210         return ret;
211 }
212
213 /* at least fileno() is simple! */
214 int x_fileno(XFILE *f)
215 {
216         return f->fd;
217 }
218
219 /* simulate fflush() */
220 int x_fflush(XFILE *f)
221 {
222         int ret;
223
224         if (f->flags & X_FLAG_ERROR) return -1;
225
226         if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
227                 errno = EINVAL;
228                 return -1;
229         }
230
231         if (f->bufused == 0) return 0;
232
233         ret = write(f->fd, f->buf, f->bufused);
234         if (ret == -1) return -1;
235         
236         f->bufused -= ret;
237         if (f->bufused > 0) {
238                 f->flags |= X_FLAG_ERROR;
239                 memmove(f->buf, ret + (char *)f->buf, f->bufused);
240                 return -1;
241         }
242
243         return 0;
244 }
245
246 /* simulate setbuffer() */
247 void x_setbuffer(XFILE *f, char *buf, size_t size)
248 {
249         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
250 }
251
252 /* simulate setbuf() */
253 void x_setbuf(XFILE *f, char *buf)
254 {
255         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
256 }
257
258 /* simulate setlinebuf() */
259 void x_setlinebuf(XFILE *f)
260 {
261         x_setvbuf(f, NULL, X_IOLBF, 0);
262 }
263
264
265 /* simulate feof() */
266 int x_feof(XFILE *f)
267 {
268         if (f->flags & X_FLAG_EOF) return 1;
269         return 0;
270 }
271
272 /* simulate ferror() */
273 int x_ferror(XFILE *f)
274 {
275         if (f->flags & X_FLAG_ERROR) return 1;
276         return 0;
277 }
278
279 /* fill the read buffer */
280 static void x_fillbuf(XFILE *f)
281 {
282         int n;
283
284         if (f->bufused) return;
285
286         if (!f->buf && !x_allocate_buffer(f)) return;
287
288         n = read(f->fd, f->buf, f->bufsize);
289         if (n <= 0) return;
290         f->bufused = n;
291         f->next = f->buf;
292 }
293
294 /* simulate fgetc() */
295 int x_fgetc(XFILE *f)
296 {
297         int ret;
298
299         if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
300         
301         if (f->bufused == 0) x_fillbuf(f);
302
303         if (f->bufused == 0) {
304                 f->flags |= X_FLAG_EOF;
305                 return EOF;
306         }
307
308         ret = *(unsigned char *)(f->next);
309         f->next++;
310         f->bufused--;
311         return ret;
312 }
313
314 /* simulate fread */
315 size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
316 {
317         size_t total = 0;
318         while (total < size*nmemb) {
319                 int c = x_fgetc(f);
320                 if (c == EOF) break;
321                 (total+(char *)p)[0] = (char)c;
322                 total++;
323         }
324         return total/size;
325 }
326
327 /* simulate fgets() */
328 char *x_fgets(char *s, int size, XFILE *stream) 
329 {
330         char *s0 = s;
331         int l = size;
332         while (l>1) {
333                 int c = x_fgetc(stream);
334                 if (c == EOF) break;
335                 *s++ = (char)c;
336                 l--;
337                 if (c == '\n') break;
338         }
339         if (l==size || x_ferror(stream)) {
340                 return 0;
341         }
342         *s = 0;
343         return s0;
344 }
345
346 /* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
347  * set then an error is returned */
348 off_t x_tseek(XFILE *f, off_t offset, int whence)
349 {
350         if (f->flags & X_FLAG_ERROR)
351                 return -1;
352
353         /* only SEEK_SET and SEEK_END are supported */
354         /* SEEK_CUR needs internal offset counter */
355         if (whence != SEEK_SET && whence != SEEK_END) {
356                 f->flags |= X_FLAG_EINVAL;
357                 errno = EINVAL;
358                 return -1;
359         }
360
361         /* empty the buffer */
362         switch (f->open_flags & O_ACCMODE) {
363         case O_RDONLY:
364                 f->bufused = 0;
365                 break;
366         case O_WRONLY:
367                 if (x_fflush(f) != 0)
368                         return -1;
369                 break;
370         default:
371                 errno = EINVAL;
372                 return -1;
373         }
374
375         f->flags &= ~X_FLAG_EOF;
376         return (off_t)sys_lseek(f->fd, offset, whence);
377 }