Merge branch 'selftest' of git://git.samba.org/jelmer/samba
[sfrench/samba-autobuild/.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 total = 0;
333         while (total < size*nmemb) {
334                 int c = x_fgetc(f);
335                 if (c == EOF) break;
336                 (total+(char *)p)[0] = (char)c;
337                 total++;
338         }
339         return total/size;
340 }
341
342 /** simulate fgets() */
343 char *x_fgets(char *s, int size, XFILE *stream) 
344 {
345         char *s0 = s;
346         int l = size;
347         while (l>1) {
348                 int c = x_fgetc(stream);
349                 if (c == EOF) break;
350                 *s++ = (char)c;
351                 l--;
352                 if (c == '\n') break;
353         }
354         if (l==size || x_ferror(stream)) {
355                 return 0;
356         }
357         *s = 0;
358         return s0;
359 }
360
361 /** 
362  * trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
363  * set then an error is returned */
364 off_t x_tseek(XFILE *f, off_t offset, int whence)
365 {
366         if (f->flags & X_FLAG_ERROR)
367                 return -1;
368
369         /* only SEEK_SET and SEEK_END are supported */
370         /* SEEK_CUR needs internal offset counter */
371         if (whence != SEEK_SET && whence != SEEK_END) {
372                 f->flags |= X_FLAG_EINVAL;
373                 errno = EINVAL;
374                 return -1;
375         }
376
377         /* empty the buffer */
378         switch (f->open_flags & O_ACCMODE) {
379         case O_RDONLY:
380                 f->bufused = 0;
381                 break;
382         case O_WRONLY:
383                 if (x_fflush(f) != 0)
384                         return -1;
385                 break;
386         default:
387                 errno = EINVAL;
388                 return -1;
389         }
390
391         f->flags &= ~X_FLAG_EOF;
392         return lseek(f->fd, offset, whence);
393 }
394
395 XFILE *x_fdup(const XFILE *f)
396 {
397         XFILE *ret;
398         int fd;
399
400         fd = dup(x_fileno(f));
401         if (fd < 0) {
402                 return NULL;
403         }
404
405         ret = (XFILE *)malloc_p(XFILE);
406         if (!ret) {
407                 close(fd);
408                 return NULL;
409         }
410         memset(ret, 0, sizeof(XFILE));
411
412         ret->fd = fd;
413         ret->open_flags = f->open_flags;
414         x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
415         return ret;
416 }