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