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