1 /* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * gsf-input-stdio.c: stdio based input
4 *
5 * Copyright (C) 2002-2006 Jody Goldberg (jody@gnome.org)
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of version 2.1 of the GNU Lesser General Public
9 * License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 #include <gsf-config.h>
23 #include <gsf/gsf-input-stdio.h>
24 #include <gsf/gsf.h>
25
26 #include <glib/gstdio.h>
27
28 #include <errno.h>
29 #ifdef HAVE_UNISTD_H
30 #include <unistd.h>
31 #endif
32 #include <sys/types.h>
33 #include <sys/stat.h>
34
35 static GObjectClass *parent_class;
36
37 struct _GsfInputStdio {
38 GsfInput input;
39
40 FILE *file;
41 char *filename;
42 guint8 *buf;
43 size_t buf_size;
44 gboolean keep_open;
45 };
46
47 typedef struct {
48 GsfInputClass input_class;
49 } GsfInputStdioClass;
50
51 static GsfInput *
make_local_copy(FILE * stream,const char * filename,GError ** err)52 make_local_copy (FILE *stream, const char *filename, GError **err)
53 {
54 GsfOutput *out;
55 GsfInput *copy = NULL;
56
57 out = gsf_output_memory_new ();
58
59 while (1) {
60 guint8 buf[4096];
61 gssize nread;
62
63 nread = fread (buf, 1, sizeof(buf), stream);
64
65 if (nread > 0) {
66 if (!gsf_output_write (out, nread, buf))
67 goto error;
68 } else if (nread == 0)
69 break;
70 else
71 goto error;
72 }
73
74 copy = gsf_input_memory_new_clone
75 (gsf_output_memory_get_bytes (GSF_OUTPUT_MEMORY (out)),
76 gsf_output_size (out));
77
78 gsf_output_close (out);
79 g_object_unref (out);
80
81 if (filename)
82 gsf_input_set_name_from_filename (GSF_INPUT (copy), filename);
83
84 return copy;
85
86 error:
87 if (err) {
88 char *utf8name = filename
89 ? g_filename_display_name (filename)
90 : g_strdup ("?");
91 g_set_error (err, gsf_input_error_id (), 0,
92 "%s: not a regular file",
93 utf8name);
94 g_free (utf8name);
95 }
96
97 gsf_output_close (out);
98 g_object_unref (out);
99
100 return NULL;
101 }
102
103 /**
104 * gsf_input_stdio_new:
105 * @filename: in utf8.
106 * @err: (allow-none): place to store a #GError if anything goes wrong
107 *
108 * Returns: a new file or %NULL.
109 **/
110 /* coverity[ -tainted_string_sink_content : arg-0 ] */
111 GsfInput *
gsf_input_stdio_new(char const * filename,GError ** err)112 gsf_input_stdio_new (char const *filename, GError **err)
113 {
114 GsfInputStdio *input;
115 struct stat st;
116 FILE *file;
117 gsf_off_t size;
118
119 g_return_val_if_fail (filename != NULL, NULL);
120
121 file = g_fopen (filename, "rb");
122 if (file == NULL) {
123 if (err) {
124 int save_errno = errno;
125 char *utf8name = g_filename_display_name (filename);
126 g_set_error (err,
127 G_FILE_ERROR,
128 g_file_error_from_errno (save_errno),
129 "%s: %s",
130 utf8name, g_strerror (save_errno));
131 g_free (utf8name);
132 }
133 return NULL;
134 }
135
136 if (fstat (fileno (file), &st) < 0 || !S_ISREG (st.st_mode)) {
137 GsfInput *res = make_local_copy (file, filename, err);
138 fclose (file);
139 return res;
140 }
141
142 size = st.st_size;
143 input = (GsfInputStdio *)g_object_new (GSF_INPUT_STDIO_TYPE, NULL);
144 input->file = file;
145 input->filename = g_strdup (filename);
146 input->buf = NULL;
147 input->buf_size = 0;
148 input->keep_open = FALSE;
149 gsf_input_set_size (GSF_INPUT (input), size);
150 gsf_input_set_name_from_filename (GSF_INPUT (input), filename);
151 gsf_input_set_modtime_from_stat (GSF_INPUT (input), &st);
152
153 return GSF_INPUT (input);
154 }
155
156 /**
157 * gsf_input_stdio_new_FILE:
158 * @filename: The filename corresponding to @file.
159 * @file: (transfer full): an existing stdio <type>FILE</type> *
160 * @keep_open: Should @file be closed when the wrapper is closed
161 *
162 * Assumes ownership of @file when succeeding. If @keep_open is true,
163 * ownership reverts to caller when the #GsfInput is closed.
164 *
165 * Returns: a new #GsfInput wrapper for @file. Note that if the file is not
166 * seekable, this function will make a local copy of the entire file.
167 **/
168 GsfInput *
gsf_input_stdio_new_FILE(char const * filename,FILE * file,gboolean keep_open)169 gsf_input_stdio_new_FILE (char const *filename, FILE *file, gboolean keep_open)
170 {
171 GsfInputStdio *stdio;
172 struct stat st;
173 gsf_off_t size;
174
175 g_return_val_if_fail (filename != NULL, NULL);
176 g_return_val_if_fail (file != NULL, NULL);
177
178 if (fstat (fileno (file), &st) < 0 || !S_ISREG (st.st_mode)) {
179 return make_local_copy (file, filename, NULL);
180 }
181
182 size = st.st_size;
183
184 stdio = g_object_new (GSF_INPUT_STDIO_TYPE, NULL);
185 stdio->file = file;
186 stdio->keep_open = keep_open;
187 stdio->filename = g_strdup (filename);
188 gsf_input_set_size (GSF_INPUT (stdio), size);
189 gsf_input_set_name_from_filename (GSF_INPUT (stdio), filename);
190 return GSF_INPUT (stdio);
191 }
192
193 static void
gsf_input_stdio_finalize(GObject * obj)194 gsf_input_stdio_finalize (GObject *obj)
195 {
196 GsfInputStdio *input = (GsfInputStdio *)obj;
197
198 if (input->file != NULL) {
199 if (!input->keep_open)
200 fclose (input->file);
201 input->file = NULL;
202 }
203
204 g_free (input->buf);
205 input->buf = NULL;
206 input->buf_size = 0;
207
208 g_free (input->filename);
209
210 parent_class->finalize (obj);
211 }
212
213 static GsfInput *
gsf_input_stdio_dup(GsfInput * src_input,GError ** err)214 gsf_input_stdio_dup (GsfInput *src_input, GError **err)
215 {
216 GsfInputStdio const *src = (GsfInputStdio *)src_input;
217 return gsf_input_stdio_new (src->filename, err);
218 }
219
220 static guint8 const *
gsf_input_stdio_read(GsfInput * input,size_t num_bytes,guint8 * buffer)221 gsf_input_stdio_read (GsfInput *input, size_t num_bytes,
222 guint8 *buffer)
223 {
224 GsfInputStdio *stdio = GSF_INPUT_STDIO (input);
225 size_t nread = 0, total_read = 0;
226
227 g_return_val_if_fail (stdio != NULL, NULL);
228 g_return_val_if_fail (stdio->file != NULL, NULL);
229
230 if (buffer == NULL) {
231 if (stdio->buf_size < num_bytes) {
232 stdio->buf_size = num_bytes;
233 g_free (stdio->buf);
234 stdio->buf = g_new (guint8, stdio->buf_size);
235 }
236 buffer = stdio->buf;
237 }
238
239 while (total_read < num_bytes) {
240 nread = fread (buffer + total_read, 1,
241 num_bytes - total_read, stdio->file);
242 total_read += nread;
243 if (total_read < num_bytes &&
244 (ferror (stdio->file) || feof (stdio->file)))
245 return NULL;
246 }
247
248 return buffer;
249 }
250
251 static gboolean
gsf_input_stdio_seek(GsfInput * input,gsf_off_t offset,GSeekType whence)252 gsf_input_stdio_seek (GsfInput *input, gsf_off_t offset, GSeekType whence)
253 {
254 GsfInputStdio const *stdio = GSF_INPUT_STDIO (input);
255 int stdio_whence = SEEK_SET;
256
257 #ifndef HAVE_FSEEKO
258 long loffset;
259 #else
260 off_t loffset;
261 #endif
262
263 if (stdio->file == NULL)
264 return TRUE;
265
266 loffset = offset;
267 if ((gsf_off_t) loffset != offset) { /* Check for overflow */
268 #ifdef HAVE_FSEEKO
269 g_warning ("offset too large for fseeko");
270 #else
271 g_warning ("offset too large for fseek");
272 #endif
273 return TRUE;
274 }
275 switch (whence) {
276 case G_SEEK_CUR : stdio_whence = SEEK_CUR; break;
277 case G_SEEK_END : stdio_whence = SEEK_END; break;
278 case G_SEEK_SET:
279 default:
280 break;
281 }
282
283 errno = 0;
284 #ifdef HAVE_FSEEKO
285 if (0 == fseeko (stdio->file, loffset, stdio_whence))
286 return FALSE;
287 #else
288 if (0 == fseek (stdio->file, loffset, stdio_whence))
289 return FALSE;
290 #endif
291
292 return TRUE;
293 }
294
295 static void
gsf_input_stdio_init(GObject * obj)296 gsf_input_stdio_init (GObject *obj)
297 {
298 GsfInputStdio *stdio = GSF_INPUT_STDIO (obj);
299
300 stdio->file = NULL;
301 stdio->filename = NULL;
302 stdio->buf = NULL;
303 stdio->buf_size = 0;
304 stdio->keep_open = FALSE;
305 }
306
307 static void
gsf_input_stdio_class_init(GObjectClass * gobject_class)308 gsf_input_stdio_class_init (GObjectClass *gobject_class)
309 {
310 GsfInputClass *input_class = GSF_INPUT_CLASS (gobject_class);
311
312 gobject_class->finalize = gsf_input_stdio_finalize;
313 input_class->Dup = gsf_input_stdio_dup;
314 input_class->Read = gsf_input_stdio_read;
315 input_class->Seek = gsf_input_stdio_seek;
316
317 parent_class = g_type_class_peek_parent (gobject_class);
318 }
319
320 GSF_CLASS (GsfInputStdio, gsf_input_stdio,
321 gsf_input_stdio_class_init, gsf_input_stdio_init,
322 GSF_INPUT_TYPE)
323