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