common: Fix recent commit 55656208.
[gnupg.git] / common / membuf.c
1 /* membuf.c - A simple implementation of a dynamic buffer.
2  * Copyright (C) 2001, 2003, 2009, 2011 Free Software Foundation, Inc.
3  * Copyright (C) 2013 Werner Koch
4  *
5  * This file is part of GnuPG.
6  *
7  * This file is free software; you can redistribute it and/or modify
8  * it under the terms of either
9  *
10  *   - the GNU Lesser General Public License as published by the Free
11  *     Software Foundation; either version 3 of the License, or (at
12  *     your option) any later version.
13  *
14  * or
15  *
16  *   - the GNU General Public License as published by the Free
17  *     Software Foundation; either version 2 of the License, or (at
18  *     your option) any later version.
19  *
20  * or both in parallel, as here.
21  *
22  * This file is distributed in the hope that it will be useful,
23  * but WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  * GNU General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with this program; if not, see <http://www.gnu.org/licenses/>.
29  */
30
31 #include <config.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <stdarg.h>
35
36 #include "membuf.h"
37
38 #include "util.h"
39
40
41 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
42    create a buffer, put_membuf to append bytes and get_membuf to
43    release and return the buffer.  Allocation errors are detected but
44    only returned at the final get_membuf(), this helps not to clutter
45    the code with out of core checks.  */
46
47 void
48 init_membuf (membuf_t *mb, int initiallen)
49 {
50   mb->len = 0;
51   mb->size = initiallen;
52   mb->out_of_core = 0;
53   mb->buf = xtrymalloc (initiallen);
54   if (!mb->buf)
55     mb->out_of_core = errno;
56 }
57
58 /* Same as init_membuf but allocates the buffer in secure memory.  */
59 void
60 init_membuf_secure (membuf_t *mb, int initiallen)
61 {
62   mb->len = 0;
63   mb->size = initiallen;
64   mb->out_of_core = 0;
65   mb->buf = xtrymalloc_secure (initiallen);
66   if (!mb->buf)
67     mb->out_of_core = errno;
68 }
69
70
71 /* Shift the the content of the membuf MB by AMOUNT bytes.  The next
72    operation will then behave as if AMOUNT bytes had not been put into
73    the buffer.  If AMOUNT is greater than the actual accumulated
74    bytes, the membuf is basically reset to its initial state.  */
75 void
76 clear_membuf (membuf_t *mb, size_t amount)
77 {
78   /* No need to clear if we are already out of core.  */
79   if (mb->out_of_core)
80     return;
81   if (amount >= mb->len)
82     mb->len = 0;
83   else
84     {
85       mb->len -= amount;
86       memmove (mb->buf, mb->buf+amount, mb->len);
87     }
88 }
89
90
91 void
92 put_membuf (membuf_t *mb, const void *buf, size_t len)
93 {
94   if (mb->out_of_core || !len)
95     return;
96
97   if (mb->len + len >= mb->size)
98     {
99       char *p;
100
101       mb->size += len + 1024;
102       p = xtryrealloc (mb->buf, mb->size);
103       if (!p)
104         {
105           mb->out_of_core = errno ? errno : ENOMEM;
106           /* Wipe out what we already accumulated.  This is required
107              in case we are storing sensitive data here.  The membuf
108              API does not provide another way to cleanup after an
109              error. */
110           wipememory (mb->buf, mb->len);
111           return;
112         }
113       mb->buf = p;
114     }
115   memcpy (mb->buf + mb->len, buf, len);
116   mb->len += len;
117 }
118
119
120 void
121 put_membuf_str (membuf_t *mb, const char *string)
122 {
123   put_membuf (mb, string, strlen (string));
124 }
125
126
127 void
128 put_membuf_printf (membuf_t *mb, const char *format, ...)
129 {
130   int rc;
131   va_list arg_ptr;
132   char *buf;
133
134   va_start (arg_ptr, format);
135   rc = estream_vasprintf (&buf, format, arg_ptr);
136   if (rc < 0)
137     mb->out_of_core = errno ? errno : ENOMEM;
138   va_end (arg_ptr);
139   if (rc >= 0)
140     {
141       put_membuf (mb, buf, strlen (buf));
142       xfree (buf);
143     }
144 }
145
146
147 void *
148 get_membuf (membuf_t *mb, size_t *len)
149 {
150   char *p;
151
152   if (mb->out_of_core)
153     {
154       if (mb->buf)
155         {
156           wipememory (mb->buf, mb->len);
157           xfree (mb->buf);
158           mb->buf = NULL;
159         }
160       gpg_err_set_errno (mb->out_of_core);
161       return NULL;
162     }
163
164   p = mb->buf;
165   if (len)
166     *len = mb->len;
167   mb->buf = NULL;
168   mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
169   return p;
170 }
171
172
173 /* Same as get_membuf but shrinks the reallocated space to the
174    required size.  */
175 void *
176 get_membuf_shrink (membuf_t *mb, size_t *len)
177 {
178   void *p, *pp;
179   size_t dummylen;
180
181   if (!len)
182     len = &dummylen;
183
184   p = get_membuf (mb, len);
185   if (!p)
186     return NULL;
187   if (*len)
188     {
189       pp = xtryrealloc (p, *len);
190       if (pp)
191         p = pp;
192     }
193
194   return p;
195 }
196
197
198 /* Peek at the membuf MB.  On success a pointer to the buffer is
199    returned which is valid until the next operation on MB.  If LEN is
200    not NULL the current LEN of the buffer is stored there.  On error
201    NULL is returned and ERRNO is set.  */
202 const void *
203 peek_membuf (membuf_t *mb, size_t *len)
204 {
205   const char *p;
206
207   if (mb->out_of_core)
208     {
209       gpg_err_set_errno (mb->out_of_core);
210       return NULL;
211     }
212
213   p = mb->buf;
214   if (len)
215     *len = mb->len;
216   return p;
217 }