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