| /* 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" | 
 |  | 
 |  | 
 | #ifdef __DO_UNLOCKED | 
 |  | 
 | #ifdef __UCLIBC_MJN3_ONLY__ | 
 | #warning WISHLIST: Add option to test for undefined behavior of fflush. | 
 | #endif /* __UCLIBC_MJN3_ONLY__ */ | 
 |  | 
 | /* Even if the stream is set to user-locking, we still need to lock | 
 |  * when all (lbf) writing streams are flushed. */ | 
 |  | 
 | #define __MY_STDIO_THREADLOCK(__stream)					\ | 
 |         __UCLIBC_IO_MUTEX_CONDITIONAL_LOCK((__stream)->__lock,		\ | 
 | 	(_stdio_user_locking != 2)) | 
 |  | 
 | #define __MY_STDIO_THREADUNLOCK(__stream)				\ | 
 |         __UCLIBC_IO_MUTEX_CONDITIONAL_UNLOCK((__stream)->__lock,		\ | 
 | 	(_stdio_user_locking != 2)) | 
 |  | 
 | #if defined(__UCLIBC_HAS_THREADS__) && defined(__STDIO_BUFFERS) | 
 | void attribute_hidden _stdio_openlist_dec_use(void) | 
 | { | 
 | 	__STDIO_THREADLOCK_OPENLIST_DEL; | 
 | 	if ((_stdio_openlist_use_count == 1) && (_stdio_openlist_del_count > 0)) { | 
 | 		FILE *p = NULL; | 
 | 		FILE *n; | 
 | 		FILE *stream; | 
 |  | 
 | #ifdef __UCLIBC_MJN3_ONLY__ | 
 | #warning REMINDER: As an optimization, we could unlock after we move past the head. | 
 | #endif | 
 | 		/* Grab the openlist add lock since we might change the head of the list. */ | 
 | 		__STDIO_THREADLOCK_OPENLIST_ADD; | 
 | 		for (stream = _stdio_openlist; stream; stream = n) { | 
 | 			n = stream->__nextopen; | 
 | #ifdef __UCLIBC_MJN3_ONLY__ | 
 | #warning REMINDER: fix for nonatomic | 
 | #endif | 
 | 			if ((stream->__modeflags & (__FLAG_READONLY|__FLAG_WRITEONLY|__FLAG_FAILED_FREOPEN)) | 
 | 				== (__FLAG_READONLY|__FLAG_WRITEONLY) | 
 | 				) {		 /* The file was closed and should be removed from the list. */ | 
 | 				if (!p) { | 
 | 					_stdio_openlist = n; | 
 | 				} else { | 
 | 					p->__nextopen = n; | 
 | 				} | 
 | 				__STDIO_STREAM_FREE_FILE(stream); | 
 | 			} else { | 
 | 				p = stream; | 
 | 			} | 
 | 		} | 
 | 		__STDIO_THREADUNLOCK_OPENLIST_ADD; | 
 | 		_stdio_openlist_del_count = 0; /* Should be clean now. */ | 
 | 	} | 
 | 	--_stdio_openlist_use_count; | 
 | 	__STDIO_THREADUNLOCK_OPENLIST_DEL; | 
 | } | 
 | #endif | 
 |  | 
 | int fflush_unlocked(register FILE *stream) | 
 | { | 
 | #ifdef __STDIO_BUFFERS | 
 |  | 
 | 	int retval = 0; | 
 | #ifdef __UCLIBC_MJN3_ONLY__ | 
 | #warning REMINDER: should probably define a modeflags type | 
 | #endif | 
 | 	unsigned short bufmask = __FLAG_LBF; | 
 |  | 
 | #ifndef NDEBUG | 
 | 	if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { | 
 | 		__STDIO_STREAM_VALIDATE(stream); /* debugging only */ | 
 | 	} | 
 | #endif | 
 |  | 
 | 	if (stream == (FILE *) &_stdio_openlist) { /* Flush all lbf streams. */ | 
 | 		stream = NULL; | 
 | 		bufmask = 0; | 
 | 	} | 
 |  | 
 | 	if (!stream) {				/* Flush all (lbf) writing streams. */ | 
 |  | 
 | 		__STDIO_OPENLIST_INC_USE; | 
 |  | 
 | 		__STDIO_THREADLOCK_OPENLIST_ADD; | 
 | 		stream = _stdio_openlist; | 
 | 		__STDIO_THREADUNLOCK_OPENLIST_ADD; | 
 |  | 
 | 		while(stream) { | 
 | 			/* We only care about currently writing streams and do not want to | 
 | 			 * block trying to obtain mutexes on non-writing streams. */ | 
 | #warning fix for nonatomic | 
 | #warning unnecessary check if no threads | 
 | 			if (__STDIO_STREAM_IS_WRITING(stream)) { /* ONLY IF ATOMIC!!! */ | 
 | 				__MY_STDIO_THREADLOCK(stream); | 
 | 				/* Need to check again once we have the lock. */ | 
 | 				if (!(((stream->__modeflags | bufmask) | 
 | 					   ^ (__FLAG_WRITING|__FLAG_LBF) | 
 | 					   ) & (__FLAG_WRITING|__MASK_BUFMODE)) | 
 | 					) { | 
 | 					if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) { | 
 | 						__STDIO_STREAM_DISABLE_PUTC(stream); | 
 | 						__STDIO_STREAM_CLEAR_WRITING(stream); | 
 | 					} else { | 
 | 						retval = EOF; | 
 | 					} | 
 | 				} | 
 | 				__MY_STDIO_THREADUNLOCK(stream); | 
 | 			} | 
 | 			stream = stream->__nextopen; | 
 | 		} | 
 |  | 
 | 		__STDIO_OPENLIST_DEC_USE; | 
 |  | 
 | 	} else if (__STDIO_STREAM_IS_WRITING(stream)) { | 
 | 		if (!__STDIO_COMMIT_WRITE_BUFFER(stream)) { | 
 | 			__STDIO_STREAM_DISABLE_PUTC(stream); | 
 | 			__STDIO_STREAM_CLEAR_WRITING(stream); | 
 | 		} else { | 
 | 			retval = EOF; | 
 | 		} | 
 | 	} | 
 | #if 0 | 
 | 	else if (stream->__modeflags & (__MASK_READING|__FLAG_READONLY)) { | 
 | 		/* ANSI/ISO says behavior in this case is undefined but also says you | 
 | 		 * shouldn't flush a stream you were reading from.  As usual, glibc | 
 | 		 * caters to broken programs and simply ignores this. */ | 
 | 		__UNDEFINED_OR_NONPORTABLE; | 
 | 		__STDIO_STREAM_SET_ERROR(stream); | 
 | 		__set_errno(EBADF); | 
 | 		retval = EOF; | 
 | 	} | 
 | #endif | 
 |  | 
 | #ifndef NDEBUG | 
 | 	if ((stream != NULL) && (stream != (FILE *) &_stdio_openlist)) { | 
 | 		__STDIO_STREAM_VALIDATE(stream); /* debugging only */ | 
 | 	} | 
 | #endif | 
 |  | 
 | 	return retval; | 
 |  | 
 | #else  /* __STDIO_BUFFERS --------------------------------------- */ | 
 |  | 
 | #ifndef NDEBUG | 
 | 	if ((stream != NULL) | 
 | #ifdef __STDIO_HAS_OPENLIST | 
 | 		&& (stream != (FILE *) &_stdio_openlist) | 
 | #endif | 
 | 		) { | 
 | 		__STDIO_STREAM_VALIDATE(stream); /* debugging only */ | 
 | 	} | 
 | #endif | 
 |  | 
 | #if 0 | 
 | 	if (stream && (stream->__modeflags & (__MASK_READING|__FLAG_READONLY))) { | 
 | 		/* ANSI/ISO says behavior in this case is undefined but also says you | 
 | 		 * shouldn't flush a stream you were reading from.  As usual, glibc | 
 | 		 * caters to broken programs and simply ignores this. */ | 
 | 		__UNDEFINED_OR_NONPORTABLE; | 
 | 		__STDIO_STREAM_SET_ERROR(stream); | 
 | 		__set_errno(EBADF); | 
 | 		return EOF; | 
 | 	} | 
 | #endif | 
 |  | 
 | 	return 0; | 
 | #endif /* __STDIO_BUFFERS */ | 
 | } | 
 | libc_hidden_def(fflush_unlocked) | 
 |  | 
 | #ifndef __UCLIBC_HAS_THREADS__ | 
 | strong_alias(fflush_unlocked,fflush) | 
 | libc_hidden_def(fflush) | 
 | #endif | 
 |  | 
 | #elif defined __UCLIBC_HAS_THREADS__ | 
 |  | 
 | int fflush(register FILE *stream) | 
 | { | 
 | 	int retval; | 
 | 	__STDIO_AUTO_THREADLOCK_VAR; | 
 |  | 
 | 	if (stream | 
 | #ifdef __STDIO_HAS_OPENLIST | 
 | 		&& (stream != (FILE *) &_stdio_openlist) | 
 | #endif | 
 | 		) { | 
 |  | 
 | 		__STDIO_AUTO_THREADLOCK(stream); | 
 |  | 
 | 		retval = fflush_unlocked(stream); | 
 |  | 
 | 		__STDIO_AUTO_THREADUNLOCK(stream); | 
 | 	} else { | 
 | 		retval = fflush_unlocked(stream); | 
 | 	} | 
 |  | 
 | 	return retval; | 
 | } | 
 | libc_hidden_def(fflush) | 
 |  | 
 | #endif |