libcli/smb: move smb2cli_flush.c from source3 to the toplevel
[Samba/vl.git] / lib / util / xfile.c
blob58a34bf1c34a9cb060a0e7539c134d8eb0f7af9b
1 /*
2 Unix SMB/CIFS implementation.
3 stdio replacement
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/>.
20 /**
21 * @file
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.
36 #include "replace.h"
37 #include "system/filesys.h"
38 #include "lib/util/samba_util.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;
50 #define X_FLAG_EOF 1
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)
57 x_fflush(f);
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) {
62 mode = X_IOFBF;
65 /* destroy any earlier buffer */
66 SAFE_FREE(f->buf);
67 f->buf = 0;
68 f->bufsize = 0;
69 f->next = NULL;
70 f->bufused = 0;
71 f->buftype = mode;
73 if (f->buftype == X_IONBF) return 0;
75 /* if buffering then we need some size */
76 if (size == 0) size = XBUFSIZE;
78 f->bufsize = size;
79 f->bufused = 0;
81 return 0;
84 /* allocate the buffer */
85 static int x_allocate_buffer(XFILE *f)
87 if (f->buf) return 1;
88 if (f->bufsize == 0) return 0;
89 f->buf = (char *)malloc(f->bufsize);
90 if (!f->buf) return 0;
91 f->next = f->buf;
92 return 1;
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)
102 XFILE *ret;
104 ret = (XFILE *)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 */
112 SAFE_FREE(ret);
113 errno = EINVAL;
114 return NULL;
117 ret->open_flags = flags;
119 ret->fd = open(fname, flags, mode);
120 if (ret->fd == -1) {
121 SAFE_FREE(ret);
122 return NULL;
125 x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
127 return ret;
130 /** simulate fclose() */
131 int x_fclose(XFILE *f)
133 int ret;
135 /* make sure we flush any buffered data */
136 x_fflush(f);
138 ret = close(f->fd);
139 f->fd = -1;
140 if (f->buf) {
141 /* make sure data can't leak into a later malloc */
142 memset(f->buf, 0, f->bufsize);
143 SAFE_FREE(f->buf);
145 /* check the file descriptor given to the function is NOT one of the static
146 * descriptor of this libreary or we will free unallocated memory
147 * --sss */
148 if (f != x_stdin && f != x_stdout && f != x_stderr) {
149 SAFE_FREE(f);
151 return ret;
154 /** simulate fwrite() */
155 size_t x_fwrite(const void *p, size_t size, size_t nmemb, XFILE *f)
157 ssize_t ret;
158 size_t total=0;
160 /* we might be writing unbuffered */
161 if (f->buftype == X_IONBF ||
162 (!f->buf && !x_allocate_buffer(f))) {
163 ret = write(f->fd, p, size*nmemb);
164 if (ret == -1) return -1;
165 return ret/size;
169 while (total < size*nmemb) {
170 size_t n = f->bufsize - f->bufused;
171 n = MIN(n, (size*nmemb)-total);
173 if (n == 0) {
174 /* it's full, flush it */
175 x_fflush(f);
176 continue;
179 memcpy(f->buf + f->bufused, total+(const char *)p, n);
180 f->bufused += n;
181 total += n;
184 /* when line buffered we need to flush at the last linefeed. This can
185 flush a bit more than necessary, but that is harmless */
186 if (f->buftype == X_IOLBF && f->bufused) {
187 int i;
188 for (i=(size*nmemb)-1; i>=0; i--) {
189 if (*(i+(const char *)p) == '\n') {
190 x_fflush(f);
191 break;
196 return total/size;
199 /** thank goodness for asprintf() */
200 int x_vfprintf(XFILE *f, const char *format, va_list ap)
202 char *p;
203 int len, ret;
204 va_list ap2;
206 va_copy(ap2, ap);
207 len = vasprintf(&p, format, ap2);
208 va_end(ap2);
209 if (len <= 0) return len;
210 ret = x_fwrite(p, 1, len, f);
211 SAFE_FREE(p);
212 return ret;
215 int x_fprintf(XFILE *f, const char *format, ...)
217 va_list ap;
218 int ret;
220 va_start(ap, format);
221 ret = x_vfprintf(f, format, ap);
222 va_end(ap);
223 return ret;
226 /* at least fileno() is simple! */
227 int x_fileno(const XFILE *f)
229 return f->fd;
232 /** simulate fflush() */
233 int x_fflush(XFILE *f)
235 int ret;
237 if (f->flags & X_FLAG_ERROR) return -1;
239 if ((f->open_flags & O_ACCMODE) != O_WRONLY) {
240 errno = EINVAL;
241 return -1;
244 if (f->bufused == 0) return 0;
246 ret = write(f->fd, f->buf, f->bufused);
247 if (ret == -1) return -1;
249 f->bufused -= ret;
250 if (f->bufused > 0) {
251 f->flags |= X_FLAG_ERROR;
252 memmove(f->buf, ret + (char *)f->buf, f->bufused);
253 return -1;
256 return 0;
259 /** simulate setbuffer() */
260 void x_setbuffer(XFILE *f, char *buf, size_t size)
262 x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, size);
265 /** simulate setbuf() */
266 void x_setbuf(XFILE *f, char *buf)
268 x_setvbuf(f, buf, buf?X_IOFBF:X_IONBF, XBUFSIZE);
271 /** simulate setlinebuf() */
272 void x_setlinebuf(XFILE *f)
274 x_setvbuf(f, NULL, X_IOLBF, 0);
278 /** simulate feof() */
279 int x_feof(XFILE *f)
281 if (f->flags & X_FLAG_EOF) return 1;
282 return 0;
285 /** simulate ferror() */
286 int x_ferror(XFILE *f)
288 if (f->flags & X_FLAG_ERROR) return 1;
289 return 0;
292 /* fill the read buffer */
293 static void x_fillbuf(XFILE *f)
295 int n;
297 if (f->bufused) return;
299 if (!f->buf && !x_allocate_buffer(f)) return;
301 n = read(f->fd, f->buf, f->bufsize);
302 if (n <= 0) return;
303 f->bufused = n;
304 f->next = f->buf;
307 /** simulate fgetc() */
308 int x_fgetc(XFILE *f)
310 int ret;
312 if (f->flags & (X_FLAG_EOF | X_FLAG_ERROR)) return EOF;
314 if (f->bufused == 0) x_fillbuf(f);
316 if (f->bufused == 0) {
317 f->flags |= X_FLAG_EOF;
318 return EOF;
321 ret = *(uint8_t *)(f->next);
322 f->next++;
323 f->bufused--;
324 return ret;
327 /** simulate fread */
328 size_t x_fread(void *p, size_t size, size_t nmemb, XFILE *f)
330 size_t remaining = size * nmemb;
331 size_t total = 0;
333 while (remaining > 0) {
334 size_t thistime;
336 x_fillbuf(f);
338 if (f->bufused == 0) {
339 f->flags |= X_FLAG_EOF;
340 break;
343 thistime = MIN(f->bufused, remaining);
345 memcpy((char *)p+total, f->next, thistime);
347 f->next += thistime;
348 f->bufused -= thistime;
349 remaining -= thistime;
350 total += thistime;
352 return total/size;
355 /** simulate fgets() */
356 char *x_fgets(char *s, int size, XFILE *stream)
358 char *s0 = s;
359 int l = size;
360 while (l>1) {
361 int c = x_fgetc(stream);
362 if (c == EOF) break;
363 *s++ = (char)c;
364 l--;
365 if (c == '\n') break;
367 if (l==size || x_ferror(stream)) {
368 return 0;
370 *s = 0;
371 return s0;
374 /**
375 * trivial seek, works only for SEEK_SET and SEEK_END if SEEK_CUR is
376 * set then an error is returned */
377 off_t x_tseek(XFILE *f, off_t offset, int whence)
379 if (f->flags & X_FLAG_ERROR)
380 return -1;
382 /* only SEEK_SET and SEEK_END are supported */
383 /* SEEK_CUR needs internal offset counter */
384 if (whence != SEEK_SET && whence != SEEK_END) {
385 f->flags |= X_FLAG_EINVAL;
386 errno = EINVAL;
387 return -1;
390 /* empty the buffer */
391 switch (f->open_flags & O_ACCMODE) {
392 case O_RDONLY:
393 f->bufused = 0;
394 break;
395 case O_WRONLY:
396 if (x_fflush(f) != 0)
397 return -1;
398 break;
399 default:
400 errno = EINVAL;
401 return -1;
404 f->flags &= ~X_FLAG_EOF;
405 return lseek(f->fd, offset, whence);
408 XFILE *x_fdup(const XFILE *f)
410 XFILE *ret;
411 int fd;
413 fd = dup(x_fileno(f));
414 if (fd < 0) {
415 return NULL;
418 ret = (XFILE *)malloc_p(XFILE);
419 if (!ret) {
420 close(fd);
421 return NULL;
423 memset(ret, 0, sizeof(XFILE));
425 ret->fd = fd;
426 ret->open_flags = f->open_flags;
427 x_setvbuf(ret, NULL, X_IOFBF, XBUFSIZE);
428 return ret;