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