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 #define COOKIE ((__oms_cookie *) cookie)
19
20 typedef struct {
21 char *buf;
22 size_t len;
23 size_t pos;
24 size_t eof;
25 char **bufloc;
26 size_t *sizeloc;
27 } __oms_cookie;
28
29 /* Nothing to do here, as memstreams are write-only. */
30 /* static ssize_t oms_read(void *cookie, char *buf, size_t bufsize) */
31 /* { */
32 /* } */
33
oms_write(register void * cookie,const char * buf,size_t bufsize)34 static ssize_t oms_write(register void *cookie, const char *buf, size_t bufsize)
35 {
36 register char *newbuf;
37 size_t count;
38
39 /* Note: we already know bufsize < SSIZE_MAX... */
40
41 count = COOKIE->len - COOKIE->pos - 1;
42 assert(COOKIE->pos < COOKIE->len); /* Always nul-terminate! */
43
44 if (bufsize > count) {
45 newbuf = realloc(COOKIE->buf, COOKIE->len + bufsize - count);
46 if (newbuf) {
47 *COOKIE->bufloc = COOKIE->buf = newbuf;
48 COOKIE->len += (bufsize - count);
49 } else {
50 bufsize = count;
51 if (count == 0) {
52 __set_errno(EFBIG); /* TODO: check glibc errno setting... */
53 return -1;
54 }
55 }
56 }
57
58 memcpy(COOKIE->buf + COOKIE->pos, buf, bufsize);
59 COOKIE->pos += bufsize;
60
61 if (COOKIE->pos > COOKIE->eof) {
62 *COOKIE->sizeloc = COOKIE->eof = COOKIE->pos;
63 COOKIE->buf[COOKIE->eof] = 0; /* Need to nul-terminate. */
64 }
65
66 return bufsize;
67 }
68
oms_seek(register void * cookie,__offmax_t * pos,int whence)69 static int oms_seek(register void *cookie, __offmax_t *pos, int whence)
70 {
71 __offmax_t p = *pos;
72 register char *buf;
73 size_t leastlen;
74
75 /* Note: fseek already checks that whence is legal, so don't check here
76 * unless debugging. */
77 assert(((unsigned int) whence) <= 2);
78
79 if (whence != SEEK_SET) {
80 p += (whence == SEEK_CUR) ? COOKIE->pos : /* SEEK_END */ COOKIE->eof;
81 }
82
83 /* Note: glibc only allows seeking in the buffer. We'll actually restrict
84 * to the data. */
85 /* Check for offset < 0, offset >= too big (need nul), or overflow... */
86 if (((uintmax_t) p) >= SIZE_MAX - 1) {
87 return -1;
88 }
89
90 leastlen = ((size_t) p) + 1; /* New pos + 1 for nul if necessary. */
91
92 if (leastlen >= COOKIE->len) { /* Need to grow buffer... */
93 buf = realloc(COOKIE->buf, leastlen);
94 if (buf) {
95 *COOKIE->bufloc = COOKIE->buf = buf;
96 COOKIE->len = leastlen;
97 memset(buf + COOKIE->eof, 0, leastlen - COOKIE->eof); /* 0-fill */
98 } else {
99 /* TODO: check glibc errno setting... */
100 return -1;
101 }
102 }
103
104 *pos = COOKIE->pos = --leastlen;
105
106 if (leastlen > COOKIE->eof) {
107 memset(COOKIE->buf + COOKIE->eof, 0, leastlen - COOKIE->eof);
108 *COOKIE->sizeloc = COOKIE->eof;
109 }
110
111 return 0;
112 }
113
oms_close(void * cookie)114 static int oms_close(void *cookie)
115 {
116 free(cookie);
117 return 0;
118 }
119
120 #undef COOKIE
121
122 static const cookie_io_functions_t _oms_io_funcs = {
123 NULL, oms_write, oms_seek, oms_close
124 };
125
126 /* TODO: If we have buffers enabled, it might be worthwile to add a pointer
127 * to the FILE in the cookie and operate directly on the buffer itself
128 * (ie replace the FILE buffer with the cookie buffer and update FILE bufstart,
129 * etc. whenever we seek). */
130
open_memstream(char ** bufloc,size_t * sizeloc)131 FILE *open_memstream(char **bufloc, size_t *sizeloc)
132 {
133 register __oms_cookie *cookie;
134 register FILE *fp;
135
136 if ((cookie = malloc(sizeof(__oms_cookie))) != NULL) {
137 if ((cookie->buf = malloc(cookie->len = BUFSIZ)) == NULL) {
138 goto EXIT_cookie;
139 }
140 *cookie->buf = 0; /* Set nul terminator for buffer. */
141 *(cookie->bufloc = bufloc) = cookie->buf;
142 *(cookie->sizeloc = sizeloc) = cookie->eof = cookie->pos = 0;
143
144 #ifndef __BCC__
145 fp = fopencookie(cookie, "w", _oms_io_funcs);
146 #else
147 fp = fopencookie(cookie, "w", &_oms_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 __STDIO_STREAM_VALIDATE(fp);
155 return fp;
156 }
157 }
158
159 free(cookie->buf);
160 EXIT_cookie:
161 free(cookie);
162
163 return NULL;
164 }
165 libc_hidden_def(open_memstream)
166 #endif
167