2002-10-09 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / data-mem.c
1 /* data-mem.c - A memory based data object.
2  *      Copyright (C) 2002 g10 Code GmbH
3  *
4  * This file is part of GPGME.
5  *
6  * GPGME is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GPGME is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #if HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <assert.h>
29
30 #include "data.h"
31 #include "util.h"
32
33 \f
34 static int
35 mem_read (GpgmeData dh, void *buffer, size_t size)
36 {
37   size_t amt = dh->data.mem.length - dh->data.mem.offset;
38   const char *src;
39
40   if (!amt)
41     return 0;
42
43   if (size < amt)
44     amt = size;
45
46   src = dh->data.mem.buffer ? dh->data.mem.buffer : dh->data.mem.orig_buffer;
47   memcpy (buffer, src + dh->data.mem.offset, amt);
48   dh->data.mem.offset += amt;
49   return amt;
50 }
51
52
53 static ssize_t
54 mem_write (GpgmeData dh, const void *buffer, size_t size)
55 {
56   size_t unused;
57
58   if (!dh->data.mem.buffer && dh->data.mem.orig_buffer)
59     {
60       size_t new_size = dh->data.mem.size;
61       char *new_buffer;
62
63       if (new_size < dh->data.mem.offset + size)
64         new_size = dh->data.mem.offset + size;
65
66       new_buffer = malloc (new_size);
67       if (!new_buffer)
68         return -1;
69       dh->data.mem.buffer = new_buffer;      
70       dh->data.mem.size = new_size;
71     }
72
73   unused = dh->data.mem.size - dh->data.mem.offset;
74   if (unused < size)
75     {
76       /* Allocate a large enough buffer with exponential backoff.  */
77 #define INITIAL_ALLOC 512
78       size_t new_size = dh->data.mem.size
79         ? (2 * dh->data.mem.size) : INITIAL_ALLOC;
80       char *new_buffer;
81
82       if (new_size < dh->data.mem.offset + size)
83         new_size = dh->data.mem.offset + size;
84
85       new_buffer = realloc (dh->data.mem.buffer, new_size);
86       if (!new_buffer && new_size > dh->data.mem.offset + size)
87         {
88           /* Maybe we were too greedy, try again.  */
89           new_size = dh->data.mem.offset + size;
90           new_buffer = realloc (dh->data.mem.buffer, new_size);
91         }
92       if (!new_buffer)
93         return -1;
94       dh->data.mem.buffer = new_buffer;
95       dh->data.mem.size = new_size;
96     }
97
98   memcpy (dh->data.mem.buffer + dh->data.mem.offset, buffer, size);
99   dh->data.mem.offset += size;
100   if (dh->data.mem.length < dh->data.mem.offset)
101     dh->data.mem.length = dh->data.mem.offset;
102   return size;
103 }
104
105
106 static off_t
107 mem_seek (GpgmeData dh, off_t offset, int whence)
108 {
109   switch (whence)
110     {
111     case SEEK_SET:
112       if (offset < 0 || offset > dh->data.mem.length)
113         {
114           errno = EINVAL;
115           return -1;
116         }
117       dh->data.mem.offset = offset;
118       break;
119     case SEEK_CUR:
120       if ((offset > 0 && dh->data.mem.length - dh->data.mem.offset < offset)
121           || (offset < 0 && dh->data.mem.offset < -offset)) 
122         {
123           errno = EINVAL;
124           return -1;
125         }
126       dh->data.mem.offset += offset;
127       break;
128     case SEEK_END:
129       if (offset > 0 || -offset > dh->data.mem.length)
130         {
131           errno = EINVAL;
132           return -1;
133         }
134       dh->data.mem.offset = dh->data.mem.length - offset;
135       break;
136     default:
137       errno = EINVAL;
138       return -1;
139     }
140   return dh->data.mem.offset;
141 }
142
143
144 static int
145 mem_release (GpgmeData dh)
146 {
147   if (dh->data.mem.buffer)
148     free (dh->data.mem.buffer);
149   return 0;
150 }
151
152
153 static struct gpgme_data_cbs mem_cbs =
154   {
155     mem_read,
156     mem_write,
157     mem_seek,
158     mem_release
159   };
160
161 \f
162 GpgmeError
163 gpgme_data_new (GpgmeData *dh)
164 {
165   GpgmeError err = _gpgme_data_new (dh, &mem_cbs);
166   if (err)
167     return err;
168
169   return 0;
170 }
171
172
173 /* Create a new data buffer filled with SIZE bytes starting from
174    BUFFER.  If COPY is zero, copying is delayed until necessary, and
175    the data is taken from the original location when needed.  */
176 GpgmeError
177 gpgme_data_new_from_mem (GpgmeData *dh, const char *buffer,
178                          size_t size, int copy)
179 {
180   GpgmeError err = _gpgme_data_new (dh, &mem_cbs);
181   if (err)
182     return err;
183
184   if (copy)
185     {
186       char *bufcpy = malloc (size);
187       if (!bufcpy)
188         _gpgme_data_release (*dh);
189       memcpy (bufcpy, buffer, size);
190       (*dh)->data.mem.buffer = bufcpy;
191     }
192   else
193     (*dh)->data.mem.orig_buffer = buffer;
194   
195   (*dh)->data.mem.size = size;
196   (*dh)->data.mem.length = size;
197   return 0;
198 }
199
200
201 /* This function does make sense when we know that it contains no nil
202    chars and if the underlying data object is memory based.  */
203 char *
204 _gpgme_data_get_as_string (GpgmeData dh)
205 {
206   char *dst = NULL;
207   const char *src = NULL;
208
209   assert (dh->cbs == &mem_cbs);
210
211   src = dh->data.mem.buffer;
212   if (!src)
213     src = dh->data.mem.orig_buffer;
214   dst = malloc (dh->data.mem.length + 1);
215   if (dst)
216     {
217       if (src)
218         memcpy (dst, src, dh->data.mem.length);
219       dst[dh->data.mem.length] = '\0';
220     }
221   return dst;
222 }
223
224
225 char *
226 gpgme_data_release_and_get_mem (GpgmeData dh, size_t *r_len)
227 {
228   char *str = NULL;
229
230   if (!dh || dh->cbs != &mem_cbs)
231     return NULL;
232
233   str = dh->data.mem.buffer;
234   if (!str && dh->data.mem.orig_buffer)
235     {
236       str = malloc (dh->data.mem.length);
237       if (!str)
238         return NULL;
239       memcpy (str, dh->data.mem.orig_buffer, dh->data.mem.length);
240     }
241
242   if (r_len)
243     *r_len = dh->data.mem.length;
244
245   return str;
246 }
247
248
249 /* This function does make sense when we know that it contains no nil
250    chars and if the underlying data object is memory based.  */
251 char *
252 _gpgme_data_release_and_return_string (GpgmeData dh)
253 {
254   char *str = NULL;
255
256   if (!dh)
257     return NULL;
258
259   assert (dh->cbs == &mem_cbs);
260   if (gpgme_data_write (dh, "", 1) == 1)
261     str = gpgme_data_release_and_get_mem (dh, NULL);
262   else
263     gpgme_data_release (dh);
264
265   return str;
266 }