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