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