OSDN Git Service

hidden_def/hidden_proto: convert all users (I hope) termios split, add some missing...
[uclinux-h8/uClibc.git] / libc / stdio / open_memstream.c
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 "_stdio.h"
9
10 libc_hidden_proto(memcpy)
11 libc_hidden_proto(memset)
12 libc_hidden_proto(fopencookie)
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
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
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, leastlen - COOKIE->eof, 0); /* 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, leastlen - COOKIE->eof, 0);
108                 *COOKIE->sizeloc = COOKIE->eof;
109         }
110
111         return 0;
112 }
113
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
131 FILE *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         if (cookie->buf != NULL) {
160                 free(cookie->buf);
161         }
162  EXIT_cookie:
163         free(cookie);
164
165         return NULL;
166 }
167 libc_hidden_proto(open_memstream)
168 libc_hidden_def(open_memstream)