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 2 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, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 * @brief scalable FILE replacement
27 stdio is very convenient, but on some systems the file descriptor
28 in FILE* is 8 bits, so it fails when more than 255 files are open.
30 XFILE replaces stdio. It is less efficient, but at least it works
31 when you have lots of files open
33 The main restriction on XFILE is that it doesn't support seeking,
34 and doesn't support O_RDWR. That keeps the code simple.
38 #include "system/filesys.h"
40 #define XBUFSIZE BUFSIZ
42 static XFILE _x_stdin
= { 0, NULL
, NULL
, XBUFSIZE
, 0, O_RDONLY
, X_IOFBF
, 0 };
43 static XFILE _x_stdout
= { 1, NULL
, NULL
, XBUFSIZE
, 0, O_WRONLY
, X_IOLBF
, 0 };
44 static XFILE _x_stderr
= { 2, NULL
, NULL
, 0, 0, O_WRONLY
, X_IONBF
, 0 };
46 XFILE
*x_stdin
= &_x_stdin
;
47 XFILE
*x_stdout
= &_x_stdout
;
48 XFILE
*x_stderr
= &_x_stderr
;
51 #define X_FLAG_ERROR 2
52 #define X_FLAG_EINVAL 3
54 /** simulate setvbuf() */
55 int x_setvbuf(XFILE
*f
, char *buf
, int mode
, size_t size
)
58 if (f
->bufused
) return -1;
60 /* on files being read full buffering is the only option */
61 if ((f
->open_flags
& O_ACCMODE
) == O_RDONLY
) {
65 /* destroy any earlier buffer */
73 if (f
->buftype
== X_IONBF
) return 0;
75 /* if buffering then we need some size */
76 if (size
== 0) size
= XBUFSIZE
;
84 /* allocate the buffer */
85 static int x_allocate_buffer(XFILE
*f
)
88 if (f
->bufsize
== 0) return 0;
89 f
->buf
= malloc(f
->bufsize
);
90 if (!f
->buf
) return 0;
96 /** this looks more like open() than fopen(), but that is quite deliberate.
97 I want programmers to *think* about O_EXCL, O_CREAT etc not just
98 get them magically added
100 XFILE
*x_fopen(const char *fname
, int flags
, mode_t mode
)
104 ret
= malloc_p(XFILE
);
105 if (!ret
) return NULL
;
107 memset(ret
, 0, sizeof(XFILE
));
109 if ((flags
& O_ACCMODE
) == O_RDWR
) {
110 /* we don't support RDWR in XFILE - use file
111 descriptors instead */
116 ret
->open_flags
= flags
;
118 ret
->fd
= open(fname
, flags
, mode
);
124 x_setvbuf(ret
, NULL
, X_IOFBF
, XBUFSIZE
);
129 /** simulate fclose() */
130 int x_fclose(XFILE
*f
)
134 /* make sure we flush any buffered data */
140 /* make sure data can't leak into a later malloc */
141 memset(f
->buf
, 0, f
->bufsize
);
144 /* check the file descriptor given to the function is NOT one of the static
145 * descriptor of this libreary or we will free unallocated memory
147 if (f
!= x_stdin
&& f
!= x_stdout
&& f
!= x_stderr
) {
153 /** simulate fwrite() */
154 size_t x_fwrite(const void *p
, size_t size
, size_t nmemb
, XFILE
*f
)
159 /* we might be writing unbuffered */
160 if (f
->buftype
== X_IONBF
||
161 (!f
->buf
&& !x_allocate_buffer(f
))) {
162 ret
= write(f
->fd
, p
, size
*nmemb
);
163 if (ret
== -1) return -1;
168 while (total
< size
*nmemb
) {
169 size_t n
= f
->bufsize
- f
->bufused
;
170 n
= MIN(n
, (size
*nmemb
)-total
);
173 /* it's full, flush it */
178 memcpy(f
->buf
+ f
->bufused
, total
+(const char *)p
, n
);
183 /* when line buffered we need to flush at the last linefeed. This can
184 flush a bit more than necessary, but that is harmless */
185 if (f
->buftype
== X_IOLBF
&& f
->bufused
) {
187 for (i
=(size
*nmemb
)-1; i
>=0; i
--) {
188 if (*(i
+(const char *)p
) == '\n') {
198 /** thank goodness for asprintf() */
199 int x_vfprintf(XFILE
*f
, const char *format
, va_list ap
)
206 len
= vasprintf(&p
, format
, ap2
);
208 if (len
<= 0) return len
;
209 ret
= x_fwrite(p
, 1, len
, f
);
214 int x_fprintf(XFILE
*f
, const char *format
, ...)
219 va_start(ap
, format
);
220 ret
= x_vfprintf(f
, format
, ap
);
225 /* at least fileno() is simple! */
226 int x_fileno(XFILE
*f
)
231 /** simulate fflush() */
232 int x_fflush(XFILE
*f
)
236 if (f
->flags
& X_FLAG_ERROR
) return -1;
238 if ((f
->open_flags
& O_ACCMODE
) != O_WRONLY
) {
243 if (f
->bufused
== 0) return 0;
245 ret
= write(f
->fd
, f
->buf
, f
->bufused
);
246 if (ret
== -1) return -1;
249 if (f
->bufused
> 0) {
250 f
->flags
|= X_FLAG_ERROR
;
251 memmove(f
->buf
, ret
+ (char *)f
->buf
, f
->bufused
);
258 /** simulate setbuffer() */
259 void x_setbuffer(XFILE
*f
, char *buf
, size_t size
)
261 x_setvbuf(f
, buf
, buf
?X_IOFBF
:X_IONBF
, size
);
264 /** simulate setbuf() */
265 void x_setbuf(XFILE
*f
, char *buf
)
267 x_setvbuf(f
, buf
, buf
?X_IOFBF
:X_IONBF
, XBUFSIZE
);
270 /** simulate setlinebuf() */
271 void x_setlinebuf(XFILE
*f
)
273 x_setvbuf(f
, NULL
, X_IOLBF
, 0);
277 /** simulate feof() */
280 if (f
->flags
& X_FLAG_EOF
) return 1;
284 /** simulate ferror() */
285 int x_ferror(XFILE
*f
)
287 if (f
->flags
& X_FLAG_ERROR
) return 1;
291 /* fill the read buffer */
292 static void x_fillbuf(XFILE
*f
)
296 if (f
->bufused
) return;
298 if (!f
->buf
&& !x_allocate_buffer(f
)) return;
300 n
= read(f
->fd
, f
->buf
, f
->bufsize
);
306 /** simulate fgetc() */
307 int x_fgetc(XFILE
*f
)
311 if (f
->flags
& (X_FLAG_EOF
| X_FLAG_ERROR
)) return EOF
;
313 if (f
->bufused
== 0) x_fillbuf(f
);
315 if (f
->bufused
== 0) {
316 f
->flags
|= X_FLAG_EOF
;
320 ret
= *(uint8_t *)(f
->next
);
326 /** simulate fread */
327 size_t x_fread(void *p
, size_t size
, size_t nmemb
, XFILE
*f
)
330 while (total
< size
*nmemb
) {
333 (total
+(char *)p
)[0] = (char)c
;
339 /** simulate fgets() */
340 char *x_fgets(char *s
, int size
, XFILE
*stream
)
345 int c
= x_fgetc(stream
);
349 if (c
== '\n') break;
351 if (l
==size
|| x_ferror(stream
)) {
359 * trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
360 * set then an error is returned */
361 off_t
x_tseek(XFILE
*f
, off_t offset
, int whence
)
363 if (f
->flags
& X_FLAG_ERROR
)
366 /* only SEEK_SET and SEEK_END are supported */
367 /* SEEK_CUR needs internal offset counter */
368 if (whence
!= SEEK_SET
&& whence
!= SEEK_END
) {
369 f
->flags
|= X_FLAG_EINVAL
;
374 /* empty the buffer */
375 switch (f
->open_flags
& O_ACCMODE
) {
380 if (x_fflush(f
) != 0)
388 f
->flags
&= ~X_FLAG_EOF
;
389 return lseek(f
->fd
, offset
, whence
);