gpg: Use a regular type instead of a void* for import stats.
[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 "util.h"
37 #include "membuf.h"
38
39
40 /* A simple implementation of a dynamic buffer.  Use init_membuf() to
41    create a buffer, put_membuf to append bytes and get_membuf to
42    release and return the buffer.  Allocation errors are detected but
43    only returned at the final get_membuf(), this helps not to clutter
44    the code with out of core checks.  */
45
46 void
47 init_membuf (membuf_t *mb, int initiallen)
48 {
49   mb->len = 0;
50   mb->size = initiallen;
51   mb->out_of_core = 0;
52   mb->buf = xtrymalloc (initiallen);
53   if (!mb->buf)
54     mb->out_of_core = errno;
55 }
56
57 /* Same as init_membuf but allocates the buffer in secure memory.  */
58 void
59 init_membuf_secure (membuf_t *mb, int initiallen)
60 {
61   mb->len = 0;
62   mb->size = initiallen;
63   mb->out_of_core = 0;
64   mb->buf = xtrymalloc_secure (initiallen);
65   if (!mb->buf)
66     mb->out_of_core = errno;
67 }
68
69
70 /* Shift the the content of the membuf MB by AMOUNT bytes.  The next
71    operation will then behave as if AMOUNT bytes had not been put into
72    the buffer.  If AMOUNT is greater than the actual accumulated
73    bytes, the membuf is basically reset to its initial state.  */
74 void
75 clear_membuf (membuf_t *mb, size_t amount)
76 {
77   /* No need to clear if we are already out of core.  */
78   if (mb->out_of_core)
79     return;
80   if (amount >= mb->len)
81     mb->len = 0;
82   else
83     {
84       mb->len -= amount;
85       memmove (mb->buf, mb->buf+amount, mb->len);
86     }
87 }
88
89
90 void
91 put_membuf (membuf_t *mb, const void *buf, size_t len)
92 {
93   if (mb->out_of_core || !len)
94     return;
95
96   if (mb->len + len >= mb->size)
97     {
98       char *p;
99
100       mb->size += len + 1024;
101       p = xtryrealloc (mb->buf, mb->size);
102       if (!p)
103         {
104           mb->out_of_core = errno ? errno : ENOMEM;
105           /* Wipe out what we already accumulated.  This is required
106              in case we are storing sensitive data here.  The membuf
107              API does not provide another way to cleanup after an
108              error. */
109           wipememory (mb->buf, mb->len);
110           return;
111         }
112       mb->buf = p;
113     }
114   memcpy (mb->buf + mb->len, buf, len);
115   mb->len += len;
116 }
117
118
119 void
120 put_membuf_str (membuf_t *mb, const char *string)
121 {
122   put_membuf (mb, string, strlen (string));
123 }
124
125
126 void
127 put_membuf_printf (membuf_t *mb, const char *format, ...)
128 {
129   int rc;
130   va_list arg_ptr;
131   char *buf;
132
133   va_start (arg_ptr, format);
134   rc = gpgrt_vasprintf (&buf, format, arg_ptr);
135   if (rc < 0)
136     mb->out_of_core = errno ? errno : ENOMEM;
137   va_end (arg_ptr);
138   if (rc >= 0)
139     {
140       put_membuf (mb, buf, strlen (buf));
141       xfree (buf);
142     }
143 }
144
145
146 void *
147 get_membuf (membuf_t *mb, size_t *len)
148 {
149   char *p;
150
151   if (mb->out_of_core)
152     {
153       if (mb->buf)
154         {
155           wipememory (mb->buf, mb->len);
156           xfree (mb->buf);
157           mb->buf = NULL;
158         }
159       gpg_err_set_errno (mb->out_of_core);
160       return NULL;
161     }
162
163   p = mb->buf;
164   if (len)
165     *len = mb->len;
166   mb->buf = NULL;
167   mb->out_of_core = ENOMEM; /* hack to make sure it won't get reused. */
168   return p;
169 }
170
171
172 /* Same as get_membuf but shrinks the reallocated space to the
173    required size.  */
174 void *
175 get_membuf_shrink (membuf_t *mb, size_t *len)
176 {
177   void *p, *pp;
178   size_t dummylen;
179
180   if (!len)
181     len = &dummylen;
182
183   p = get_membuf (mb, len);
184   if (!p)
185     return NULL;
186   if (*len)
187     {
188       pp = xtryrealloc (p, *len);
189       if (pp)
190         p = pp;
191     }
192
193   return p;
194 }
195
196
197 /* Peek at the membuf MB.  On success a pointer to the buffer is
198    returned which is valid until the next operation on MB.  If LEN is
199    not NULL the current LEN of the buffer is stored there.  On error
200    NULL is returned and ERRNO is set.  */
201 const void *
202 peek_membuf (membuf_t *mb, size_t *len)
203 {
204   const char *p;
205
206   if (mb->out_of_core)
207     {
208       gpg_err_set_errno (mb->out_of_core);
209       return NULL;
210     }
211
212   p = mb->buf;
213   if (len)
214     *len = mb->len;
215   return p;
216 }