114d83660bd32876e05e675c960fb938213017d7
[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 char *
201 gpgme_data_release_and_get_mem (GpgmeData dh, size_t *r_len)
202 {
203   char *str = NULL;
204
205   if (!dh || dh->cbs != &mem_cbs)
206     return NULL;
207
208   str = dh->data.mem.buffer;
209   if (!str && dh->data.mem.orig_buffer)
210     {
211       str = malloc (dh->data.mem.length);
212       if (!str)
213         return NULL;
214       memcpy (str, dh->data.mem.orig_buffer, dh->data.mem.length);
215     }
216
217   if (r_len)
218     *r_len = dh->data.mem.length;
219
220   return str;
221 }