2003-05-18 Marcus Brinkmann <marcus@g10code.de>
[gpgme.git] / gpgme / data-compat.c
1 /* data-compat.c - Compatibility interfaces for data objects.
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 <sys/stat.h>
26 #include <stdlib.h>
27
28 #include "data.h"
29 #include "util.h"
30
31 \f
32 /* Create a new data buffer filled with LENGTH bytes starting from
33    OFFSET within the file FNAME or stream STREAM (exactly one must be
34    non-zero).  */
35 gpgme_error_t
36 gpgme_data_new_from_filepart (gpgme_data_t *dh, const char *fname,
37                               FILE *stream, off_t offset, size_t length)
38 {
39   gpgme_error_t err;
40   char *buf = NULL;
41
42   if (stream && fname)
43     return GPGME_Invalid_Value;
44
45   if (fname)
46     stream = fopen (fname, "rb");
47   if (!stream)
48     return GPGME_File_Error;
49
50   if (fseek (stream, offset, SEEK_SET))
51     goto ferr;
52
53   buf = malloc (length);
54   if (!buf)
55     goto ferr;
56
57   while (fread (buf, length, 1, stream) < 1
58          && ferror (stream) && errno == EINTR);
59   if (ferror (stream))
60     {
61       if (buf)
62         free (buf);
63       goto ferr;
64     }
65
66   if (fname)
67     fclose (stream);
68
69   err = gpgme_data_new (dh);
70   if (err)
71     {
72       if (buf)
73         free (buf);
74       return err;
75     }
76
77   (*dh)->data.mem.buffer = buf;
78   (*dh)->data.mem.size = length;
79   (*dh)->data.mem.length = length;
80   return 0;
81
82  ferr:
83   {
84     int saved_errno = errno;
85     if (fname)
86       fclose (stream);
87     errno = saved_errno;
88     return GPGME_File_Error;
89   }
90 }
91
92 \f
93 /* Create a new data buffer filled with the content of file FNAME.
94    COPY must be non-zero (delayed reads are not supported yet).  */
95 gpgme_error_t
96 gpgme_data_new_from_file (gpgme_data_t *dh, const char *fname, int copy)
97 {
98   struct stat statbuf;
99
100   if (!fname || !copy)
101     return GPGME_Invalid_Value;
102
103   if (stat (fname, &statbuf) < 0)
104     return GPGME_File_Error;
105
106   return gpgme_data_new_from_filepart (dh, fname, NULL, 0, statbuf.st_size);
107 }
108
109 \f
110 static int
111 gpgme_error_to_errno (gpgme_error_t err)
112 {
113   switch (err)
114     {
115     case GPGME_EOF:
116       return 0;
117     case GPGME_Out_Of_Core:
118       errno = ENOMEM;
119       return -1;
120     case GPGME_Invalid_Value:
121       errno = EINVAL;
122       return -1;
123     case GPGME_Not_Implemented:
124       errno = EOPNOTSUPP;
125       return -1;
126     default:
127       /* XXX Yeah, well.  */
128       errno = EINVAL;
129       return -1;
130     }
131 }
132
133 static ssize_t
134 old_user_read (gpgme_data_t dh, void *buffer, size_t size)
135 {
136   size_t amt;
137   gpgme_error_t err = (*dh->data.old_user.cb) (dh->data.old_user.handle,
138                                                buffer, size, &amt);
139   if (err)
140     return gpgme_error_to_errno (err);
141   return amt;
142 }
143
144
145 static off_t
146 old_user_seek (gpgme_data_t dh, off_t offset, int whence)
147 {
148   gpgme_error_t err;
149   if (whence != SEEK_SET || offset)
150     return EINVAL;
151   err = (*dh->data.old_user.cb) (dh->data.old_user.handle, NULL, 0, NULL);
152   if (err)
153     return gpgme_error_to_errno (err);
154   return 0;
155 }
156
157
158 static struct _gpgme_data_cbs old_user_cbs =
159   {
160     old_user_read,
161     NULL,
162     old_user_seek,
163     NULL
164   };
165
166
167 /* Create a new data buffer which retrieves the data from the callback
168    function READ_CB.  */
169 gpgme_error_t
170 gpgme_data_new_with_read_cb (gpgme_data_t *dh,
171                              int (*read_cb) (void *, char *, size_t, size_t *),
172                              void *read_cb_value)
173 {
174   gpgme_error_t err = _gpgme_data_new (dh, &old_user_cbs);
175   if (err)
176     return err;
177
178   (*dh)->data.old_user.cb = read_cb;
179   (*dh)->data.old_user.handle = read_cb_value;
180   return 0;
181 }
182
183 \f
184 gpgme_error_t
185 gpgme_data_rewind (gpgme_data_t dh)
186 {
187   return (gpgme_data_seek (dh, 0, SEEK_SET) == -1)
188     ? GPGME_File_Error : 0;
189 }