r9705: r9685@blu: tridge | 2005-08-27 19:43:44 +1000
[bbaumbach/samba-autobuild/.git] / source4 / 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 #include "system/filesys.h"
34
35 #define XBUFSIZE BUFSIZ
36
37 static XFILE _x_stdin =  { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
38 static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
39 static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
40
41 XFILE *x_stdin = &_x_stdin;
42 XFILE *x_stdout = &_x_stdout;
43 XFILE *x_stderr = &_x_stderr;
44
45 #define X_FLAG_EOF 1
46 #define X_FLAG_ERROR 2
47 #define X_FLAG_EINVAL 3
48
49 /* simulate setvbuf() */
50 int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
51 {
52         x_fflush(f);
53         if (f->bufused) return -1;
54
55         /* on files being read full buffering is the only option */
56         if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
57                 mode = X_IOFBF;
58         }
59
60         /* destroy any earlier buffer */
61         SAFE_FREE(f->buf);
62         f->buf = 0;
63         f->bufsize = 0;
64         f->next = NULL;
65         f->bufused = 0;
66         f->buftype = mode;
67
68         if (f->buftype == X_IONBF) return 0;
69
70         /* if buffering then we need some size */
71         if (size == 0) size = XBUFSIZE;
72
73         f->bufsize = size;
74         f->bufused = 0;
75
76         return 0;
77 }
78
79 /* allocate the buffer */
80 static int x_allocate_buffer(XFILE *f)
81 {
82         if (f->buf) return 1;
83         if (f->bufsize == 0) return 0;
84         f->buf = malloc(f->bufsize);
85         if (!f->buf) return 0;
86         f->next = f->buf;
87         return 1;
88 }
89
90
91 /* this looks more like open() than fopen(), but that is quite deliberate.
92    I want programmers to *think* about O_EXCL, O_CREAT etc not just
93    get them magically added 
94 */
95 XFILE *x_fopen(const char *fname, int flags, mode_t mode)
96 {
97         XFILE *ret;
98
99         ret = malloc_p(XFILE);
100         if (!ret) return NULL;
101
102         memset(ret, 0, sizeof(XFILE));
103
104         if ((flags & O_ACCMODE) == O_RDWR) {
105                 /* we don't support RDWR in XFILE - use file 
106                    descriptors instead */
107                 errno = EINVAL;
108                 return NULL;
109         }
110
111         ret->open_flags = flags;
112
113         ret->fd = open(fname, flags, mode);
114         if (ret->fd == -1) {
115                 SAFE_FREE(ret);
116                 return NULL;
117         }
118
119         x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
120         
121         return ret;
122 }
123
124 /* simulate fclose() */
125 int x_fclose(XFILE *f)
126 {
127         int ret;
128
129         /* make sure we flush any buffered data */
130         x_fflush(f);
131
132         ret = close(f->fd);
133         f->fd = -1;
134         if (f->buf) {
135                 /* make sure data can't leak into a later malloc */
136                 memset(f->buf, 0, f->bufsize);
137                 SAFE_FREE(f->buf);
138         }
139         /* check the file descriptor given to the function is NOT one of the static
140          * descriptor of this libreary or we will free unallocated memory
141          * --sss */
142         if (f != x_stdin && f != x_stdout && f != x_stderr) {
143                 SAFE_FREE(f);
144         }
145         return ret;
146 }
147
148 /* simulate fwrite() */
149 size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
150 {
151         ssize_t ret;
152         size_t total=0;
153
154         /* we might be writing unbuffered */
155         if (f->buftype == X_IONBF || 
156             (!f->buf && !x_allocate_buffer(f))) {
157                 ret = write(f->fd, p, size*nmemb);
158                 if (ret == -1) return -1;
159                 return ret/size;
160         } 
161
162
163         while (total < size*nmemb) {
164                 size_t n = f->bufsize - f->bufused;
165                 n = MIN(n, (size*nmemb)-total);
166
167                 if (n == 0) {
168                         /* it's full, flush it */
169                         x_fflush(f);
170                         continue;
171                 }
172
173                 memcpy(f->buf + f->bufused, total+(const char *)p, n);
174                 f->bufused += n;
175                 total += n;
176         }
177
178         /* when line buffered we need to flush at the last linefeed. This can
179            flush a bit more than necessary, but that is harmless */
180         if (f->buftype == X_IOLBF && f->bufused) {
181                 int i;
182                 for (i=(size*nmemb)-1; i>=0; i--) {
183                         if (*(i+(const char *)p) == '\n') {
184                                 x_fflush(f);
185                                 break;
186                         }
187                 }
188         }
189
190         return total/size;
191 }
192
193 /* thank goodness for asprintf() */
194  int x_vfprintf(XFILE *f, const char *format, va_list ap)
195 {
196         char *p;
197         int len, ret;
198         va_list ap2;
199
200         VA_COPY(ap2, ap);
201
202         len = vasprintf(&p, format, ap2);
203         if (len <= 0) return len;
204         ret = x_fwrite(p, 1, len, f);
205         SAFE_FREE(p);
206         return ret;
207 }
208
209  int x_fprintf(XFILE *f, const char *format, ...)
210 {
211         va_list ap;
212         int ret;
213
214         va_start(ap, format);
215         ret = x_vfprintf(f, format, ap);
216         va_end(ap);
217         return ret;
218 }
219
220 /* at least fileno() is simple! */
221 int x_fileno(XFILE *f)
222 {
223         return f->fd;
224 }
225
226 /* simulate fflush() */
227 int x_fflush(XFILE *f)
228 {
229         int ret;
230
231         if (f->flags & X_FLAG_ERROR) return -1;
232
233         if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
234                 errno = EINVAL;
235                 return -1;
236         }
237
238         if (f->bufused == 0) return 0;
239
240         ret = write(f->fd, f->buf, f->bufused);
241         if (ret == -1) return -1;
242         
243         f->bufused -= ret;
244         if (f->bufused > 0) {
245                 f->flags |= X_FLAG_ERROR;
246                 memmove(f->buf, ret + (char *)f->buf, f->bufused);
247                 return -1;
248         }
249
250         return 0;
251 }
252
253 /* simulate setbuffer() */
254 void x_setbuffer(XFILE *f, char *buf, size_t size)
255 {
256         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
257 }
258
259 /* simulate setbuf() */
260 void x_setbuf(XFILE *f, char *buf)
261 {
262         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
263 }
264
265 /* simulate setlinebuf() */
266 void x_setlinebuf(XFILE *f)
267 {
268         x_setvbuf(f, NULL, X_IOLBF, 0);
269 }
270
271
272 /* simulate feof() */
273 int x_feof(XFILE *f)
274 {
275         if (f->flags & X_FLAG_EOF) return 1;
276         return 0;
277 }
278
279 /* simulate ferror() */
280 int x_ferror(XFILE *f)
281 {
282         if (f->flags & X_FLAG_ERROR) return 1;
283         return 0;
284 }
285
286 /* fill the read buffer */
287 static void x_fillbuf(XFILE *f)
288 {
289         int n;
290
291         if (f->bufused) return;
292
293         if (!f->buf && !x_allocate_buffer(f)) return;
294
295         n = read(f->fd, f->buf, f->bufsize);
296         if (n <= 0) return;
297         f->bufused = n;
298         f->next = f->buf;
299 }
300
301 /* simulate fgetc() */
302 int x_fgetc(XFILE *f)
303 {
304         int ret;
305
306         if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
307         
308         if (f->bufused == 0) x_fillbuf(f);
309
310         if (f->bufused == 0) {
311                 f->flags |= X_FLAG_EOF;
312                 return EOF;
313         }
314
315         ret = *(uint8_t *)(f->next);
316         f->next++;
317         f->bufused--;
318         return ret;
319 }
320
321 /* simulate fread */
322 size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
323 {
324         size_t total = 0;
325         while (total < size*nmemb) {
326                 int c = x_fgetc(f);
327                 if (c == EOF) break;
328                 (total+(char *)p)[0] = (char)c;
329                 total++;
330         }
331         return total/size;
332 }
333
334 /* simulate fgets() */
335 char *x_fgets(char *s, int size, XFILE *stream) 
336 {
337         char *s0 = s;
338         int l = size;
339         while (l>1) {
340                 int c = x_fgetc(stream);
341                 if (c == EOF) break;
342                 *s++ = (char)c;
343                 l--;
344                 if (c == '\n') break;
345         }
346         if (l==size || x_ferror(stream)) {
347                 return 0;
348         }
349         *s = 0;
350         return s0;
351 }
352
353 /* trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
354  * set then an error is returned */
355 off_t x_tseek(XFILE *f, off_t offset, int whence)
356 {
357         if (f->flags & X_FLAG_ERROR)
358                 return -1;
359
360         /* only SEEK_SET and SEEK_END are supported */
361         /* SEEK_CUR needs internal offset counter */
362         if (whence != SEEK_SET && whence != SEEK_END) {
363                 f->flags |= X_FLAG_EINVAL;
364                 errno = EINVAL;
365                 return -1;
366         }
367
368         /* empty the buffer */
369         switch (f->open_flags & O_ACCMODE) {
370         case O_RDONLY:
371                 f->bufused = 0;
372                 break;
373         case O_WRONLY:
374                 if (x_fflush(f) != 0)
375                         return -1;
376                 break;
377         default:
378                 errno = EINVAL;
379                 return -1;
380         }
381
382         f->flags &= ~X_FLAG_EOF;
383         return lseek(f->fd, offset, whence);
384 }