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