1 /* Copyright (C) 2004       Manuel Novoa III    <mjn3@codepoet.org>
2  *
3  * GNU Library General Public License (LGPL) version 2 or later.
4  *
5  * Dedicated to Toni.  See uClibc/DEDICATION.mjn3 for details.
6  */
7 
8 #include <features.h>
9 
10 #ifdef __USE_GNU
11 #include "_stdio.h"
12 
13 
14 #ifndef __UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__
15 #error no custom streams!
16 #endif
17 
18 typedef struct {
19 	size_t pos;
20 	size_t len;
21 	size_t eof;
22 	int dynbuf;
23 	unsigned char *buf;
24 	FILE *fp;
25 } __fmo_cookie;
26 
27 #define COOKIE ((__fmo_cookie *) cookie)
28 
fmo_read(register void * cookie,char * buf,size_t bufsize)29 static ssize_t fmo_read(register void *cookie, char *buf, size_t bufsize)
30 {
31 	size_t count = COOKIE->len - COOKIE->pos;
32 
33 	/* Note: 0 < bufsize < SSIZE_MAX because of _stdio_READ. */
34 	if (!count) {				/* EOF! */
35 		return 0;
36 	}
37 
38 	if (bufsize > count) {
39 		bufsize = count;
40 	}
41 
42 	memcpy(buf, COOKIE->buf + COOKIE->pos, bufsize);
43 	COOKIE->pos += bufsize;
44 
45 	return bufsize;
46 }
47 
fmo_write(register void * cookie,const char * buf,size_t bufsize)48 static ssize_t fmo_write(register void *cookie, const char *buf, size_t bufsize)
49 {
50 	size_t count;
51 
52 	/* Note: bufsize < SSIZE_MAX because of _stdio_WRITE. */
53 
54 	/* If appending, need to seek to end of file!!!! */
55 	if (COOKIE->fp->__modeflags & __FLAG_APPEND) {
56 		COOKIE->pos = COOKIE->eof;
57 	}
58 
59 	count = COOKIE->len - COOKIE->pos;
60 
61 	if (bufsize > count) {
62 		bufsize = count;
63 		if (count == 0) {		/* We're at the end of the buffer... */
64 			__set_errno(EFBIG);
65 			return -1;
66 		}
67 	}
68 
69 	memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize);
70 	COOKIE->pos += bufsize;
71 
72 	if (COOKIE->pos > COOKIE->eof) {
73 		COOKIE->eof = COOKIE->pos;
74 		if (bufsize < count) {	/* New eof and still room in buffer? */
75 			*(COOKIE->buf + COOKIE->pos) = 0;
76 		}
77 	}
78 
79 	return bufsize;
80 }
81 
82 /* glibc doesn't allow seeking, but it has in-buffer seeks... we don't. */
fmo_seek(register void * cookie,__offmax_t * pos,int whence)83 static int fmo_seek(register void *cookie, __offmax_t *pos, int whence)
84 {
85 	__offmax_t p = *pos;
86 
87 	/* Note: fseek already checks that whence is legal, so don't check here
88 	 * unless debugging. */
89 	assert(((unsigned int) whence) <= 2);
90 
91 	if (whence != SEEK_SET) {
92 		p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof;
93 	}
94 
95 	/* Note: glibc only allows seeking in the buffer.  We'll actually restrict
96 	 * to the data. */
97 	/* Check for offset < 0, offset > eof, or offset overflow... */
98 	if (((uintmax_t) p) > COOKIE->eof) {
99 		return -1;
100 	}
101 
102 	COOKIE->pos = *pos = p;
103 	return 0;
104 }
105 
fmo_close(register void * cookie)106 static int fmo_close(register void *cookie)
107 {
108 	if (COOKIE->dynbuf) {
109 		free(COOKIE->buf);
110 	}
111 	free(cookie);
112 	return 0;
113 }
114 
115 #undef COOKIE
116 
117 static const cookie_io_functions_t _fmo_io_funcs = {
118 	fmo_read, fmo_write, fmo_seek, fmo_close
119 };
120 
121 /* TODO: If we have buffers enabled, it might be worthwile to add a pointer
122  * to the FILE in the cookie and have read, write, and seek operate directly
123  * on the buffer itself (ie replace the FILE buffer with the cookie buffer
124  * and update FILE bufstart, etc. whenever we seek). */
125 
fmemopen(void * s,size_t len,const char * modes)126 FILE *fmemopen(void *s, size_t len, const char *modes)
127 {
128 	FILE *fp;
129 	register __fmo_cookie *cookie;
130 	size_t i;
131 
132 	if ((cookie = malloc(sizeof(__fmo_cookie))) != NULL) {
133 		cookie->len = len;
134 		cookie->eof = cookie->pos = 0; /* pos and eof adjusted below. */
135 		cookie->dynbuf = 0;
136 		if (((cookie->buf = s) == NULL) && (len > 0)) {
137 			if ((cookie->buf = malloc(len)) == NULL) {
138 				goto EXIT_cookie;
139 			}
140 			cookie->dynbuf = 1;
141 			*cookie->buf = 0;	/* If we're appending, treat as empty file. */
142 		}
143 
144 #ifndef __BCC__
145 		fp = fopencookie(cookie, modes, _fmo_io_funcs);
146 #else
147 		fp = fopencookie(cookie, modes, &_fmo_io_funcs);
148 #endif
149 		/* Note: We don't need to worry about locking fp in the thread case
150 		 * as the only possible access would be a close or flush with
151 		 * nothing currently in the FILE's write buffer. */
152 
153 		if (fp != NULL) {
154 			cookie->fp = fp;
155 			if (fp->__modeflags & __FLAG_READONLY) {
156 				cookie->eof = len;
157 			}
158 			if ((fp->__modeflags & __FLAG_APPEND) && (len > 0)) {
159 				for (i = 0 ; i < len ; i++) {
160 					if (cookie->buf[i] == 0) {
161 						break;
162 					}
163 				}
164 				cookie->eof = cookie->pos = i; /* Adjust eof and pos. */
165 			}
166 
167 			__STDIO_STREAM_VALIDATE(fp);
168 
169 			return fp;
170 		}
171 	}
172 
173 	if (!s) {
174 		free(cookie->buf);
175 	}
176  EXIT_cookie:
177 	free(cookie);
178 
179 	return NULL;
180 }
181 #endif
182