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