2 Unix SMB/CIFS implementation.
4 Copyright (C) Andrew Tridgell 2001
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.
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.
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/>.
22 * @brief scalable FILE replacement
26 stdio is very convenient, but on some systems the file descriptor
27 in FILE* is 8 bits, so it fails when more than 255 files are open.
29 XFILE replaces stdio. It is less efficient, but at least it works
30 when you have lots of files open
32 The main restriction on XFILE is that it doesn't support seeking,
33 and doesn't support O_RDWR. That keeps the code simple.
37 #include "system/filesys.h"
39 #define XBUFSIZE BUFSIZ
41 static XFILE _x_stdin = { 0, NULL, NULL, XBUFSIZE, 0, O_RDONLY, X_IOFBF, 0 };
42 static XFILE _x_stdout = { 1, NULL, NULL, XBUFSIZE, 0, O_WRONLY, X_IOLBF, 0 };
43 static XFILE _x_stderr = { 2, NULL, NULL, 0, 0, O_WRONLY, X_IONBF, 0 };
45 XFILE *x_stdin = &_x_stdin;
46 XFILE *x_stdout = &_x_stdout;
47 XFILE *x_stderr = &_x_stderr;
50 #define X_FLAG_ERROR 2
51 #define X_FLAG_EINVAL 3
53 /** simulate setvbuf() */
54 int x_setvbuf(XFILE *f, char *buf, int mode, size_t size)
57 if (f->bufused) return -1;
59 /* on files being read full buffering is the only option */
60 if ((f->open_flags & O_ACCMODE) == O_RDONLY) {
64 /* destroy any earlier buffer */
72 if (f->buftype == X_IONBF) return 0;
74 /* if buffering then we need some size */
75 if (size == 0) size = XBUFSIZE;
83 /* allocate the buffer */
84 static int x_allocate_buffer(XFILE *f)
87 if (f->bufsize == 0) return 0;
88 f->buf = (char *)malloc(f->bufsize);
89 if (!f->buf) return 0;
95 /** this looks more like open() than fopen(), but that is quite deliberate.
96 I want programmers to *think* about O_EXCL, O_CREAT etc not just
97 get them magically added
99 XFILE *x_fopen(const char *fname, int flags, mode_t mode)
103 ret = malloc_p(XFILE);
104 if (!ret) return NULL;
106 memset(ret, 0, sizeof(XFILE));
108 if ((flags & O_ACCMODE) == O_RDWR) {
109 /* we don't support RDWR in XFILE - use file
110 descriptors instead */
115 ret->open_flags = flags;
117 ret->fd = open(fname, flags, mode);
123 x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
128 /** simulate fclose() */
129 int x_fclose(XFILE *f)
133 /* make sure we flush any buffered data */
139 /* make sure data can't leak into a later malloc */
140 memset(f->buf, 0, f->bufsize);
143 /* check the file descriptor given to the function is NOT one of the static
144 * descriptor of this libreary or we will free unallocated memory
146 if (f != x_stdin && f != x_stdout && f != x_stderr) {
152 /** simulate fwrite() */
153 size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
158 /* we might be writing unbuffered */
159 if (f->buftype == X_IONBF ||
160 (!f->buf && !x_allocate_buffer(f))) {
161 ret = write(f->fd, p, size*nmemb);
162 if (ret == -1) return -1;
167 while (total < size*nmemb) {
168 size_t n = f->bufsize - f->bufused;
169 n = MIN(n, (size*nmemb)-total);
172 /* it's full, flush it */
177 memcpy(f->buf + f->bufused, total+(const char *)p, n);
182 /* when line buffered we need to flush at the last linefeed. This can
183 flush a bit more than necessary, but that is harmless */
184 if (f->buftype == X_IOLBF && f->bufused) {
186 for (i=(size*nmemb)-1; i>=0; i--) {
187 if (*(i+(const char *)p) == '\n') {
197 /** thank goodness for asprintf() */
198 int x_vfprintf(XFILE *f, const char *format, va_list ap)
205 len = vasprintf(&p, format, ap2);
207 if (len <= 0) return len;
208 ret = x_fwrite(p, 1, len, f);
213 int x_fprintf(XFILE *f, const char *format, ...)
218 va_start(ap, format);
219 ret = x_vfprintf(f, format, ap);
224 /* at least fileno() is simple! */
225 int x_fileno(XFILE *f)
230 /** simulate fflush() */
231 int x_fflush(XFILE *f)
235 if (f->flags & X_FLAG_ERROR) return -1;
237 if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
242 if (f->bufused == 0) return 0;
244 ret = write(f->fd, f->buf, f->bufused);
245 if (ret == -1) return -1;
248 if (f->bufused > 0) {
249 f->flags |= X_FLAG_ERROR;
250 memmove(f->buf, ret + (char *)f->buf, f->bufused);
257 /** simulate setbuffer() */
258 void x_setbuffer(XFILE *f, char *buf, size_t size)
260 x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
263 /** simulate setbuf() */
264 void x_setbuf(XFILE *f, char *buf)
266 x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
269 /** simulate setlinebuf() */
270 void x_setlinebuf(XFILE *f)
272 x_setvbuf(f, NULL, X_IOLBF, 0);
276 /** simulate feof() */
279 if (f->flags & X_FLAG_EOF) return 1;
283 /** simulate ferror() */
284 int x_ferror(XFILE *f)
286 if (f->flags & X_FLAG_ERROR) return 1;
290 /* fill the read buffer */
291 static void x_fillbuf(XFILE *f)
295 if (f->bufused) return;
297 if (!f->buf && !x_allocate_buffer(f)) return;
299 n = read(f->fd, f->buf, f->bufsize);
305 /** simulate fgetc() */
306 int x_fgetc(XFILE *f)
310 if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
312 if (f->bufused == 0) x_fillbuf(f);
314 if (f->bufused == 0) {
315 f->flags |= X_FLAG_EOF;
319 ret = *(uint8_t *)(f->next);
325 /** simulate fread */
326 size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
329 while (total < size*nmemb) {
332 (total+(char *)p)[0] = (char)c;
338 /** simulate fgets() */
339 char *x_fgets(char *s, int size, XFILE *stream)
344 int c = x_fgetc(stream);
348 if (c == '\n') break;
350 if (l==size || x_ferror(stream)) {
358 * trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
359 * set then an error is returned */
360 off_t x_tseek(XFILE *f, off_t offset, int whence)
362 if (f->flags & X_FLAG_ERROR)
365 /* only SEEK_SET and SEEK_END are supported */
366 /* SEEK_CUR needs internal offset counter */
367 if (whence != SEEK_SET && whence != SEEK_END) {
368 f->flags |= X_FLAG_EINVAL;
373 /* empty the buffer */
374 switch (f->open_flags & O_ACCMODE) {
379 if (x_fflush(f) != 0)
387 f->flags &= ~X_FLAG_EOF;
388 return lseek(f->fd, offset, whence);