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