doc/
[gpgme.git] / gpgme / data-mem.c
1 /* data-mem.c - A memory based data object.
2    Copyright (C) 2002, 2003 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 #include <string.h>
29
30 #include "data.h"
31 #include "util.h"
32
33 \f
34 static ssize_t
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 void
145 mem_release (GpgmeData dh)
146 {
147   if (dh->data.mem.buffer)
148     free (dh->data.mem.buffer);
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 }