7fc519e451a04140b6fff32e366dc91a6cd7d422
[samba.git] / source3 / lib / xfile.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    stdio replacement
5    Copyright (C) Andrew Tridgell 2001
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 /*
23   stdio is very convenient, but on some systems the file descriptor
24   in FILE* is 8 bits, so it fails when more than 255 files are open. 
25
26   XFILE replaces stdio. It is less efficient, but at least it works
27   when you have lots of files open
28
29   The main restriction on XFILE is that it doesn't support seeking,
30   and doesn't support O_RDWR. That keeps the code simple.
31 */
32
33 #include "includes.h"
34
35 static XFILE _x_stdin =  { 0, NULL, NULL, 0, 0, O_RDONLY, X_IOFBF, 0 };
36 static XFILE _x_stdout = { 1, NULL, NULL, 0, 0, O_WRONLY, X_IOLBF, 0 };
37 static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
38
39 XFILE *x_stdin = &_x_stdin;
40 XFILE *x_stdout = &_x_stdout;
41 XFILE *x_stderr = &_x_stderr;
42
43 #define XBUFSIZE BUFSIZ
44
45 #define X_FLAG_EOF 1
46 #define X_FLAG_ERROR 2
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 int x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
144 {
145         int ret, total=0;
146
147         /* we might be writing unbuffered */
148         if (f->buftype == X_IONBF || 
149             (!f->buf && !x_allocate_buffer(f))) {
150                 ret = write(f->fd, p, size*nmemb);
151                 if (ret == -1) return -1;
152                 return ret/size;
153         } 
154
155
156         while (total < size*nmemb) {
157                 int n = f->bufsize - f->bufused;
158                 n = MIN(n, (size*nmemb)-total);
159
160                 if (n == 0) {
161                         /* it's full, flush it */
162                         x_fflush(f);
163                         continue;
164                 }
165
166                 memcpy(f->buf + f->bufused, total+(const char *)p, n);
167                 f->bufused += n;
168                 total += n;
169         }
170
171         /* when line buffered we need to flush at the last linefeed. This can
172            flush a bit more than necessary, but that is harmless */
173         if (f->buftype == X_IOLBF && f->bufused) {
174                 int i;
175                 for (i=size-1; i>=0; i--) {
176                         if (*(i+(const char *)p) == '\n') {
177                                 x_fflush(f);
178                                 break;
179                         }
180                 }
181         }
182
183         return total/size;
184 }
185
186 /* thank goodness for asprintf() */
187 int x_vfprintf(XFILE *f, const char *format, va_list ap)
188 {
189         char *p;
190         int len, ret;
191         len = vasprintf(&p, format, ap);
192         if (len <= 0) return len;
193         ret = x_fwrite(p, 1, len, f);
194         SAFE_FREE(p);
195         return ret;
196 }
197
198 int x_fprintf(XFILE *f, const char *format, ...)
199 {
200         va_list ap;
201         int ret;
202
203         va_start(ap, format);
204         ret = x_vfprintf(f, format, ap);
205         va_end(ap);
206         return ret;
207 }
208
209 /* at least fileno() is simple! */
210 int x_fileno(XFILE *f)
211 {
212         return f->fd;
213 }
214
215 /* simulate fflush() */
216 int x_fflush(XFILE *f)
217 {
218         int ret;
219
220         if (f->flags & X_FLAG_ERROR) return -1;
221
222         if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
223                 errno = EINVAL;
224                 return -1;
225         }
226
227         if (f->bufused == 0) return 0;
228
229         ret = write(f->fd, f->buf, f->bufused);
230         if (ret == -1) return -1;
231         
232         f->bufused -= ret;
233         if (f->bufused > 0) {
234                 f->flags |= X_FLAG_ERROR;
235                 memmove(f->buf, ret + (char *)f->buf, f->bufused);
236                 return -1;
237         }
238
239         return 0;
240 }
241
242 /* simulate setbuffer() */
243 void x_setbuffer(XFILE *f, char *buf, size_t size)
244 {
245         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
246 }
247
248 /* simulate setbuf() */
249 void x_setbuf(XFILE *f, char *buf)
250 {
251         x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
252 }
253
254 /* simulate setlinebuf() */
255 void x_setlinebuf(XFILE *f)
256 {
257         x_setvbuf(f, NULL, X_IOLBF, 0);
258 }
259
260
261 /* simulate feof() */
262 int x_feof(XFILE *f)
263 {
264         if (f->flags & X_FLAG_EOF) return 1;
265         return 0;
266 }
267
268 /* simulate ferror() */
269 int x_ferror(XFILE *f)
270 {
271         if (f->flags & X_FLAG_ERROR) return 1;
272         return 0;
273 }
274
275 /* fill the read buffer */
276 static void x_fillbuf(XFILE *f)
277 {
278         int n;
279
280         if (f->bufused) return;
281
282         if (!f->buf && !x_allocate_buffer(f)) return;
283
284         n = read(f->fd, f->buf, f->bufsize);
285         if (n <= 0) return;
286         f->bufused = n;
287         f->next = f->buf;
288 }
289
290 /* simulate fgetc() */
291 int x_fgetc(XFILE *f)
292 {
293         int ret;
294
295         if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
296         
297         if (f->bufused == 0) x_fillbuf(f);
298
299         if (f->bufused == 0) {
300                 f->flags |= X_FLAG_EOF;
301                 return EOF;
302         }
303
304         ret = *(unsigned char *)(f->next);
305         f->next++;
306         f->bufused--;
307         return ret;
308 }
309
310 /* simulate fread */
311 size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
312 {
313         size_t total = 0;
314         while (total < size*nmemb) {
315                 int c = x_fgetc(f);
316                 if (c == EOF) break;
317                 (total+(char *)p)[0] = (char)c;
318                 total++;
319         }
320         return total/size;
321 }
322
323 /* simulate fgets() */
324 char *x_fgets(char *s, int size, XFILE *stream) 
325 {
326         char *s0 = s;
327         int l = size;
328         while (l>1) {
329                 int c = x_fgetc(stream);
330                 if (c == EOF) break;
331                 *s++ = (char)c;
332                 l--;
333                 if (c == '\n') break;
334         }
335         if (l==size || x_ferror(stream)) {
336                 return 0;
337         }
338         *s = 0;
339         return s0;
340 }