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