/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* nautilus-convert-metadata.c - Convert old metadata format to gvfs metadata.
*
* Copyright (C) 2009 Alexander Larsson
*
* Nautilus is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* Nautilus is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; see the file COPYING. If not,
* see .
*
* Authors:
* Alexander Larsson
*/
#include
#include
#include
#include
#include
#include
#include
static gboolean quiet = FALSE;
static xmlNodePtr
xml_get_children (xmlNodePtr parent)
{
if (parent == NULL) {
return NULL;
}
return parent->children;
}
static xmlNodePtr
xml_get_root_children (xmlDocPtr document)
{
return xml_get_children (xmlDocGetRootElement (document));
}
static char *
get_uri_from_nautilus_metafile_name (const char *filename)
{
GString *s;
char c;
char *base_name, *p;
int len;
base_name = g_path_get_basename (filename);
len = strlen (base_name);
if (len <= 4 ||
strcmp (base_name + len - 4, ".xml") != 0) {
g_free (base_name);
return NULL;
}
base_name[len-4] = 0;
s = g_string_new (NULL);
p = base_name;
while (*p) {
c = *p++;
if (c == '%') {
c = g_ascii_xdigit_value (p[0]) << 4 |
g_ascii_xdigit_value (p[1]);
p += 2;
}
g_string_append_c (s, c);
}
g_free (base_name);
return g_string_free (s, FALSE);
}
static struct {
const char *old_key;
const char *new_key;
} metadata_keys[] = {
{"background_color", "metadata::" NAUTILUS_METADATA_KEY_LOCATION_BACKGROUND_COLOR},
{"background_tile_image", "metadata::" NAUTILUS_METADATA_KEY_LOCATION_BACKGROUND_IMAGE},
{"icon_view_auto_layout", "metadata::" NAUTILUS_METADATA_KEY_ICON_VIEW_AUTO_LAYOUT},
{"icon_view_sort_by", "metadata::" NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_BY},
{"icon_view_sort_reversed", "metadata::" NAUTILUS_METADATA_KEY_ICON_VIEW_SORT_REVERSED},
{"icon_view_keep_aligned", "metadata::" NAUTILUS_METADATA_KEY_ICON_VIEW_KEEP_ALIGNED},
{"icon_view_layout_timestamp", "metadata::" NAUTILUS_METADATA_KEY_ICON_VIEW_LAYOUT_TIMESTAMP},
{"list_view_sort_column", "metadata::" NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_COLUMN},
{"list_view_sort_reversed", "metadata::" NAUTILUS_METADATA_KEY_LIST_VIEW_SORT_REVERSED},
{"list_view_visible_columns", "metadata::" NAUTILUS_METADATA_KEY_LIST_VIEW_VISIBLE_COLUMNS},
{"list_view_column_order", "metadata::" NAUTILUS_METADATA_KEY_LIST_VIEW_COLUMN_ORDER},
{"window_geometry", "metadata::" NAUTILUS_METADATA_KEY_WINDOW_GEOMETRY},
{"window_scroll_position", "metadata::" NAUTILUS_METADATA_KEY_WINDOW_SCROLL_POSITION},
{"window_show_hidden_files", "metadata::" NAUTILUS_METADATA_KEY_WINDOW_SHOW_HIDDEN_FILES},
{"window_maximized", "metadata::" NAUTILUS_METADATA_KEY_WINDOW_MAXIMIZED},
{"window_sticky", "metadata::" NAUTILUS_METADATA_KEY_WINDOW_STICKY},
{"window_keep_above", "metadata::" NAUTILUS_METADATA_KEY_WINDOW_KEEP_ABOVE},
{"sidebar_background_color", "metadata::" NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_COLOR},
{"sidebar_background_tile_image", "metadata::" NAUTILUS_METADATA_KEY_SIDEBAR_BACKGROUND_IMAGE},
{"sidebar_buttons", "metadata::" NAUTILUS_METADATA_KEY_SIDEBAR_BUTTONS},
{"annotation", "metadata::" NAUTILUS_METADATA_KEY_ANNOTATION},
{"icon_position", "metadata::" NAUTILUS_METADATA_KEY_ICON_POSITION},
{"icon_position_timestamp", "metadata::" NAUTILUS_METADATA_KEY_ICON_POSITION_TIMESTAMP},
{"icon_scale", "metadata::" NAUTILUS_METADATA_KEY_ICON_SCALE},
{"custom_icon", "metadata::" NAUTILUS_METADATA_KEY_CUSTOM_ICON},
{"screen", "metadata::" NAUTILUS_METADATA_KEY_SCREEN},
{"keyword", "metadata::" NAUTILUS_METADATA_KEY_EMBLEMS},
};
static const char *
convert_key_name (const char *old_key)
{
int i;
for (i = 0; i < G_N_ELEMENTS (metadata_keys); i++) {
if (strcmp (metadata_keys[i].old_key, old_key) == 0) {
return metadata_keys[i].new_key;
}
}
return NULL;
}
static void
parse_xml_node (GFile *file,
xmlNodePtr filenode)
{
xmlNodePtr node;
xmlAttrPtr attr;
xmlChar *property;
const char *new_key;
GHashTable *list_keys;
GList *keys, *l;
GHashTableIter iter;
GFileInfo *info;
int i;
char **strv;
GError *error;
info = g_file_info_new ();
for (attr = filenode->properties; attr != NULL; attr = attr->next) {
if (strcmp ((char *)attr->name, "name") == 0 ||
strcmp ((char *)attr->name, "timestamp") == 0) {
continue;
}
new_key = convert_key_name ((const gchar *) attr->name);
if (new_key) {
property = xmlGetProp (filenode, attr->name);
if (property) {
g_file_info_set_attribute_string (info,
new_key,
(const gchar *) property);
xmlFree (property);
}
}
}
list_keys = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL);
for (node = filenode->children; node != NULL; node = node->next) {
for (attr = node->properties; attr != NULL; attr = attr->next) {
new_key = convert_key_name ((const gchar *) node->name);
if (new_key) {
property = xmlGetProp (node, attr->name);
if (property) {
keys = g_hash_table_lookup (list_keys, new_key);
keys = g_list_append (keys, property);
g_hash_table_replace (list_keys, (char *)new_key, keys);
}
}
}
}
g_hash_table_iter_init (&iter, list_keys);
while (g_hash_table_iter_next (&iter, (void **)&new_key, (void **)&keys)) {
strv = g_new0 (char *, g_list_length (keys) + 1);
for (l = keys, i = 0; l != NULL; l = l->next, i++) {
strv[i] = l->data;
}
g_file_info_set_attribute_stringv (info,
new_key,
strv);
g_free (strv);
g_list_foreach (keys, (GFunc)xmlFree, NULL);
g_list_free (keys);
}
g_hash_table_destroy (list_keys);
if (info) {
error = NULL;
if (!g_file_set_attributes_from_info (file,
info,
0, NULL, &error)) {
char *uri;
uri = g_file_get_uri (file);
if (!quiet) {
g_print ("error setting info for %s: %s\n", uri, error->message);
}
g_free (uri);
g_error_free (error);
}
g_object_unref (info);
}
}
static void
convert_xml_file (xmlDocPtr xml,
GFile *dir)
{
xmlNodePtr node;
xmlChar *name;
char *unescaped_name;
GFile *file;
for (node = xml_get_root_children (xml);
node != NULL; node = node->next) {
if (strcmp ((char *)node->name, "file") == 0) {
name = xmlGetProp (node, (xmlChar *)"name");
unescaped_name = g_uri_unescape_string ((char *)name, "/");
xmlFree (name);
if (unescaped_name == NULL) {
continue;
}
if (strcmp (unescaped_name, ".") == 0) {
file = g_object_ref (dir);
} else {
file = g_file_get_child (dir, unescaped_name);
}
parse_xml_node (file, node);
g_object_unref (file);
g_free (unescaped_name);
}
}
}
static void
convert_nautilus_file (char *file)
{
GFile *dir;
char *uri;
gchar *contents;
gsize length;
xmlDocPtr xml;
if (!g_file_get_contents (file, &contents, &length, NULL)) {
if (!quiet) {
g_print ("failed to load %s\n", file);
}
return;
}
uri = get_uri_from_nautilus_metafile_name (file);
if (uri == NULL) {
g_free (contents);
return;
}
dir = g_file_new_for_uri (uri);
g_free (uri);
xml = xmlParseMemory (contents, length);
g_free (contents);
if (xml == NULL) {
return;
}
convert_xml_file (xml, dir);
xmlFreeDoc (xml);
}
static GOptionEntry entries[] =
{
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet,
"Don't show errors", NULL },
{ NULL }
};
int
main (int argc, char *argv[])
{
GOptionContext *context;
GError *error = NULL;
int i;
context = g_option_context_new (" - convert nautilus metadata");
g_option_context_add_main_entries (context, entries, NULL);
if (!g_option_context_parse (context, &argc, &argv, &error)) {
g_printerr ("option parsing failed: %s\n", error->message);
return 1;
}
if (argc < 2) {
GDir *dir;
char *metafile_dir;
char *file;
const char *entry;
/* Convert all metafiles */
metafile_dir = g_build_filename (g_get_home_dir (),
".nautilus/metafiles", NULL);
dir = g_dir_open (metafile_dir, 0, NULL);
if (dir) {
while ((entry = g_dir_read_name (dir)) != NULL) {
file = g_build_filename (metafile_dir, entry, NULL);
if (g_str_has_suffix (file, ".xml"))
convert_nautilus_file (file);
g_free (file);
}
g_dir_close (dir);
}
g_free (metafile_dir);
} else {
for (i = 1; i < argc; i++) {
convert_nautilus_file (argv[i]);
}
}
return 0;
}