util: rewrite dlinklist.h so that DLIST_ADD_END() is O(1)
[ira/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                 SAFE_FREE(ret);
116                 errno = EINVAL;
117                 return NULL;
118         }
119
120         ret->open_flags = flags;
121
122         ret->fd = open(fname, flags, mode);
123         if (ret->fd == -1) {
124                 SAFE_FREE(ret);
125                 return NULL;
126         }
127
128         x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
129         
130         return ret;
131 }
132
133 /** simulate fclose() */
134 int x_fclose(XFILE *f)
135 {
136         int ret;
137
138         /* make sure we flush any buffered data */
139         x_fflush(f);
140
141         ret = close(f->fd);
142         f->fd = -1;
143         if (f->buf) {
144                 /* make sure data can't leak into a later malloc */
145                 memset(f->buf, 0, f->bufsize);
146                 SAFE_FREE(f->buf);
147         }
148         /* check the file descriptor given to the function is NOT one of the static
149          * descriptor of this libreary or we will free unallocated memory
150          * --sss */
151         if (f != x_stdin && f != x_stdout && f != x_stderr) {
152                 SAFE_FREE(f);
153         }
154         return ret;
155 }
156
157 /** simulate fwrite() */
158 size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
159 {
160         ssize_t ret;
161         size_t total=0;
162
163         /* we might be writing unbuffered */
164         if (f->buftype == X_IONBF || 
165             (!f->buf && !x_allocate_buffer(f))) {
166                 ret = write(f->fd, p, size*nmemb);
167                 if (ret == -1) return -1;
168                 return ret/size;
169         } 
170
171
172         while (total < size*nmemb) {
173                 size_t n = f->bufsize - f->bufused;
174                 n = MIN(n, (size*nmemb)-total);
175
176                 if (n == 0) {
177                         /* it's full, flush it */
178                         x_fflush(f);
179                         continue;
180                 }
181
182                 memcpy(f->buf + f->bufused, total+(const char *)p, n);
183                 f->bufused += n;
184                 total += n;
185         }
186
187         /* when line buffered we need to flush at the last linefeed. This can
188            flush a bit more than necessary, but that is harmless */
189         if (f->buftype == X_IOLBF && f->bufused) {
190                 int i;
191                 for (i=(size*nmemb)-1; i>=0; i--) {
192                         if (*(i+(const char *)p) == '\n') {
193                                 x_fflush(f);
194                                 break;
195                         }
196                 }
197         }
198
199         return total/size;
200 }
201
202 /** thank goodness for asprintf() */
203  int x_vfprintf(XFILE *f, const char *format, va_list ap)
204 {
205         char *p;
206         int len, ret;
207         va_list ap2;
208
209         va_copy(ap2, ap);
210         len = vasprintf(&p, format, ap2);
211         va_end(ap2);
212         if (len <= 0) return len;
213         ret = x_fwrite(p, 1, len, f);
214         SAFE_FREE(p);
215         return ret;
216 }
217
218  int x_fprintf(XFILE *f, const char *format, ...)
219 {
220         va_list ap;
221         int ret;
222
223         va_start(ap, format);
224         ret = x_vfprintf(f, format, ap);
225         va_end(ap);
226         return ret;
227 }
228
229 /* at least fileno() is simple! */
230 int x_fileno(const XFILE *f)
231 {
232         return f->fd;
233 }
234
235 /** simulate fflush() */
236 int x_fflush(XFILE *f)
237 {
238         int ret;
239
240         if (f->flags & X_FLAG_ERROR) return -1;
241
242         if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
243                 errno = EINVAL;
244                 return -1;
245         }
246
247         if (f->bufused == 0) return 0;
248
249         ret = write(f->fd, f->buf, f->bufused);
250         if (ret == -1) return -1;
251         
252         f->bufused -= ret;
253         if (f->bufused > 0) {
254                 f->flags |= X_FLAG_ERROR;
255                 memmove(f->buf, ret + (char *)f->buf, f->bufused);
256                 return -1;
257         }
258
259         return 0;
260 }
261
262 /** simulate setbuffer() */
263 void x_setbuffer(XFILE *f, char *buf, size_t size)
264 {
265         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
266 }
267
268 /** simulate setbuf() */
269 void x_setbuf(XFILE *f, char *buf)
270 {
271         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
272 }
273
274 /** simulate setlinebuf() */
275 void x_setlinebuf(XFILE *f)
276 {
277         x_setvbuf(f, NULL, X_IOLBF, 0);
278 }
279
280
281 /** simulate feof() */
282 int x_feof(XFILE *f)
283 {
284         if (f->flags & X_FLAG_EOF) return 1;
285         return 0;
286 }
287
288 /** simulate ferror() */
289 int x_ferror(XFILE *f)
290 {
291         if (f->flags & X_FLAG_ERROR) return 1;
292         return 0;
293 }
294
295 /* fill the read buffer */
296 static void x_fillbuf(XFILE *f)
297 {
298         int n;
299
300         if (f->bufused) return;
301
302         if (!f->buf && !x_allocate_buffer(f)) return;
303
304         n = read(f->fd, f->buf, f->bufsize);
305         if (n <= 0) return;
306         f->bufused = n;
307         f->next = f->buf;
308 }
309
310 /** simulate fgetc() */
311 int x_fgetc(XFILE *f)
312 {
313         int ret;
314
315         if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
316         
317         if (f->bufused == 0) x_fillbuf(f);
318
319         if (f->bufused == 0) {
320                 f->flags |= X_FLAG_EOF;
321                 return EOF;
322         }
323
324         ret = *(uint8_t *)(f->next);
325         f->next++;
326         f->bufused--;
327         return ret;
328 }
329
330 /** simulate fread */
331 size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
332 {
333         size_t remaining = size * nmemb;
334         size_t total = 0;
335
336         while (remaining > 0) {
337                 size_t thistime;
338
339                 x_fillbuf(f);
340
341                 if (f->bufused == 0) {
342                         f->flags |= X_FLAG_EOF;
343                         break;
344                 }
345
346                 thistime = MIN(f->bufused, remaining);
347
348                 memcpy((char *)p+total, f->next, thistime);
349
350                 f->next += thistime;
351                 f->bufused -= thistime;
352                 remaining -= thistime;
353                 total += thistime;
354         }
355         return total/size;
356 }
357
358 /** simulate fgets() */
359 char *x_fgets(char *s, int size, XFILE *stream) 
360 {
361         char *s0 = s;
362         int l = size;
363         while (l>1) {
364                 int c = x_fgetc(stream);
365                 if (c == EOF) break;
366                 *s++ = (char)c;
367                 l--;
368                 if (c == '\n') break;
369         }
370         if (l==size || x_ferror(stream)) {
371                 return 0;
372         }
373         *s = 0;
374         return s0;
375 }
376
377 /** 
378  * trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
379  * set then an error is returned */
380 off_t x_tseek(XFILE *f, off_t offset, int whence)
381 {
382         if (f->flags & X_FLAG_ERROR)
383                 return -1;
384
385         /* only SEEK_SET and SEEK_END are supported */
386         /* SEEK_CUR needs internal offset counter */
387         if (whence != SEEK_SET && whence != SEEK_END) {
388                 f->flags |= X_FLAG_EINVAL;
389                 errno = EINVAL;
390                 return -1;
391         }
392
393         /* empty the buffer */
394         switch (f->open_flags & O_ACCMODE) {
395         case O_RDONLY:
396                 f->bufused = 0;
397                 break;
398         case O_WRONLY:
399                 if (x_fflush(f) != 0)
400                         return -1;
401                 break;
402         default:
403                 errno = EINVAL;
404                 return -1;
405         }
406
407         f->flags &= ~X_FLAG_EOF;
408         return lseek(f->fd, offset, whence);
409 }
410
411 XFILE *x_fdup(const XFILE *f)
412 {
413         XFILE *ret;
414         int fd;
415
416         fd = dup(x_fileno(f));
417         if (fd < 0) {
418                 return NULL;
419         }
420
421         ret = (XFILE *)malloc_p(XFILE);
422         if (!ret) {
423                 close(fd);
424                 return NULL;
425         }
426         memset(ret, 0, sizeof(XFILE));
427
428         ret->fd = fd;
429         ret->open_flags = f->open_flags;
430         x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
431         return ret;
432 }