blob: 8b552c7963a3a924d57287e7115d5b5acfe60755 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* 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
20typedef 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
34static 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
69static 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
114static int oms_close(void *cookie)
115{
116 free(cookie);
117 return 0;
118}
119
120#undef COOKIE
121
122static 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
131FILE *open_memstream(char **__restrict bufloc, size_t *__restrict 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}
165libc_hidden_def(open_memstream)
166#endif