2 * Copyright (C) 2002 Jorn Baayen
3 * Copyright © 2009 Christian Persch
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <sys/types.h>
29 #include <glib/gstdio.h>
30 #include <glib/gi18n-lib.h>
32 #include "ev-file-helpers.h"
34 static gchar *tmp_dir = NULL;
37 * ev_dir_ensure_exists:
38 * @dir: the directory name
39 * @mode: permissions to use when creating the directory
40 * @error: a location to store a #GError
42 * Create @dir recursively with permissions @mode.
44 * Returns: %TRUE on success, or %FALSE on error with @error filled in
47 _ev_dir_ensure_exists (const gchar *dir,
54 g_return_val_if_fail (dir != NULL, FALSE);
57 if (g_mkdir_with_parents (dir, mode) == 0)
61 if (errsv == EEXIST && g_file_test (dir, G_FILE_TEST_IS_DIR))
64 display_name = g_filename_display_name (dir);
65 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
66 "Failed to create directory '%s': %s",
67 display_name, g_strerror (errsv));
68 g_free (display_name);
75 * @error: a location to store a #GError
77 * Returns the tmp directory.
79 * Returns: the tmp directory, or %NULL with @error filled in if the
80 * directory could not be created
83 _ev_tmp_dir (GError **error)
86 if (tmp_dir == NULL) {
87 gchar *dirname, *prgname;
89 prgname = g_get_prgname ();
90 dirname = g_strdup_printf ("%s-%u", prgname ? prgname : "unknown", getpid ());
91 tmp_dir = g_build_filename (g_get_tmp_dir (), dirname, NULL);
95 if (!_ev_dir_ensure_exists (tmp_dir, 0700, error))
102 _ev_file_helpers_init (void)
107 _ev_file_helpers_shutdown (void)
118 * @template: a template string; must contain 'XXXXXX', but not necessarily as a suffix
119 * @file_name: a location to store the filename of the temp file
120 * @error: a location to store a #GError
122 * Creates a temp file in the evince temp directory.
124 * Returns: a file descriptor to the newly created temp file name, or %-1
125 * on error with @error filled in
128 ev_mkstemp (const char *template,
136 if ((tmp = _ev_tmp_dir (error)) == NULL)
139 name = g_build_filename (tmp, template, NULL);
140 fd = g_mkstemp (name);
145 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
146 _("Failed to create a temporary file: %s"),
160 close_fd_cb (gpointer fdptr)
162 int fd = GPOINTER_TO_INT (fdptr);
169 * @template: a template string; must contain 'XXXXXX', but not necessarily as a suffix
170 * @error: a location to store a #GError
172 * Creates a temp #GFile in the evince temp directory. See ev_mkstemp() for more information.
174 * Returns: a newly allocated #GFile for the newly created temp file name, or %NULL
175 * on error with @error filled in
178 ev_mkstemp_file (const char *template,
185 fd = ev_mkstemp (template, &file_name, error);
189 file = g_file_new_for_path (file_name);
192 g_object_set_data_full (G_OBJECT (file), "ev-mkstemp-fd",
193 GINT_TO_POINTER (fd), (GDestroyNotify) close_fd_cb);
199 * This function is copied from
200 * http://bugzilla.gnome.org/show_bug.cgi?id=524831
201 * and renamed from g_mkdtemp to _ev_g_mkdtemp.
203 * If/when this function gets added to glib, it can be removed from
208 * @tmpl: template directory name
210 * Creates a temporary directory. See the mkdtemp() documentation
211 * on most UNIX-like systems.
213 * The parameter is a string that should follow the rules for
214 * mkdtemp() templates, i.e. contain the string "XXXXXX". g_mkdtemp()
215 * is slightly more flexible than mkdtemp() in that the sequence does
216 * not have to occur at the very end of the template. The X string
217 * will be modified to form the name of a directory that didn't
218 * already exist. The string should be in the GLib file name
219 * encoding. Most importantly, on Windows it should be in UTF-8.
221 * Return value: If a temporary directory was successfully created,
222 * @tmpl will be returned with the XXXXXX string modified in such a
223 * way as to make the path unique. In case of errors, %NULL is
227 _ev_g_mkdtemp (gchar *tmpl)
229 static const char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
230 static const int NLETTERS = sizeof (letters) - 1;
231 static int counter = 0;
237 /* find the last occurrence of "XXXXXX" */
238 XXXXXX = g_strrstr (tmpl, "XXXXXX");
240 if (!XXXXXX || strncmp (XXXXXX, "XXXXXX", 6))
246 /* Get some more or less random data. */
247 g_get_current_time (&tv);
248 value = (tv.tv_usec ^ tv.tv_sec) + counter++;
250 for (count = 0; count < 100; value += 7777, ++count)
254 /* Fill in the random bits. */
255 XXXXXX[0] = letters[v % NLETTERS];
257 XXXXXX[1] = letters[v % NLETTERS];
259 XXXXXX[2] = letters[v % NLETTERS];
261 XXXXXX[3] = letters[v % NLETTERS];
263 XXXXXX[4] = letters[v % NLETTERS];
265 XXXXXX[5] = letters[v % NLETTERS];
267 /* tmpl is in UTF-8 on Windows, thus use g_mkdir() */
268 if (g_mkdir (tmpl, 0700) == 0)
272 /* Any other error will apply also to other names we might
273 * try, and there are 2^32 or so of them, so give up now.
278 /* We got out of the loop because we ran out of combinations to try. */
285 * @template: a template string; must end in 'XXXXXX'
286 * @error: a location to store a #GError
288 * Creates a temp directory in the evince temp directory.
290 * Returns: a newly allocated string with the temp directory name, or %NULL
291 * on error with @error filled in
294 ev_mkdtemp (const char *template,
300 if ((tmp = _ev_tmp_dir (error)) == NULL)
303 name = g_build_filename (tmp, template, NULL);
304 if (_ev_g_mkdtemp (name) == NULL) {
307 g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv),
308 _("Failed to create a temporary directory: %s"),
318 /* Remove a local temp file created by evince */
320 ev_tmp_filename_unlink (const gchar *filename)
328 if (g_str_has_prefix (filename, tmp_dir)) {
334 ev_tmp_file_unlink (GFile *file)
337 GError *error = NULL;
342 res = g_file_delete (file, NULL, &error);
346 uri = g_file_get_uri (file);
347 g_warning ("Unable to delete temp file %s: %s\n", uri, error->message);
349 g_error_free (error);
354 ev_tmp_uri_unlink (const gchar *uri)
361 file = g_file_new_for_uri (uri);
362 if (!g_file_is_native (file)) {
363 g_warning ("Attempting to delete non native uri: %s\n", uri);
364 g_object_unref (file);
368 ev_tmp_file_unlink (file);
369 g_object_unref (file);
373 ev_file_is_temp (GFile *file)
378 if (!g_file_is_native (file))
381 path = g_file_get_path (file);
385 retval = g_str_has_prefix (path, g_get_tmp_dir ());
392 * ev_xfer_uri_simple:
393 * @from: the source URI
394 * @to: the target URI
395 * @error: a #GError location to store an error, or %NULL
397 * Performs a g_file_copy() from @from to @to.
399 * Returns: %TRUE on success, or %FALSE on error with @error filled in
402 ev_xfer_uri_simple (const char *from,
413 g_return_val_if_fail (to != NULL, TRUE);
415 source_file = g_file_new_for_uri (from);
416 target_file = g_file_new_for_uri (to);
418 result = g_file_copy (source_file, target_file,
419 G_FILE_COPY_TARGET_DEFAULT_PERMS |
420 G_FILE_COPY_OVERWRITE,
421 NULL, NULL, NULL, error);
423 g_object_unref (target_file);
424 g_object_unref (source_file);
430 get_mime_type_from_uri (const gchar *uri, GError **error)
433 GFileInfo *file_info;
434 const gchar *content_type;
435 gchar *mime_type = NULL;
437 file = g_file_new_for_uri (uri);
438 file_info = g_file_query_info (file,
439 G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE,
441 g_object_unref (file);
443 if (file_info == NULL)
446 content_type = g_file_info_get_content_type (file_info);
448 mime_type = g_content_type_get_mime_type (content_type);
451 g_object_unref (file_info);
456 get_mime_type_from_data (const gchar *uri, GError **error)
460 GFileInputStream *input_stream;
464 gchar *content_type, *mime_type;
466 file = g_file_new_for_uri (uri);
468 input_stream = g_file_read (file, NULL, error);
470 g_object_unref (file);
474 size_read = g_input_stream_read (G_INPUT_STREAM (input_stream),
475 buffer, sizeof (buffer), NULL, error);
476 if (size_read == -1) {
477 g_object_unref (input_stream);
478 g_object_unref (file);
482 retval = g_input_stream_close (G_INPUT_STREAM (input_stream), NULL, error);
484 g_object_unref (input_stream);
485 g_object_unref (file);
489 content_type = g_content_type_guess (NULL, /* no filename */
495 mime_type = g_content_type_get_mime_type (content_type);
496 g_free (content_type);
500 * On Windows, the implementation of g_content_type_guess() is too limited at the moment, so we do not
501 * use it and fall back to get_mime_type_from_uri()
503 return get_mime_type_from_uri (uri, error);
504 #endif /* G_OS_WIN32 */
508 * ev_file_get_mime_type:
510 * @fast: whether to use fast MIME type detection
511 * @error: a #GError location to store an error, or %NULL
513 * Note: on unknown MIME types, this may return NULL without @error
516 * Returns: a newly allocated string with the MIME type of the file at
517 * @uri, or %NULL on error or if the MIME type could not be determined
520 ev_file_get_mime_type (const gchar *uri,
524 return fast ? get_mime_type_from_uri (uri, error) : get_mime_type_from_data (uri, error);
527 /* Compressed files support */
528 #define BZIPCOMMAND "bzip2"
529 #define GZIPCOMMAND "gzip"
531 #define BUFFER_SIZE 1024
534 compression_run (const gchar *uri,
535 EvCompressionType type,
540 gchar *uri_dst = NULL;
541 gchar *filename, *filename_dst = NULL;
546 if (type == EV_COMPRESSION_NONE)
549 cmd = g_find_program_in_path ((type == EV_COMPRESSION_BZIP2) ? BZIPCOMMAND : GZIPCOMMAND);
551 /* FIXME: better error codes! */
552 /* FIXME: i18n later */
553 g_set_error (error, G_FILE_ERROR, G_FILE_ERROR_FAILED,
554 "Failed to find the \"%s\" command in the search path.",
555 type == EV_COMPRESSION_BZIP2 ? BZIPCOMMAND : GZIPCOMMAND);
559 filename = g_filename_from_uri (uri, NULL, error);
565 fd = ev_mkstemp ("comp.XXXXXX", &filename_dst, error);
574 argv[1] = compress ? "-c" : "-cd";
578 if (g_spawn_async_with_pipes (NULL, argv, NULL,
579 G_SPAWN_STDERR_TO_DEV_NULL,
581 NULL, &pout, NULL, &err)) {
582 GIOChannel *in, *out;
583 gchar buf[BUFFER_SIZE];
584 GIOStatus read_st, write_st;
585 gsize bytes_read, bytes_written;
587 in = g_io_channel_unix_new (pout);
588 g_io_channel_set_encoding (in, NULL, NULL);
589 out = g_io_channel_unix_new (fd);
590 g_io_channel_set_encoding (out, NULL, NULL);
593 read_st = g_io_channel_read_chars (in, buf,
597 if (read_st == G_IO_STATUS_NORMAL) {
598 write_st = g_io_channel_write_chars (out, buf,
602 if (write_st == G_IO_STATUS_ERROR)
604 } else if (read_st == G_IO_STATUS_ERROR) {
607 } while (bytes_read > 0);
609 g_io_channel_unref (in);
610 g_io_channel_unref (out);
616 g_propagate_error (error, err);
618 uri_dst = g_filename_to_uri (filename_dst, NULL, error);
623 g_free (filename_dst);
629 * ev_file_uncompress:
631 * @type: the compression type
632 * @error: a #GError location to store an error, or %NULL
634 * Uncompresses the file at @uri.
636 * If @type is %EV_COMPRESSION_NONE, it does nothing and returns %NULL.
638 * Otherwise, it returns the filename of a
639 * temporary file containing the decompressed data from the file at @uri.
640 * On error it returns %NULL and fills in @error.
642 * It is the caller's responsibility to unlink the temp file after use.
644 * Returns: a newly allocated string URI, or %NULL on error
647 ev_file_uncompress (const gchar *uri,
648 EvCompressionType type,
651 g_return_val_if_fail (uri != NULL, NULL);
653 return compression_run (uri, type, FALSE, error);
659 * @type: the compression type
660 * @error: a #GError location to store an error, or %NULL
662 * Compresses the file at @uri.
664 * If @type is %EV_COMPRESSION_NONE, it does nothing and returns %NULL.
666 * Otherwise, it returns the filename of a
667 * temporary file containing the compressed data from the file at @uri.
669 * On error it returns %NULL and fills in @error.
671 * It is the caller's responsibility to unlink the temp file after use.
673 * Returns: a newly allocated string URI, or %NULL on error
676 ev_file_compress (const gchar *uri,
677 EvCompressionType type,
680 g_return_val_if_fail (uri != NULL, NULL);
682 return compression_run (uri, type, TRUE, error);