📄 stdio.c
字号:
/* Copyright (C) 1996 Robert de Bath <rdebath@cix.compulink.co.uk> * This file is part of the Linux-8086 C library and is distributed * under the GNU Library General Public License. *//* This is an implementation of the C standard IO package. * * Updates: * 29-Sep-2000 W. Greathouse 1. fgetc copying beyond end of buffer * 2. stdout needs flushed when input requested on * stdin. * 3. bufend was set incorrectly to 4 bytes beyond * bufin (sizeof a pointer) instead of BUFSIZ. * This resulted in 4 byte buffers for full * buffered stdin and stdout and an 8 byte * buffer for the unbuffered stderr! *//* * Feb 27, 2001 Manuel Novoa III * * Most of the core functionality has been completely rewritten. * A number of functions have been added as well, as mandated by C89. * * An extension function "fsfopen" has been added: * Open a file using an automatically (stack) or statically allocated FILE. * The FILE * returned behaves just as any other FILE * with respect to the * stdio functions, but be aware of the following: * NOTE: The buffer used for the file is FILE's builtin 2-byte buffer, so * setting a new buffer is probably advisable. * NOTE: This function is primarily intended to be used for stack-allocated * FILEs when uClibc stdio has no dynamic memory support. * For the statically allocated case, it is probably better to increase * the value of FIXED_STREAMS in stdio.c. * WARNING: If allocated on the stack, make sure you call fclose before the * stack memory is reclaimed! */#include <stdlib.h>#include <stdio.h>#include <unistd.h>#include <fcntl.h>#include <sys/types.h>#include <malloc.h>#include <errno.h>#include <string.h>#include <assert.h>#include <limits.h>extern off_t _uClibc_fwrite(const unsigned char *buf, off_t bytes, FILE *fp);extern off_t _uClibc_fread(unsigned char *buf, off_t bytes, FILE *fp);/* Used internally to actually open files */extern FILE *__fopen __P((__const char *__restrict __filename, int __fd, FILE *__restrict __stream, __const char *__restrict __mode));/* Note: This def of READING is ok since 1st ungetc puts in buf. */#define READING(fp) (fp->bufstart < fp->bufread)#define WRITING(fp) (fp->bufwrite > fp->bufstart)#define READABLE(fp) (fp->bufread != 0)#define WRITEABLE(fp) (fp->bufwrite != 0)#define EOF_OR_ERROR(fp) (fp->mode & (__MODE_EOF | __MODE_ERR))/***********************************************************************//* BUILD TIME OPTIONS *//***********************************************************************//* * FIXED_STREAMS must be >= 3 and FIXED_BUFFERS must be >= 2. * As a feature, these can be increased, although this is probably * only useful if DISABLE_DYNAMIC is set to 1 below. */#define FIXED_STREAMS 3#define FIXED_BUFFERS 2/* * As a feature, you can build uClibc with no dynamic allocation done * by the stdio package. Just set DISABLE_DYNAMIC to nonzero. Note that * use of asprintf, getdelim, or getline will pull malloc into the link. * * Note: You can't trust FOPEN_MAX if DISABLE_DYNAMIC != 0. */#define DISABLE_DYNAMIC 0/* * As a feature, you can try to allow setvbuf calls after file operations. * Setting FLEXIBLE_SETVBUF to nonzero will cause setvbuf to try to fflush * any buffered writes or sync the file position for buffered reads. If it * is successful, the buffer change can then take place. */#define FLEXIBLE_SETVBUF 0/***********************************************************************/#if DISABLE_DYNAMIC != 0#undef malloc#undef free#define malloc(x) 0#define free(x)#endifextern FILE *__IO_list; /* For fflush. */extern FILE *_free_file_list;extern char _free_buffer_index;extern FILE _stdio_streams[FIXED_STREAMS];extern unsigned char _fixed_buffers[FIXED_BUFFERS * BUFSIZ];extern unsigned char *_alloc_stdio_buffer(size_t size);extern void _free_stdio_buffer_of_file(FILE *fp);extern void _free_stdio_stream(FILE *fp);#ifdef L__alloc_stdio_bufferunsigned char *_alloc_stdio_buffer(size_t size){ unsigned char *buf; if ((size == BUFSIZ) && (_free_buffer_index < FIXED_BUFFERS)) { buf = _fixed_buffers + ((unsigned int)_free_buffer_index) * BUFSIZ; _free_buffer_index = *buf; return buf; } return malloc(size);}#endif#ifdef L__free_stdio_buffer_of_filevoid _free_stdio_buffer_of_file(FILE *fp){ unsigned char *buf; if (!(fp->mode & __MODE_FREEBUF)) { return; } fp->mode &= ~(__MODE_FREEBUF); buf = fp->bufstart; if ((buf >= _fixed_buffers) && (buf < _fixed_buffers + (FIXED_BUFFERS * BUFSIZ))) { *buf = _free_buffer_index; _free_buffer_index = (buf - _fixed_buffers)/BUFSIZ; return; } free(buf);}#endif#ifdef L__stdio_init#if FIXED_BUFFERS < 2#error FIXED_BUFFERS must be >= 2#endif#if FIXED_BUFFERS >= UCHAR_MAX#error FIXED_BUFFERS must be < UCHAR_MAX#endif#define bufin (_fixed_buffers)#define bufout (_fixed_buffers + BUFSIZ)#define buferr (_stdio_streams[2].unbuf) /* Stderr is unbuffered */unsigned char _fixed_buffers[FIXED_BUFFERS * BUFSIZ];#if FIXED_STREAMS < 3#error FIXED_STREAMS must be >= 3#endifFILE _stdio_streams[FIXED_STREAMS] = { {bufin, bufin, 0, bufin, bufin + BUFSIZ, _stdio_streams + 1, 0, _IOFBF | __MODE_FREEFIL | __MODE_FREEBUF | __MODE_TIED }, {bufout, 0, bufout, bufout, bufout + BUFSIZ, _stdio_streams + 2, 1, _IOFBF | __MODE_FREEFIL | __MODE_FREEBUF | __MODE_TIED }, {buferr, 0, buferr, buferr, buferr + 1, NULL, 2, _IONBF | __MODE_FREEFIL }};FILE *stdin = _stdio_streams + 0;FILE *stdout = _stdio_streams + 1;FILE *stderr = _stdio_streams + 2;/* * Note: the following forces linking of the __init_stdio function if * any of the stdio functions are used since they all call fflush directly * or indirectly. */FILE *__IO_list = _stdio_streams; /* For fflush. */FILE *_free_file_list = 0;char _free_buffer_index = FIXED_BUFFERS;/* * __stdio_close_all is automatically when exiting if stdio is used. * See misc/internals/__uClibc_main.c and and stdlib/atexit.c. */void __stdio_close_all(void){ fflush(NULL); /* Files will be closed on _exit call. */}/* * __init_stdio is automatically by __uClibc_main if stdio is used. */void __init_stdio(void){#if (FIXED_BUFFERS > 2) || (FIXED_STREAMS > 3) int i;#endif#if FIXED_BUFFERS > 2 _free_buffer_index = 2; for ( i = 2 ; i < FIXED_BUFFERS ; i++ ) { _fixed_buffers[i*BUFSIZ] = i; }#endif#if FIXED_STREAMS > 3 _free_file_list = _stdio_streams + 3; for ( i = 3 ; i < FIXED_STREAMS-1 ; i++ ) { _stdio_streams[i].next = _stdio_streams + i + 1; } _stdio_streams[i].next = 0;#endif#if _IOFBF != 0 || _IOLBF != 1#error Assumption violated -- values of _IOFBF and/or _IOLBF/* This asssumption is also made in _fopen. */#endif /* stdout uses line buffering when connected to a tty. */ _stdio_streams[1].mode |= isatty(1);}#endif#ifdef L_fputcint fputc(int c, FILE *fp){ unsigned char buf[1]; *buf = (unsigned char) c; if (_uClibc_fwrite(buf, 1, fp)) { return (unsigned char) c; } return EOF;}#endif#ifdef L_fgetcint fgetc(FILE *fp){ unsigned char buf[1]; if (_uClibc_fread(buf, 1, fp)) { return *buf; } return EOF;}#endif#ifdef L_fflushint fflush(FILE *fp){ int rv; rv = 0; if (fp == NULL) { /* On NULL flush the lot. */ for (fp = __IO_list; fp; fp = fp->next) { if (WRITEABLE(fp) && fflush(fp)) { rv = EOF; } } } else if (WRITING(fp)) { /* Output buffer contents. */ _uClibc_fwrite(NULL, 0, fp); if (fp->mode & __MODE_ERR) { rv = -1; } } else if (!WRITEABLE(fp)) { /* File opened read-only!!! */ /* * According to info, glibc returns an error when the file is opened * in read-only mode. * ANSI says behavior in this case is undefined but also says you * shouldn't flush a stream you were reading from. */ __set_errno(EBADF); /* Should we set stream error indicator? */ rv = -1; } return rv;}#endif#ifdef L_fgets/* Nothing special here ... */char *fgets(char *s, int count, FILE *fp){ int ch; char *p; p = s; while (count-- > 1) { /* Guard against count arg == INT_MIN. */ ch = getc(fp); if (ch == EOF) { break; } *p++ = ch; if (ch == '\n') { break; } } if (ferror(fp) || (s == p)) { return 0; } *p = 0; return s;}#endif#ifdef L_getslink_warning (gets, "the `gets' function is dangerous and should not be used.")char *gets(char *str) /* This is an UNSAFE function! */{ /* * Strictly speaking, this implementation is incorrect as the number * of chars gets can read should be unlimited. However, I can't * imagine anyone wanting to gets() into a buffer bigger than INT_MAX. * * Besides, this function is inherently unsafe and shouldn't be used. */ return fgets(str, INT_MAX, stdin);}#endif#ifdef L_fputsint fputs(const char *str, FILE *fp){ int n; n = strlen(str); _uClibc_fwrite((const unsigned char *)str, n, fp); if (fp->mode & __MODE_ERR) { n = EOF; } return n;}#endif#ifdef L_putsint puts(const char *str){ int n; n = fputs(str, stdout); /* Let next fputc handle EOF or error. */ if (fputc('\n', stdout) == EOF) { /* Don't use putc since we want to */ return EOF; /* fflush stdout if it is line buffered. */ } return n + 1;}#endif#ifdef L_fread/* * fread will often be used to read in large chunks of data calling read() * directly can be a big win in this case. Beware also fgetc calls this * function to fill the buffer. */size_t fread(buf, size, nelm, fp)void *buf;size_t size;size_t nelm;FILE *fp;{ unsigned char *p; unsigned char *q;#warning TODO: handle possible overflow of size * nelm p = (unsigned char *) buf; q = p + (size * nelm); while ((p < q) && !EOF_OR_ERROR(fp)) { p += _uClibc_fread(p, q - p, fp); } return (p - (unsigned char *) buf)/size;}#endif#ifdef L__uClibc_freadoff_t _uClibc_fread(unsigned char *buf, off_t bytes, FILE *fp){ unsigned char *p; off_t len; if (!READABLE(fp)) { fp->mode |= __MODE_ERR; } else if (WRITING(fp)) { fflush(fp); } else if (fp->mode & stdout->mode & __MODE_TIED) { fflush(stdout); } if (EOF_OR_ERROR(fp) || (bytes <= 0)) { return 0; } p = (unsigned char *) buf; if (fp->mode & __MODE_UNGOT) { /* If we had an ungetc'd char, */ fp->mode ^= __MODE_UNGOT; /* reset the flag and return it. */ *p++ = fp->ungot; --bytes; } FROM_BUF: len = fp->bufread - fp->bufpos; if (len > bytes) { /* Enough buffered */ len = bytes; } bytes -= len; while (len--) { *p++ = *fp->bufpos++; } if (bytes && !EOF_OR_ERROR(fp)) { /* More requested but buffer empty. */ if (bytes < fp->bufend - fp->bufstart) { fp->bufpos = fp->bufread = fp->bufstart; /* Reset pointers. */ fp->bufread += _uClibc_fread(fp->bufstart, fp->bufend - fp->bufstart, fp); goto FROM_BUF; } TRY_READ: len = read(fp->fd, p, (unsigned) bytes); if (len < 0) { if (errno == EINTR) { /* We were interrupted, so try again. */ goto TRY_READ; } fp->mode |= __MODE_ERR; } else { p += len; if (len == 0) { fp->mode |= __MODE_EOF; } } } return (p - (unsigned char *)buf);}#endif#ifdef L_fwrite/* * Like fread, fwrite will often be used to write out large chunks of * data; calling write() directly can be a big win in this case. * * But first we check to see if there's space in the buffer. */size_t fwrite(buf, size, nelm, fp)const void *buf;size_t size;size_t nelm;FILE *fp;{ off_t bytes;#warning TODO: handle possible overflow for bytes bytes = size * nelm; /* How many bytes do we want? */ bytes = _uClibc_fwrite((const unsigned char *)buf, bytes, fp); return bytes/size;}#endif#ifdef L__uClibc_fwrite/* * If buf == NULL, fflush. * If buf != NULL, (fflush and) write * Returns number of chars written from fp buffer _OR_ from buf. */off_t _uClibc_fwrite(const unsigned char *buf, off_t bytes, FILE *fp){ unsigned char *p; int rv, had_newline; /* * Fail if stream isn't writable, if we were reading and get an error * changing over to write mode (ie. can't update stream position), * or if the stream was already in an error state. */ if (!WRITEABLE(fp)) { /* Fail if stream isn't writable. */ fp->mode |= __MODE_ERR; } else if (READING(fp)) { /* If read buffer isn't empty, */ fseek(fp, 0, SEEK_CUR); /* stop reading and update position. */ } else if (READABLE(fp)) { fp->bufread = fp->bufstart; /* Reset start of read buffer. */ } if (EOF_OR_ERROR(fp)) { return 0; } p = (unsigned char *)buf; if (p && (fp->bufpos + bytes <= fp->bufend)) { /* Enough buffer space? */ had_newline = 0; while (bytes--) { if (*p == '\n') { had_newline = 1; } *fp->bufpos++ = *p++; } if (fp->bufpos < fp->bufend) { /* Buffer is not full. */ fp->bufwrite = fp->bufend; if ((fp->mode & __MODE_BUF) == _IOLBF) { fp->bufwrite = fp->bufpos; if (had_newline) { goto FFLUSH; } } goto DONE; } FFLUSH: /* If we get here, either buffer is full or we need to flush anyway. */ buf = fp->bufpos - (p - (unsigned char *)buf); p = NULL; } if (!p) { /* buf == NULL means fflush */ p = fp->bufstart; bytes = fp->bufpos - p; fp->bufpos = fp->bufwrite = p; } else if (fp->bufpos > fp->bufstart) { /* If there are buffered chars, */ _uClibc_fwrite(NULL, 0, fp); /* write them. */ if (ferror(fp)) { return 0; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -