OSDN Git Service

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