| /* 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 <features.h> | 
 |  | 
 | #ifdef __USE_GNU | 
 | #include "_stdio.h" | 
 |  | 
 |  | 
 | #ifndef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__ | 
 | #error no custom streams! | 
 | #endif | 
 |  | 
 | typedef struct { | 
 | 	size_t pos; | 
 | 	size_t len; | 
 | 	size_t eof; | 
 | 	int dynbuf; | 
 | 	unsigned char *buf; | 
 | 	FILE *fp; | 
 | } __fmo_cookie; | 
 |  | 
 | #define COOKIE ((__fmo_cookie *) cookie) | 
 |  | 
 | static ssize_t fmo_read(register void *cookie, char *buf, size_t bufsize) | 
 | { | 
 | 	size_t count = COOKIE->len - COOKIE->pos; | 
 |  | 
 | 	/* Note: 0 < bufsize < SSIZE_MAX because of _stdio_READ. */ | 
 | 	if (!count) {				/* EOF! */ | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if (bufsize > count) { | 
 | 		bufsize = count; | 
 | 	} | 
 |  | 
 | 	memcpy(buf, COOKIE->buf + COOKIE->pos, bufsize); | 
 | 	COOKIE->pos += bufsize; | 
 |  | 
 | 	return bufsize; | 
 | } | 
 |  | 
 | static ssize_t fmo_write(register void *cookie, const char *buf, size_t bufsize) | 
 | { | 
 | 	size_t count; | 
 |  | 
 | 	/* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */ | 
 |  | 
 | 	/* If appending, need to seek to end of file!!!! */ | 
 | 	if (COOKIE->fp->__modeflags & __FLAG_APPEND) { | 
 | 		COOKIE->pos = COOKIE->eof; | 
 | 	} | 
 |  | 
 | 	count = COOKIE->len - COOKIE->pos; | 
 |  | 
 | 	if (bufsize > count) { | 
 | 		bufsize = count; | 
 | 		if (count == 0) {		/* We're at the end of the buffer... */ | 
 | 			__set_errno(EFBIG); | 
 | 			return -1; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize); | 
 | 	COOKIE->pos += bufsize; | 
 |  | 
 | 	if (COOKIE->pos > COOKIE->eof) { | 
 | 		COOKIE->eof = COOKIE->pos; | 
 | 		if (bufsize < count) {	/* New eof and still room in buffer? */ | 
 | 			*(COOKIE->buf + COOKIE->pos) = 0; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	return bufsize; | 
 | } | 
 |  | 
 | /* glibc doesn't allow seeking, but it has in-buffer seeks... we don't. */ | 
 | static int fmo_seek(register void *cookie, __offmax_t *pos, int whence) | 
 | { | 
 | 	__offmax_t p = *pos; | 
 |  | 
 | 	/* Note: fseek already checks that whence is legal, so don't check here | 
 | 	 * unless debugging. */ | 
 | 	assert(((unsigned int) whence) <= 2); | 
 |  | 
 | 	if (whence != SEEK_SET) { | 
 | 		p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof; | 
 | 	} | 
 |  | 
 | 	/* Note: glibc only allows seeking in the buffer.  We'll actually restrict | 
 | 	 * to the data. */ | 
 | 	/* Check for offset < 0, offset > eof, or offset overflow... */ | 
 | 	if (((uintmax_t) p) > COOKIE->eof) { | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	COOKIE->pos = *pos = p; | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int fmo_close(register void *cookie) | 
 | { | 
 | 	if (COOKIE->dynbuf) { | 
 | 		free(COOKIE->buf); | 
 | 	} | 
 | 	free(cookie); | 
 | 	return 0; | 
 | } | 
 |  | 
 | #undef COOKIE | 
 |  | 
 | static const cookie_io_functions_t _fmo_io_funcs = { | 
 | 	fmo_read, fmo_write, fmo_seek, fmo_close | 
 | }; | 
 |  | 
 | /* TODO: If we have buffers enabled, it might be worthwile to add a pointer | 
 |  * to the FILE in the cookie and have read, write, and seek operate directly | 
 |  * on the buffer itself (ie replace the FILE buffer with the cookie buffer | 
 |  * and update FILE bufstart, etc. whenever we seek). */ | 
 |  | 
 | FILE *fmemopen(void *s, size_t len, const char *modes) | 
 | { | 
 | 	FILE *fp; | 
 | 	register __fmo_cookie *cookie; | 
 | 	size_t i; | 
 |  | 
 | 	if ((cookie = malloc(sizeof(__fmo_cookie))) != NULL) { | 
 | 		cookie->len = len; | 
 | 		cookie->eof = cookie->pos = 0; /* pos and eof adjusted below. */ | 
 | 		cookie->dynbuf = 0; | 
 | 		if (((cookie->buf = s) == NULL) && (len > 0)) { | 
 | 			if ((cookie->buf = malloc(len)) == NULL) { | 
 | 				goto EXIT_cookie; | 
 | 			} | 
 | 			cookie->dynbuf = 1; | 
 | 			*cookie->buf = 0;	/* If we're appending, treat as empty file. */ | 
 | 		} | 
 |  | 
 | #ifndef __BCC__ | 
 | 		fp = fopencookie(cookie, modes, _fmo_io_funcs); | 
 | #else | 
 | 		fp = fopencookie(cookie, modes, &_fmo_io_funcs); | 
 | #endif | 
 | 		/* Note: We don't need to worry about locking fp in the thread case | 
 | 		 * as the only possible access would be a close or flush with | 
 | 		 * nothing currently in the FILE's write buffer. */ | 
 |  | 
 | 		if (fp != NULL) { | 
 | 			cookie->fp = fp; | 
 | 			if (fp->__modeflags & __FLAG_READONLY) { | 
 | 				cookie->eof = len; | 
 | 			} | 
 | 			if ((fp->__modeflags & __FLAG_APPEND) && (len > 0)) { | 
 | 				for (i = 0 ; i < len ; i++) { | 
 | 					if (cookie->buf[i] == 0) { | 
 | 						break; | 
 | 					} | 
 | 				} | 
 | 				cookie->eof = cookie->pos = i; /* Adjust eof and pos. */ | 
 | 			} | 
 |  | 
 | 			__STDIO_STREAM_VALIDATE(fp); | 
 |  | 
 | 			return fp; | 
 | 		} | 
 | 	} | 
 |  | 
 | 	if (!s) { | 
 | 		free(cookie->buf); | 
 | 	} | 
 |  EXIT_cookie: | 
 | 	free(cookie); | 
 |  | 
 | 	return NULL; | 
 | } | 
 | #endif |