doc/
[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   return 0;
149 }
150
151
152 static struct gpgme_data_cbs mem_cbs =
153   {
154     mem_read,
155     mem_write,
156     mem_seek,
157     mem_release
158   };
159
160 \f
161 GpgmeError
162 gpgme_data_new (GpgmeData *dh)
163 {
164   GpgmeError err = _gpgme_data_new (dh, &mem_cbs);
165   if (err)
166     return err;
167
168   return 0;
169 }
170
171
172 /* Create a new data buffer filled with SIZE bytes starting from
173    BUFFER.  If COPY is zero, copying is delayed until necessary, and
174    the data is taken from the original location when needed.  */
175 GpgmeError
176 gpgme_data_new_from_mem (GpgmeData *dh, const char *buffer,
177                          size_t size, int copy)
178 {
179   GpgmeError err = _gpgme_data_new (dh, &mem_cbs);
180   if (err)
181     return err;
182
183   if (copy)
184     {
185       char *bufcpy = malloc (size);
186       if (!bufcpy)
187         _gpgme_data_release (*dh);
188       memcpy (bufcpy, buffer, size);
189       (*dh)->data.mem.buffer = bufcpy;
190     }
191   else
192     (*dh)->data.mem.orig_buffer = buffer;
193   
194   (*dh)->data.mem.size = size;
195   (*dh)->data.mem.length = size;
196   return 0;
197 }
198
199
200 /* This function does make sense when we know that it contains no nil
201    chars and if the underlying data object is memory based.  */
202 char *
203 _gpgme_data_get_as_string (GpgmeData dh)
204 {
205   char *dst = NULL;
206   const char *src = NULL;
207
208   assert (dh->cbs == &mem_cbs);
209
210   src = dh->data.mem.buffer;
211   if (!src)
212     src = dh->data.mem.orig_buffer;
213   dst = malloc (dh->data.mem.length + 1);
214   if (dst)
215     {
216       if (src)
217         memcpy (dst, src, dh->data.mem.length);
218       dst[dh->data.mem.length] = '\0';
219     }
220   return dst;
221 }
222
223
224 char *
225 gpgme_data_release_and_get_mem (GpgmeData dh, size_t *r_len)
226 {
227   char *str = NULL;
228
229   if (!dh || dh->cbs != &mem_cbs)
230     return NULL;
231
232   str = dh->data.mem.buffer;
233   if (!str && dh->data.mem.orig_buffer)
234     {
235       str = malloc (dh->data.mem.length);
236       if (!str)
237         return NULL;
238       memcpy (str, dh->data.mem.orig_buffer, dh->data.mem.length);
239     }
240
241   if (r_len)
242     *r_len = dh->data.mem.length;
243
244   return str;
245 }
246
247
248 /* This function does make sense when we know that it contains no nil
249    chars and if the underlying data object is memory based.  */
250 char *
251 _gpgme_data_release_and_return_string (GpgmeData dh)
252 {
253   char *str = NULL;
254
255   if (!dh)
256     return NULL;
257
258   assert (dh->cbs == &mem_cbs);
259   if (gpgme_data_write (dh, "", 1) == 1)
260     str = gpgme_data_release_and_get_mem (dh, NULL);
261   else
262     gpgme_data_release (dh);
263
264   return str;
265 }