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