/* Copyright (C) 2004 Manuel Novoa III <mjn3@codepoet.org> * * GNU Library General Public License (LGPL) version 2 or later. * * Dedicated to Toni. See uClibc/DEDICATION.mjn3 for details. */ #include "_stdio.h" /* This is pretty much straight from uClibc, but with one important * difference. * * We now initialize the locking flag to user locking instead of * auto locking (i.e. FSETLOCKING_BYCALLER vs FSETLOCKING_INTERNAL). * This greatly benefits non-threading applications linked to a * shared thread-enabled library. In threading applications, we * walk the stdio open file list and reset the locking mode * appropriately when the thread subsystem is initialized. */ /**********************************************************************/ #ifdef __UCLIBC_HAS_WCHAR__ #define __STDIO_FILE_INIT_WUNGOT { 0, 0 }, #else #define __STDIO_FILE_INIT_WUNGOT #endif #ifdef __STDIO_GETC_MACRO # define __STDIO_FILE_INIT_BUFGETC(x) x, #else # define __STDIO_FILE_INIT_BUFGETC(x) #endif #ifdef __STDIO_PUTC_MACRO # define __STDIO_FILE_INIT_BUFPUTC(x) x, #else # define __STDIO_FILE_INIT_BUFPUTC(x) #endif #ifdef __STDIO_HAS_OPENLIST #define __STDIO_FILE_INIT_NEXT(next) (next), #else #define __STDIO_FILE_INIT_NEXT(next) #endif #ifdef __STDIO_BUFFERS #define __STDIO_FILE_INIT_BUFFERS(buf,bufsize) \ (buf), (buf)+(bufsize), (buf), (buf), #else #define __STDIO_FILE_INIT_BUFFERS(buf,bufsize) #endif #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ #define __STDIO_FILE_INIT_CUSTOM_STREAM(stream) \ &((stream).__filedes), { _cs_read, _cs_write, _cs_seek, _cs_close }, #else #define __STDIO_FILE_INIT_CUSTOM_STREAM(stream) #endif #ifdef __STDIO_MBSTATE #define __STDIO_FILE_INIT_MBSTATE \ { 0, 0 }, #else #define __STDIO_FILE_INIT_MBSTATE #endif #ifdef __UCLIBC_HAS_XLOCALE__ #define __STDIO_FILE_INIT_UNUSED \ NULL, #else #define __STDIO_FILE_INIT_UNUSED #endif #ifdef __UCLIBC_HAS_THREADS__ #ifdef __USE_STDIO_FUTEXES__ #define __STDIO_FILE_INIT_THREADSAFE \ 2, _LIBC_LOCK_RECURSIVE_INITIALIZER, #else #define __STDIO_FILE_INIT_THREADSAFE \ 2, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP, #endif #else #define __STDIO_FILE_INIT_THREADSAFE #endif #define __STDIO_INIT_FILE_STRUCT(stream, flags, filedes, next, buf, bufsize) \ { (flags), \ { 0, 0 }, /* ungot[2] (no wchar) or ungot_width[2] (wchar)*/ \ (filedes), \ __STDIO_FILE_INIT_BUFFERS(buf,bufsize) \ __STDIO_FILE_INIT_BUFGETC((buf)) \ __STDIO_FILE_INIT_BUFPUTC((buf)) \ __STDIO_FILE_INIT_NEXT(next) \ __STDIO_FILE_INIT_CUSTOM_STREAM(stream) \ __STDIO_FILE_INIT_WUNGOT \ __STDIO_FILE_INIT_MBSTATE \ __STDIO_FILE_INIT_UNUSED \ __STDIO_FILE_INIT_THREADSAFE \ } /* TODO: builtin buf */ /**********************************************************************/ /* First we need the standard files. */ #ifdef __STDIO_BUFFERS static unsigned char _fixed_buffers[2 * BUFSIZ]; #endif static FILE _stdio_streams[] = { __STDIO_INIT_FILE_STRUCT(_stdio_streams[0], \ __FLAG_LBF|__FLAG_READONLY, \ 0, \ _stdio_streams + 1, \ _fixed_buffers, \ BUFSIZ ), __STDIO_INIT_FILE_STRUCT(_stdio_streams[1], \ __FLAG_LBF|__FLAG_WRITEONLY, \ 1, \ _stdio_streams + 2, \ _fixed_buffers + BUFSIZ, \ BUFSIZ ), __STDIO_INIT_FILE_STRUCT(_stdio_streams[2], \ __FLAG_NBF|__FLAG_WRITEONLY, \ 2, \ NULL, \ NULL, \ 0 ) }; FILE *stdin = _stdio_streams; FILE *stdout = _stdio_streams + 1; FILE *stderr = _stdio_streams + 2; #ifdef __STDIO_GETC_MACRO FILE *__stdin = _stdio_streams; /* For getchar() macro. */ #endif #ifdef __STDIO_PUTC_MACRO FILE *__stdout = _stdio_streams + 1; /* For putchar() macro. */ /* FILE *__stderr = _stdio_streams + 2; */ #endif /**********************************************************************/ #ifdef __STDIO_HAS_OPENLIST /* In certain configurations, we need to keep a list of open files. * 1) buffering enabled - We need to initialize the buffering mode * (full or line buffering) of stdin and stdout. We also * need to flush all write buffers prior to normal termination. * 2) custom streams - Even if we aren't buffering in the library * itself, we need to fclose() all custom streams when terminating * so that any special cleanup is done. * 3) threads enabled - We need to be able to reset the locking mode * of all open streams when the threading system is initialized. */ FILE *_stdio_openlist = _stdio_streams; # ifdef __UCLIBC_HAS_THREADS__ __UCLIBC_IO_MUTEX_INIT(_stdio_openlist_add_lock); # ifdef __STDIO_BUFFERS __UCLIBC_IO_MUTEX_INIT(_stdio_openlist_del_lock); volatile int _stdio_openlist_use_count = 0; int _stdio_openlist_del_count = 0; # endif # endif #endif /**********************************************************************/ #ifdef __UCLIBC_HAS_THREADS__ /* 2 if threading not initialized and 0 otherwise; */ int _stdio_user_locking = 2; #ifndef __USE_STDIO_FUTEXES__ void attribute_hidden __stdio_init_mutex(__UCLIBC_MUTEX_TYPE *m) { const __UCLIBC_MUTEX_STATIC(__stdio_mutex_initializer, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP); memcpy(m, &__stdio_mutex_initializer, sizeof(__stdio_mutex_initializer)); } #endif #endif /**********************************************************************/ /* We assume here that we are the only remaining thread. */ void attribute_hidden _stdio_term(void) { #if defined(__STDIO_BUFFERS) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) register FILE *ptr; #ifdef __UCLIBC_HAS_THREADS__ /* First, make sure the open file list is unlocked. If it was * locked, then I suppose there is a chance that a pointer in the * chain might be corrupt due to a partial store. */ STDIO_INIT_MUTEX(_stdio_openlist_add_lock); #warning check #ifdef __STDIO_BUFFERS STDIO_INIT_MUTEX(_stdio_openlist_del_lock); #endif /* Next we need to worry about the streams themselves. If a stream * is currently locked, then it may be in an invalid state. So we * 'disable' it in case a custom stream is stacked on top of it. * Then we reinitialize the locks. */ for (ptr = _stdio_openlist ; ptr ; ptr = ptr->__nextopen ) { if (__STDIO_ALWAYS_THREADTRYLOCK_CANCEL_UNSAFE(ptr)) { /* The stream is already locked, so we don't want to touch it. * However, if we have custom streams, we can't just close it * or leave it locked since a custom stream may be stacked * on top of it. So we do unlock it, while also disabling it. */ ptr->__modeflags = (__FLAG_READONLY|__FLAG_WRITEONLY); __STDIO_STREAM_DISABLE_GETC(ptr); __STDIO_STREAM_DISABLE_PUTC(ptr); __STDIO_STREAM_INIT_BUFREAD_BUFPOS(ptr); } ptr->__user_locking = 1; /* Set locking mode to "by caller". */ STDIO_INIT_MUTEX(ptr->__lock); /* Shouldn't be necessary, but... */ } #endif /* Finally, flush all writing streams and shut down all custom streams. * NOTE: We assume that any stacking by custom streams is done on top * of streams previously allocated, and hence further down the * list. Otherwise we have no way of knowing the order in which * to shut them down. * Remember that freopen() counts as a new allocation here, even * though the stream is reused. That's because it moves the * stream to the head of the list. */ for (ptr = _stdio_openlist ; ptr ; ptr = ptr->__nextopen ) { #ifdef __STDIO_BUFFERS /* Write any pending buffered chars. */ if (__STDIO_STREAM_IS_WRITING(ptr)) { __STDIO_COMMIT_WRITE_BUFFER(ptr); } #endif #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ /* Actually close all custom streams to perform any special cleanup. */ if (ptr->__cookie != &ptr->__filedes) { __CLOSE(ptr); } #endif } #endif } #if defined __STDIO_BUFFERS || !defined __UCLIBC__ void attribute_hidden _stdio_init(void) { #ifdef __STDIO_BUFFERS int old_errno = errno; /* stdin and stdout uses line buffering when connected to a tty. */ if (!isatty(0)) _stdio_streams[0].__modeflags ^= __FLAG_LBF; if (!isatty(1)) _stdio_streams[1].__modeflags ^= __FLAG_LBF; __set_errno(old_errno); #endif #ifndef __UCLIBC__ /* _stdio_term is done automatically when exiting if stdio is used. * See misc/internals/__uClibc_main.c and and stdlib/atexit.c. */ atexit(_stdio_term); #endif } #endif /**********************************************************************/ #if !(__MASK_READING & __FLAG_UNGOT) #error Assumption violated about __MASK_READING and __FLAG_UNGOT #endif #ifdef __UCLIBC_HAS_THREADS__ #include <pthread.h> #endif #ifndef NDEBUG void attribute_hidden _stdio_validate_FILE(const FILE *stream) { #ifdef __UCLIBC_HAS_THREADS__ assert(((unsigned int)(stream->__user_locking)) <= 2); #endif #warning Define a constant for minimum possible valid __filedes? assert(stream->__filedes >= -3); if (stream->__filedes < 0) { /* assert((stream->__filedes != -1) */ /* #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ */ /* || (stream->__cookie == &stream->__filedes) /\* custom *\/ */ /* #endif */ /* ); */ /* assert((stream->__filedes == -1) || __STDIO_STREAM_IS_FBF(stream)); */ assert(!__STDIO_STREAM_IS_FAKE_VSNPRINTF(stream) || __STDIO_STREAM_IS_NARROW(stream)); assert(!__STDIO_STREAM_IS_FAKE_VSSCANF(stream) || __STDIO_STREAM_IS_NARROW(stream)); #ifdef __STDIO_STREAM_IS_FAKE_VSWPRINTF assert(!__STDIO_STREAM_IS_FAKE_VSWPRINTF(stream) || __STDIO_STREAM_IS_WIDE(stream)); #endif #ifdef __STDIO_STREAM_IS_FAKE_VSWSCANF assert(!__STDIO_STREAM_IS_FAKE_VSWSCANF(stream) || __STDIO_STREAM_IS_WIDE(stream)); #endif } #ifdef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ if (stream->__cookie != &stream->__filedes) { /* custom */ assert(stream->__filedes == -1); } #endif /* Can not be both narrow and wide oriented at the same time. */ assert(!(__STDIO_STREAM_IS_NARROW(stream) && __STDIO_STREAM_IS_WIDE(stream))); /* The following impossible case is used to disable a stream. */ if ((stream->__modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY)) == (__FLAG_READONLY|__FLAG_WRITEONLY) ) { assert(stream->__modeflags == (__FLAG_READONLY|__FLAG_WRITEONLY)); assert(stream->__filedes == -1); #ifdef __STDIO_BUFFERS assert(stream->__bufpos == stream->__bufstart); assert(stream->__bufread == stream->__bufstart); # ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ assert(stream->__bufputc_u == stream->__bufstart); # endif # ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ assert(stream->__bufgetc_u == stream->__bufstart); # endif #endif } if (__STDIO_STREAM_IS_READONLY(stream)) { /* assert(!__STDIO_STREAM_IS_WRITEONLY(stream)); */ assert(!__STDIO_STREAM_IS_WRITING(stream)); if (stream->__modeflags & __FLAG_UNGOT) { assert(((unsigned)(stream->__ungot[1])) <= 1); assert(!__FEOF_UNLOCKED(stream)); } } if (__STDIO_STREAM_IS_WRITEONLY(stream)) { /* assert(!__STDIO_STREAM_IS_READONLY(stream)); */ assert(!__STDIO_STREAM_IS_READING(stream)); assert(!(stream->__modeflags & __FLAG_UNGOT)); } if (__STDIO_STREAM_IS_NBF(stream)) { /* We require that all non buffered streams have no buffer. */ assert(!__STDIO_STREAM_BUFFER_SIZE(stream)); } assert((stream->__modeflags & __MASK_BUFMODE) <= __FLAG_NBF); #ifdef __STDIO_BUFFERS /* Ensure __bufstart <= __bufpos <= __bufend. */ assert(stream->__bufpos >= stream->__bufstart); assert(stream->__bufpos <= stream->__bufend); /* Ensure __bufstart <= __bufread <= __bufend. */ assert(stream->__bufread >= stream->__bufstart); assert(stream->__bufread <= stream->__bufend); #endif /* If EOF, then we must have no buffered readable or ungots. */ if (__FEOF_UNLOCKED(stream)) { #ifdef __STDIO_BUFFERS assert(stream->__bufpos == stream->__bufread); #endif assert(!(stream->__modeflags & __FLAG_UNGOT)); } if (!__STDIO_STREAM_IS_WRITING(stream)) { #ifdef __STDIO_BUFFERS /* If not writing, then putc macro must be disabled. */ # ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ assert(stream->__bufputc_u == stream->__bufstart); # endif #endif } if (!__STDIO_STREAM_IS_READING(stream)) { /* If not reading, then can not have ungots. */ assert(!(stream->__modeflags & __FLAG_UNGOT)); #ifdef __STDIO_BUFFERS /* Ensure __bufread == __bufstart. */ assert(stream->__bufread == stream->__bufstart); /* If not reading, then getc macro must be disabled. */ # ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ assert(stream->__bufgetc_u == stream->__bufstart); # endif #endif } if (__STDIO_STREAM_IS_READING(stream)) { assert(!__STDIO_STREAM_IS_WRITING(stream)); #ifdef __STDIO_BUFFERS /* Ensure __bufpos <= __bufread. */ assert(stream->__bufpos <= stream->__bufread); /* Ensure __bufgetc_u is valid. */ # ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ assert(stream->__bufgetc_u >= stream->__bufstart); assert(stream->__bufgetc_u <= stream->__bufread); # endif #endif } if (__STDIO_STREAM_IS_WRITING(stream)) { assert(!__STDIO_STREAM_IS_READING(stream)); #ifdef __STDIO_BUFFERS # ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ assert(stream->__bufputc_u >= stream->__bufstart); assert(stream->__bufputc_u <= stream->__bufend); # endif #endif } /* If have an ungotten char, then getc (and putc) must be disabled. */ /* Also, wide streams must have the getc/putc macros disabled. */ if ((stream->__modeflags & __FLAG_UNGOT) || __STDIO_STREAM_IS_WIDE(stream) ) { #ifdef __STDIO_BUFFERS # ifdef __UCLIBC_HAS_STDIO_PUTC_MACRO__ assert(stream->__bufputc_u == stream->__bufstart); # endif # ifdef __UCLIBC_HAS_STDIO_GETC_MACRO__ assert(stream->__bufgetc_u == stream->__bufstart); # endif #endif } /* TODO -- filepos? ungot_width? filedes? nextopen? */ } #endif