]>
Commit | Line | Data |
---|---|---|
28f540f4 RM |
1 | /* Copyright (C) 1991, 1992, 1994 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. | |
3 | ||
4 | The GNU C Library is free software; you can redistribute it and/or | |
5 | modify it under the terms of the GNU Library General Public License as | |
6 | published by the Free Software Foundation; either version 2 of the | |
7 | License, or (at your option) any later version. | |
8 | ||
9 | The GNU C Library is distributed in the hope that it will be useful, | |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | Library General Public License for more details. | |
13 | ||
14 | You should have received a copy of the GNU Library General Public | |
15 | License along with the GNU C Library; see the file COPYING.LIB. If | |
16 | not, write to the Free Software Foundation, Inc., 675 Mass Ave, | |
17 | Cambridge, MA 02139, USA. */ | |
18 | ||
19 | #include <ansidecl.h> | |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | #include <string.h> | |
23 | ||
24 | struct memstream_info | |
25 | { | |
26 | char **buffer; | |
27 | size_t *bufsize; | |
28 | }; | |
29 | ||
30 | /* Enlarge STREAM's buffer. */ | |
31 | static void | |
32 | DEFUN(enlarge_buffer, (stream, c), | |
33 | register FILE *stream AND int c) | |
34 | { | |
35 | struct memstream_info *info = (struct memstream_info *) stream->__cookie; | |
36 | size_t need; | |
37 | ||
38 | if (stream->__put_limit != stream->__buffer) | |
39 | /* Record how much has actually been written into the buffer. */ | |
40 | *info->bufsize = stream->__bufp - stream->__buffer; | |
41 | ||
42 | if (stream->__target != -1 | |
43 | && stream->__target > *info->bufsize) | |
44 | /* Our target (where the buffer maps to) is always zero except when | |
45 | the user just did a SEEK_END fseek. If he sought within the | |
46 | buffer, we need do nothing and will zero the target below. If he | |
47 | sought past the end of the object, grow and zero-fill the buffer | |
48 | up to the target address. */ | |
49 | need = stream->__target; | |
50 | else | |
51 | need = *info->bufsize; | |
52 | ||
53 | /* We always need an extra character in the buffer. Either we are | |
54 | writing C, or we are flushing and need to write a NUL terminator. */ | |
55 | ++need; | |
56 | ||
57 | if (stream->__bufsize < need) | |
58 | { | |
59 | /* Enlarge the buffer. */ | |
60 | char *newbuf; | |
61 | size_t newsize; | |
62 | if (stream->__bufsize * 2 < need) | |
63 | newsize = need; | |
64 | else | |
65 | newsize = stream->__bufsize * 2; | |
66 | newbuf = (char *) realloc ((PTR) stream->__buffer, newsize); | |
67 | if (newbuf == NULL) | |
68 | { | |
69 | stream->__error = 1; | |
70 | return; | |
71 | } | |
72 | *info->buffer = stream->__buffer = newbuf; | |
73 | stream->__bufsize = newsize; | |
74 | } | |
75 | ||
76 | stream->__target = stream->__offset = 0; | |
77 | stream->__get_limit = stream->__bufp = stream->__buffer + *info->bufsize; | |
78 | stream->__put_limit = stream->__buffer + stream->__bufsize; | |
79 | ||
80 | need -= stream->__bufp - stream->__buffer + 1; | |
81 | if (need > 0) | |
82 | { | |
83 | /* We are extending the buffer after an fseek; zero-fill new space. */ | |
84 | bzero (stream->__bufp, need); | |
85 | stream->__bufp += need; | |
86 | } | |
87 | ||
88 | if (c != EOF) | |
89 | *stream->__bufp++ = (unsigned char) c; | |
90 | else | |
91 | *stream->__bufp = '\0'; | |
92 | } | |
93 | ||
94 | /* Seek function for memstreams. | |
95 | There is no external state to munge. */ | |
96 | ||
97 | static int | |
98 | DEFUN(seek, (cookie, pos, whence), | |
99 | PTR cookie AND fpos_t *pos AND int whence) | |
100 | { | |
101 | switch (whence) | |
102 | { | |
103 | case SEEK_SET: | |
104 | case SEEK_CUR: | |
105 | return 0; | |
106 | ||
107 | case SEEK_END: | |
108 | /* Return the position relative to the end of the object. | |
109 | fseek has just flushed us, so the info is consistent. */ | |
110 | *pos += *((struct memstream_info *) cookie)->bufsize; | |
111 | return 0; | |
112 | ||
113 | default: | |
114 | __libc_fatal ("memstream::seek called with bogus WHENCE\n"); | |
115 | return -1; | |
116 | } | |
117 | } | |
118 | ||
119 | static int | |
120 | DEFUN(free_info, (cookie), PTR cookie) | |
121 | { | |
122 | #if 0 | |
123 | struct memstream_info *info = (struct memstream_info *) cookie; | |
124 | char *buf; | |
125 | ||
126 | buf = (char *) realloc ((PTR) *info->buffer, *info->bufsize); | |
127 | if (buf != NULL) | |
128 | *info->buffer = buf; | |
129 | #endif | |
130 | ||
131 | free (cookie); | |
132 | ||
133 | return 0; | |
134 | } | |
135 | \f | |
136 | /* Open a stream that writes into a malloc'd buffer that is expanded as | |
137 | necessary. *BUFLOC and *SIZELOC are updated with the buffer's location | |
138 | and the number of characters written on fflush or fclose. */ | |
139 | FILE * | |
140 | DEFUN(open_memstream, (bufloc, sizeloc), | |
141 | char **bufloc AND size_t *sizeloc) | |
142 | { | |
143 | FILE *stream; | |
144 | struct memstream_info *info; | |
145 | ||
146 | if (bufloc == NULL || sizeloc == NULL) | |
147 | { | |
148 | errno = EINVAL; | |
149 | return NULL; | |
150 | } | |
151 | ||
152 | stream = fmemopen ((char *) NULL, BUFSIZ, "w+"); | |
153 | if (stream == NULL) | |
154 | return NULL; | |
155 | ||
156 | info = (struct memstream_info *) malloc (sizeof (struct memstream_info)); | |
157 | if (info == NULL) | |
158 | { | |
159 | int save = errno; | |
160 | (void) fclose (stream); | |
161 | errno = save; | |
162 | return NULL; | |
163 | } | |
164 | ||
165 | stream->__room_funcs.__output = enlarge_buffer; | |
166 | stream->__io_funcs.__seek = seek; | |
167 | stream->__io_funcs.__close = free_info; | |
168 | stream->__cookie = (PTR) info; | |
169 | stream->__userbuf = 1; | |
170 | ||
171 | info->buffer = bufloc; | |
172 | info->bufsize = sizeloc; | |
173 | ||
174 | *bufloc = stream->__buffer; | |
175 | ||
176 | return stream; | |
177 | } |