1
/* update-desktop-database.c - maintains mimetype<->desktop mapping cache
2
* vim: set ts=2 sw=2 et: */
5
* Copyright (C) 2004-2006 Red Hat, Inc.
6
* Copyright (C) 2006, 2008 Vincent Untz
8
* Program written by Ray Strode <rstrode@redhat.com>
9
* Vincent Untz <vuntz@gnome.org>
11
* update-desktop-database is free software; you can redistribute it
12
* and/or modify it under the terms of the GNU General Public License as
13
* published by the Free Software Foundation; either version 2 of the
14
* License, or (at your option) any later version.
16
* update-desktop-database is distributed in the hope that it will be
17
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty
18
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19
* General Public License for more details.
21
* You should have received a copy of the GNU General Public License
22
* along with update-desktop-database; see the file COPYING. If not,
23
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite
24
* 330, Boston, MA 02111-1307, USA.
32
#include <sys/types.h>
37
#include <glib/gi18n.h>
39
#include "keyfileutils.h"
40
#include "mimeutils.h"
42
#define NAME "update-desktop-database"
43
#define CACHE_FILENAME "mimeinfo.cache"
44
#define TEMP_CACHE_FILENAME_PREFIX ".mimeinfo.cache.XXXXXX"
46
#define udd_print(...) if (!quiet) g_printerr (__VA_ARGS__)
47
#define udd_verbose_print(...) if (!quiet && verbose) g_printerr (__VA_ARGS__)
49
static FILE *open_temp_cache_file (const char *dir,
52
static void add_mime_type (const char *mime_type, GList *desktop_files, FILE *f);
53
static void sync_database (const char *dir, GError **error);
54
static void cache_desktop_file (const char *desktop_file,
55
const char *mime_type,
57
static void process_desktop_file (const char *desktop_file,
60
static void process_desktop_files (const char *desktop_dir,
63
static void update_database (const char *desktop_dir, GError **error);
64
static const char ** get_default_search_path (void);
65
static void print_desktop_dirs (const char **dirs);
67
static GHashTable *mime_types_map = NULL;
68
static gboolean verbose = FALSE, quiet = FALSE;
71
list_free_deep (gpointer key, GList *l, gpointer data)
73
g_list_foreach (l, (GFunc)g_free, NULL);
78
cache_desktop_file (const char *desktop_file,
79
const char *mime_type,
84
desktop_files = (GList *) g_hash_table_lookup (mime_types_map, mime_type);
86
/* do not add twice a desktop file mentioning the mime type more than once
87
* (no need to use g_list_find() because we cache all mime types registered
88
* by a desktop file before moving to another desktop file) */
90
strcmp (desktop_file, (const char *) desktop_files->data) == 0)
93
desktop_files = g_list_prepend (desktop_files, g_strdup (desktop_file));
94
g_hash_table_insert (mime_types_map, g_strdup (mime_type), desktop_files);
99
process_desktop_file (const char *desktop_file,
108
keyfile = g_key_file_new ();
111
g_key_file_load_from_file (keyfile, desktop_file,
112
G_KEY_FILE_NONE, &load_error);
114
if (load_error != NULL)
116
g_propagate_error (error, load_error);
120
/* Hidden=true means that the .desktop file should be completely ignored */
121
if (g_key_file_get_boolean (keyfile, GROUP_DESKTOP_ENTRY, "Hidden", NULL))
123
g_key_file_free (keyfile);
127
mime_types = g_key_file_get_string_list (keyfile,
129
"MimeType", NULL, &load_error);
131
g_key_file_free (keyfile);
133
if (load_error != NULL)
135
g_propagate_error (error, load_error);
139
for (i = 0; mime_types[i] != NULL; i++)
142
MimeUtilsValidity valid;
145
mime_type = g_strchomp (mime_types[i]);
146
valid = mu_mime_type_is_valid (mime_types[i], &valid_error);
152
udd_print (_("Warning in file \"%s\": usage of MIME type \"%s\" is "
153
"discouraged (%s)\n"),
154
desktop_file, mime_types[i], valid_error);
155
g_free (valid_error);
158
udd_print (_("Error in file \"%s\": \"%s\" is an invalid MIME type "
160
desktop_file, mime_types[i], valid_error);
161
g_free (valid_error);
162
/* not a break: we continue to the next mime type */
165
g_assert_not_reached ();
168
cache_desktop_file (name, mime_type, &load_error);
170
if (load_error != NULL)
172
g_propagate_error (error, load_error);
173
g_strfreev (mime_types);
177
g_strfreev (mime_types);
181
process_desktop_files (const char *desktop_dir,
185
GError *process_error;
187
const char *filename;
189
process_error = NULL;
190
dir = g_dir_open (desktop_dir, 0, &process_error);
192
if (process_error != NULL)
194
g_propagate_error (error, process_error);
198
while ((filename = g_dir_read_name (dir)) != NULL)
200
char *full_path, *name;
202
full_path = g_build_filename (desktop_dir, filename, NULL);
204
if (g_file_test (full_path, G_FILE_TEST_IS_DIR))
208
sub_prefix = g_strdup_printf ("%s%s-", prefix, filename);
210
process_desktop_files (full_path, sub_prefix, &process_error);
213
if (process_error != NULL)
215
udd_verbose_print (_("Could not process directory \"%s\": %s\n"),
216
full_path, process_error->message);
217
g_error_free (process_error);
218
process_error = NULL;
223
else if (!g_str_has_suffix (filename, ".desktop"))
229
name = g_strdup_printf ("%s%s", prefix, filename);
230
process_desktop_file (full_path, name, &process_error);
233
if (process_error != NULL)
235
if (!g_error_matches (process_error,
237
G_KEY_FILE_ERROR_KEY_NOT_FOUND))
239
udd_print (_("Could not parse file \"%s\": %s\n"), full_path,
240
process_error->message);
244
udd_verbose_print (_("File \"%s\" lacks MimeType key\n"),
248
g_error_free (process_error);
249
process_error = NULL;
259
open_temp_cache_file (const char *dir, char **filename, GError **error)
266
file = g_build_filename (dir, TEMP_CACHE_FILENAME_PREFIX, NULL);
267
fd = g_mkstemp (file);
271
g_set_error (error, G_FILE_ERROR,
272
g_file_error_from_errno (errno),
273
"%s", g_strerror (errno));
281
fchmod (fd, 0666 & ~mask);
283
fp = fdopen (fd, "w+");
286
g_set_error (error, G_FILE_ERROR,
287
g_file_error_from_errno (errno),
288
"%s", g_strerror (errno));
303
add_mime_type (const char *mime_type, GList *desktop_files, FILE *f)
308
list = g_string_new (mime_type);
309
g_string_append_c (list, '=');
310
for (desktop_file = desktop_files;
311
desktop_file != NULL;
312
desktop_file = desktop_file->next)
314
g_string_append (list, (const char *) desktop_file->data);
315
g_string_append_c (list, ';');
317
g_string_append_c (list, '\n');
319
fputs (list->str, f);
321
g_string_free (list, TRUE);
325
sync_database (const char *dir, GError **error)
328
char *temp_cache_file, *cache_file;
332
temp_cache_file = NULL;
334
tmp_file = open_temp_cache_file (dir, &temp_cache_file, &sync_error);
336
if (sync_error != NULL)
338
g_propagate_error (error, sync_error);
342
fputs ("[MIME Cache]\n", tmp_file);
344
keys = g_hash_table_get_keys (mime_types_map);
345
keys = g_list_sort (keys, (GCompareFunc) g_strcmp0);
347
for (key = keys; key != NULL; key = key->next)
348
add_mime_type (key->data,
349
g_hash_table_lookup (mime_types_map, key->data),
355
cache_file = g_build_filename (dir, CACHE_FILENAME, NULL);
356
if (rename (temp_cache_file, cache_file) < 0)
358
g_set_error (error, G_FILE_ERROR,
359
g_file_error_from_errno (errno),
360
_("Cache file \"%s\" could not be written: %s"),
361
cache_file, g_strerror (errno));
363
unlink (temp_cache_file);
365
g_free (temp_cache_file);
370
update_database (const char *desktop_dir,
373
GError *update_error;
375
mime_types_map = g_hash_table_new_full (g_str_hash, g_str_equal,
376
(GDestroyNotify)g_free,
380
process_desktop_files (desktop_dir, "", &update_error);
382
if (update_error != NULL)
383
g_propagate_error (error, update_error);
386
sync_database (desktop_dir, &update_error);
387
if (update_error != NULL)
388
g_propagate_error (error, update_error);
390
g_hash_table_foreach (mime_types_map, (GHFunc) list_free_deep, NULL);
391
g_hash_table_destroy (mime_types_map);
395
get_default_search_path (void)
397
static char **args = NULL;
398
const char * const *data_dirs;
402
return (const char **) args;
404
data_dirs = g_get_system_data_dirs ();
406
for (i = 0; data_dirs[i] != NULL; i++);
408
args = g_new (char *, i + 1);
410
for (i = 0; data_dirs[i] != NULL; i++)
411
args[i] = g_build_filename (data_dirs[i], "applications", NULL);
415
return (const char **) args;
419
print_desktop_dirs (const char **dirs)
423
directories = g_strjoinv (", ", (char **) dirs);
424
udd_verbose_print(_("Search path is now: [%s]\n"), directories);
425
g_free (directories);
433
GOptionContext *context;
434
const char **desktop_dirs;
436
gboolean found_processable_dir;
438
const GOptionEntry options[] =
440
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet,
441
N_("Do not display any information about processing and "
442
"updating progress"), NULL},
444
{ "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
445
N_("Display more information about processing and updating progress"),
448
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &desktop_dirs,
449
NULL, N_("[DIRECTORY...]") },
453
context = g_option_context_new ("");
454
g_option_context_set_summary (context, _("Build cache database of MIME types handled by desktop files."));
455
g_option_context_add_main_entries (context, options, NULL);
459
g_option_context_parse (context, &argc, &argv, &error);
462
g_printerr ("%s\n", error->message);
463
g_printerr (_("Run \"%s --help\" to see a full list of available command line options.\n"), argv[0]);
464
g_error_free (error);
468
if (desktop_dirs == NULL || desktop_dirs[0] == NULL)
469
desktop_dirs = get_default_search_path ();
471
print_desktop_dirs (desktop_dirs);
473
found_processable_dir = FALSE;
474
for (i = 0; desktop_dirs[i] != NULL; i++)
477
update_database (desktop_dirs[i], &error);
481
udd_verbose_print (_("Could not create cache file in \"%s\": %s\n"),
482
desktop_dirs[i], error->message);
483
g_error_free (error);
487
found_processable_dir = TRUE;
489
g_option_context_free (context);
491
if (!found_processable_dir)
495
directories = g_strjoinv (", ", (char **) desktop_dirs);
496
udd_print (_("The databases in [%s] could not be updated.\n"),
499
g_free (directories);