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"
41 #define XBUFSIZE BUFSIZ
43 static XFILE _x_stdin
= { 0, NULL
, NULL
, XBUFSIZE
, 0, O_RDONLY
, X_IOFBF
, 0 };
44 static XFILE _x_stdout
= { 1, NULL
, NULL
, XBUFSIZE
, 0, O_WRONLY
, X_IOLBF
, 0 };
45 static XFILE _x_stderr
= { 2, NULL
, NULL
, 0, 0, O_WRONLY
, X_IONBF
, 0 };
47 XFILE
*x_stdin
= &_x_stdin
;
48 XFILE
*x_stdout
= &_x_stdout
;
49 XFILE
*x_stderr
= &_x_stderr
;
52 #define X_FLAG_ERROR 2
53 #define X_FLAG_EINVAL 3
55 /** simulate setvbuf() */
56 int x_setvbuf(XFILE
*f
, char *buf
, int mode
, size_t size
)
59 if (f
->bufused
) return -1;
61 /* on files being read full buffering is the only option */
62 if ((f
->open_flags
& O_ACCMODE
) == O_RDONLY
) {
66 /* destroy any earlier buffer */
74 if (f
->buftype
== X_IONBF
) return 0;
76 /* if buffering then we need some size */
77 if (size
== 0) size
= XBUFSIZE
;
85 /* allocate the buffer */
86 static int x_allocate_buffer(XFILE
*f
)
89 if (f
->bufsize
== 0) return 0;
90 f
->buf
= (char *)malloc(f
->bufsize
);
91 if (!f
->buf
) return 0;
97 /** this looks more like open() than fopen(), but that is quite deliberate.
98 I want programmers to *think* about O_EXCL, O_CREAT etc not just
99 get them magically added
101 XFILE
*x_fopen(const char *fname
, int flags
, mode_t mode
)
105 ret
= (XFILE
*)malloc_p(XFILE
);
106 if (!ret
) return NULL
;
108 memset(ret
, 0, sizeof(XFILE
));
110 if ((flags
& O_ACCMODE
) == O_RDWR
) {
111 /* we don't support RDWR in XFILE - use file
112 descriptors instead */
118 ret
->open_flags
= flags
;
120 ret
->fd
= open(fname
, flags
, mode
);
126 x_setvbuf(ret
, NULL
, X_IOFBF
, XBUFSIZE
);
131 /** simulate fclose() */
132 int x_fclose(XFILE
*f
)
136 /* make sure we flush any buffered data */
142 /* make sure data can't leak into a later malloc */
143 memset(f
->buf
, 0, f
->bufsize
);
146 /* check the file descriptor given to the function is NOT one of the static
147 * descriptor of this libreary or we will free unallocated memory
149 if (f
!= x_stdin
&& f
!= x_stdout
&& f
!= x_stderr
) {
155 /** simulate fwrite() */
156 size_t x_fwrite(const void *p
, size_t size
, size_t nmemb
, XFILE
*f
)
161 /* we might be writing unbuffered */
162 if (f
->buftype
== X_IONBF
||
163 (!f
->buf
&& !x_allocate_buffer(f
))) {
164 ret
= write(f
->fd
, p
, size
*nmemb
);
165 if (ret
== -1) return -1;
170 while (total
< size
*nmemb
) {
171 size_t n
= f
->bufsize
- f
->bufused
;
172 n
= MIN(n
, (size
*nmemb
)-total
);
175 /* it's full, flush it */
180 memcpy(f
->buf
+ f
->bufused
, total
+(const char *)p
, n
);
185 /* when line buffered we need to flush at the last linefeed. This can
186 flush a bit more than necessary, but that is harmless */
187 if (f
->buftype
== X_IOLBF
&& f
->bufused
) {
189 for (i
=(size
*nmemb
)-1; i
>=0; i
--) {
190 if (*(i
+(const char *)p
) == '\n') {
200 /** thank goodness for asprintf() */
201 int x_vfprintf(XFILE
*f
, const char *format
, va_list ap
)
208 len
= vasprintf(&p
, format
, ap2
);
210 if (len
<= 0) return len
;
211 ret
= x_fwrite(p
, 1, len
, f
);
216 int x_fprintf(XFILE
*f
, const char *format
, ...)
221 va_start(ap
, format
);
222 ret
= x_vfprintf(f
, format
, ap
);
227 /* at least fileno() is simple! */
228 int x_fileno(const XFILE
*f
)
233 /** simulate fflush() */
234 int x_fflush(XFILE
*f
)
238 if (f
->flags
& X_FLAG_ERROR
) return -1;
240 if ((f
->open_flags
& O_ACCMODE
) != O_WRONLY
) {
245 if (f
->bufused
== 0) return 0;
247 ret
= write(f
->fd
, f
->buf
, f
->bufused
);
248 if (ret
== -1) return -1;
251 if (f
->bufused
> 0) {
252 f
->flags
|= X_FLAG_ERROR
;
253 memmove(f
->buf
, ret
+ (char *)f
->buf
, f
->bufused
);
260 /** simulate setbuffer() */
261 void x_setbuffer(XFILE
*f
, char *buf
, size_t size
)
263 x_setvbuf(f
, buf
, buf
?X_IOFBF
:X_IONBF
, size
);
266 /** simulate setbuf() */
267 void x_setbuf(XFILE
*f
, char *buf
)
269 x_setvbuf(f
, buf
, buf
?X_IOFBF
:X_IONBF
, XBUFSIZE
);
272 /** simulate setlinebuf() */
273 void x_setlinebuf(XFILE
*f
)
275 x_setvbuf(f
, NULL
, X_IOLBF
, 0);
279 /** simulate feof() */
282 if (f
->flags
& X_FLAG_EOF
) return 1;
286 /** simulate ferror() */
287 int x_ferror(XFILE
*f
)
289 if (f
->flags
& X_FLAG_ERROR
) return 1;
293 /* fill the read buffer */
294 static void x_fillbuf(XFILE
*f
)
298 if (f
->bufused
) return;
300 if (!f
->buf
&& !x_allocate_buffer(f
)) return;
302 n
= read(f
->fd
, f
->buf
, f
->bufsize
);
308 /** simulate fgetc() */
309 int x_fgetc(XFILE
*f
)
313 if (f
->flags
& (X_FLAG_EOF
| X_FLAG_ERROR
)) return EOF
;
315 if (f
->bufused
== 0) x_fillbuf(f
);
317 if (f
->bufused
== 0) {
318 f
->flags
|= X_FLAG_EOF
;
322 ret
= *(uint8_t *)(f
->next
);
328 /** simulate fread */
329 size_t x_fread(void *p
, size_t size
, size_t nmemb
, XFILE
*f
)
331 size_t remaining
= size
* nmemb
;
334 while (remaining
> 0) {
339 if (f
->bufused
== 0) {
340 f
->flags
|= X_FLAG_EOF
;
344 thistime
= MIN(f
->bufused
, remaining
);
346 memcpy((char *)p
+total
, f
->next
, thistime
);
349 f
->bufused
-= thistime
;
350 remaining
-= thistime
;
356 /** simulate fgets() */
357 char *x_fgets(char *s
, int size
, XFILE
*stream
)
362 int c
= x_fgetc(stream
);
366 if (c
== '\n') break;
368 if (l
==size
|| x_ferror(stream
)) {
376 * trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
377 * set then an error is returned */
378 off_t
x_tseek(XFILE
*f
, off_t offset
, int whence
)
380 if (f
->flags
& X_FLAG_ERROR
)
383 /* only SEEK_SET and SEEK_END are supported */
384 /* SEEK_CUR needs internal offset counter */
385 if (whence
!= SEEK_SET
&& whence
!= SEEK_END
) {
386 f
->flags
|= X_FLAG_EINVAL
;
391 /* empty the buffer */
392 switch (f
->open_flags
& O_ACCMODE
) {
397 if (x_fflush(f
) != 0)
405 f
->flags
&= ~X_FLAG_EOF
;
406 return lseek(f
->fd
, offset
, whence
);
409 XFILE
*x_fdup(const XFILE
*f
)
414 fd
= dup(x_fileno(f
));
419 ret
= (XFILE
*)malloc_p(XFILE
);
424 memset(ret
, 0, sizeof(XFILE
));
427 ret
->open_flags
= f
->open_flags
;
428 x_setvbuf(ret
, NULL
, X_IOFBF
, XBUFSIZE
);