]> Pileus Git - ~andy/gtk/blobdiff - gtk/updateiconcache.c
Apply a cleanup patch by Kjartan Maraas (#341812)
[~andy/gtk] / gtk / updateiconcache.c
index 82aa18d539ba91423b56a564c66ac8a4eb68f937..f19d03c9c3f6ab058ae762bd384ec53cfc7521da 100644 (file)
 #include <string.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#ifdef HAVE_UNISTD_H
 #include <unistd.h>
+#endif
 #include <errno.h>
+#ifdef _MSC_VER
+#include <sys/utime.h>
+#else
 #include <utime.h>
+#endif
 
 #include <glib.h>
 #include <glib/gstdio.h>
 #include <gdk-pixbuf/gdk-pixdata.h>
+#include <glib/gi18n.h>
 
 static gboolean force_update = FALSE;
+static gboolean ignore_theme_index = FALSE;
 static gboolean quiet = FALSE;
+static gboolean index_only = FALSE;
+static gchar *var_name = "-";
 
 #define CACHE_NAME "icon-theme.cache"
 
@@ -42,7 +52,7 @@ static gboolean quiet = FALSE;
 #define HAS_SUFFIX_PNG (1 << 2)
 #define HAS_ICON_FILE  (1 << 3)
 
-#define CAN_CACHE_IMAGE_DATA(flags) (((flags) & HAS_SUFFIX_PNG) || ((flags) & HAS_SUFFIX_XPM))
+#define CAN_CACHE_IMAGE_DATA(flags) (!index_only && (((flags) & HAS_SUFFIX_PNG) || ((flags) & HAS_SUFFIX_XPM)))
 
 #define MAJOR_VERSION 1
 #define MINOR_VERSION 0
@@ -51,7 +61,7 @@ static gboolean quiet = FALSE;
 #define ALIGN_VALUE(this, boundary) \
   (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
 
-gboolean
+static gboolean
 is_cache_up_to_date (const gchar *path)
 {
   struct stat path_stat, cache_stat;
@@ -71,7 +81,7 @@ is_cache_up_to_date (const gchar *path)
   retval = g_stat (cache_path, &cache_stat);
   g_free (cache_path);
   
-  if (retval < 0 && errno == ENOENT)
+  if (retval < 0)
     {
       /* Cache file not found */
       return FALSE;
@@ -81,13 +91,39 @@ is_cache_up_to_date (const gchar *path)
   return cache_stat.st_mtime >= path_stat.st_mtime;
 }
 
+static gboolean
+has_theme_index (const gchar *path)
+{
+  gboolean result;
+  gchar *index_path;
+
+  index_path = g_build_filename (path, "index.theme", NULL);
+
+  result = g_file_test (index_path, G_FILE_TEST_IS_REGULAR);
+  
+  g_free (index_path);
+
+  return result;
+}
+
+
+typedef struct 
+{
+  gboolean has_pixdata;
+  GdkPixdata pixdata;
+  guint32 offset;
+  guint pixel_data_size;
+} ImageData;
+
+static GHashTable *image_data_hash = NULL;
+
 typedef struct
 {
   int flags;
   int dir_index;
 
-  gboolean has_pixdata;
-  GdkPixdata pixdata;
+  ImageData *image_data;
+  guint pixel_data_size;
 
   int has_embedded_rect;
   int x0, y0, x1, y1;
@@ -110,9 +146,9 @@ foreach_remove_func (gpointer key, gpointer value, gpointer user_data)
   if (image->flags == HAS_ICON_FILE)
     {
       g_free (key);
-      g_free (image);
       g_free (image->attach_points);
       g_strfreev (image->display_names);
+      g_free (image);
 
       return TRUE;
     }
@@ -239,24 +275,182 @@ load_icon_data (Image *image, const char *path)
   g_key_file_free (icon_file);
 }
 
+/*
+ * This function was copied from gtkfilesystemunix.c, it should
+ * probably go to GLib
+ */
 static void
-maybe_cache_image_data (Image *image, const gchar *path)
+canonicalize_filename (gchar *filename)
 {
-  if (CAN_CACHE_IMAGE_DATA(image->flags) && !image->has_pixdata) 
+  gchar *p, *q;
+  gboolean last_was_slash = FALSE;
+
+  p = filename;
+  q = filename;
+
+  while (*p)
     {
-      GdkPixbuf *pixbuf;
+      if (*p == G_DIR_SEPARATOR)
+       {
+         if (!last_was_slash)
+           *q++ = G_DIR_SEPARATOR;
+
+         last_was_slash = TRUE;
+       }
+      else
+       {
+         if (last_was_slash && *p == '.')
+           {
+             if (*(p + 1) == G_DIR_SEPARATOR ||
+                 *(p + 1) == '\0')
+               {
+                 if (*(p + 1) == '\0')
+                   break;
+
+                 p += 1;
+               }
+             else if (*(p + 1) == '.' &&
+                      (*(p + 2) == G_DIR_SEPARATOR ||
+                       *(p + 2) == '\0'))
+               {
+                 if (q > filename + 1)
+                   {
+                     q--;
+                     while (q > filename + 1 &&
+                            *(q - 1) != G_DIR_SEPARATOR)
+                       q--;
+                   }
+
+                 if (*(p + 2) == '\0')
+                   break;
+
+                 p += 2;
+               }
+             else
+               {
+                 *q++ = *p;
+                 last_was_slash = FALSE;
+               }
+           }
+         else
+           {
+             *q++ = *p;
+             last_was_slash = FALSE;
+           }
+       }
+
+      p++;
+    }
+
+  if (q > filename + 1 && *(q - 1) == G_DIR_SEPARATOR)
+    q--;
+
+  *q = '\0';
+}
+
+static gchar *
+follow_links (const gchar *path)
+{
+  gchar *target;
+  gchar *d, *s;
+  gchar *path2 = NULL;
+
+  path2 = g_strdup (path);
+  while (g_file_test (path2, G_FILE_TEST_IS_SYMLINK))
+    {
+      target = g_file_read_link (path2, NULL);
       
-      pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+      if (target)
+       {
+         if (g_path_is_absolute (target))
+           path2 = target;
+         else
+           {
+             d = g_path_get_dirname (path2);
+             s = g_build_filename (d, target, NULL);
+             g_free (d);
+             g_free (target);
+             g_free (path2);
+             path2 = s;
+           }
+       }
+      else
+       break;
+    }
+
+  if (strcmp (path, path2) == 0)
+    {
+      g_free (path2);
+      path2 = NULL;
+    }
+
+  return path2;
+}
+
+static void
+maybe_cache_image_data (Image       *image, 
+                       const gchar *path)
+{
+  if (CAN_CACHE_IMAGE_DATA(image->flags) && !image->image_data) 
+    {
+      GdkPixbuf *pixbuf;
+      ImageData *idata;
+      gchar *path2;
+
+      idata = g_hash_table_lookup (image_data_hash, path);
+
+      path2 = follow_links (path);
+
+      if (path2)
+       {
+         ImageData *idata2;
+
+         canonicalize_filename (path2);
+  
+         idata2 = g_hash_table_lookup (image_data_hash, path2);
+
+         if (idata && idata2 && idata != idata2)
+           g_error (_("different idatas found for symlinked '%s' and '%s'\n"),
+                    path, path2);
+
+         if (idata && !idata2)
+           g_hash_table_insert (image_data_hash, g_strdup (path2), idata);
+
+         if (!idata && idata2)
+           {
+             g_hash_table_insert (image_data_hash, g_strdup (path), idata2);
+             idata = idata2;
+           }
+       }
       
-      if (pixbuf) 
+      if (!idata)
        {
-         image->has_pixdata = TRUE;
-         gdk_pixdata_from_pixbuf (&image->pixdata, pixbuf, FALSE);
+         idata = g_new0 (ImageData, 1);
+         g_hash_table_insert (image_data_hash, g_strdup (path), idata);
+         if (path2)
+           g_hash_table_insert (image_data_hash, g_strdup (path2), idata);  
+       }
+
+      if (!idata->has_pixdata)
+       {
+         pixbuf = gdk_pixbuf_new_from_file (path, NULL);
+         
+         if (pixbuf) 
+           {
+             gdk_pixdata_from_pixbuf (&idata->pixdata, pixbuf, FALSE);
+             idata->pixel_data_size = idata->pixdata.length + 8;
+             idata->has_pixdata = TRUE;
+           }
        }
+
+      image->image_data = idata;
+
+      if (path2)
+       g_free (path2);
     }
 }
 
-GList *
+static GList *
 scan_directory (const gchar *base_path, 
                const gchar *subdir, 
                GHashTable  *files, 
@@ -306,7 +500,6 @@ scan_directory (const gchar *base_path,
        }
 
       retval = g_file_test (path, G_FILE_TEST_IS_REGULAR);
-
       if (retval)
        {
          if (g_str_has_suffix (name, ".png"))
@@ -360,7 +553,6 @@ scan_directory (const gchar *base_path,
        }
 
       g_free (path);
-
     }
 
   g_dir_close (dir);
@@ -385,8 +577,8 @@ struct _HashNode
 static guint
 icon_name_hash (gconstpointer key)
 {
-  const char *p = key;
-  guint h = *p;
+  const signed char *p = key;
+  guint32 h = *p;
 
   if (h)
     for (p += 1; *p != '\0'; p++)
@@ -422,7 +614,7 @@ convert_to_hash (gpointer key, gpointer value, gpointer user_data)
   return TRUE;
 }
 
-gboolean
+static gboolean
 write_string (FILE *cache, const gchar *n)
 {
   gchar *s;
@@ -439,40 +631,37 @@ write_string (FILE *cache, const gchar *n)
   
 }
 
-gboolean
+static gboolean
 write_card16 (FILE *cache, guint16 n)
 {
   int i;
-  gchar s[2];
 
-  *((guint16 *)s) = GUINT16_TO_BE (n);
+  n = GUINT16_TO_BE (n);
   
-  i = fwrite (s, 2, 1, cache);
+  i = fwrite ((char *)&n, 2, 1, cache);
 
   return i == 1;
 }
 
-gboolean
+static gboolean
 write_card32 (FILE *cache, guint32 n)
 {
   int i;
-  gchar s[4];
 
-  *((guint32 *)s) = GUINT32_TO_BE (n);
+  n = GUINT32_TO_BE (n);
   
-  i = fwrite (s, 4, 1, cache);
+  i = fwrite ((char *)&n, 4, 1, cache);
 
   return i == 1;
 }
 
 
-gboolean
+static gboolean
 write_pixdata (FILE *cache, GdkPixdata *pixdata)
 {
   guint8 *s;
-  int len;
-  int i;
-
+  guint len;
+  gint i;
 
   /* Type 0 is GdkPixdata */
   if (!write_card32 (cache, 0))
@@ -502,7 +691,7 @@ write_header (FILE *cache, guint32 dir_list_offset)
          write_card32 (cache, dir_list_offset));
 }
 
-guint
+static guint
 get_image_meta_data_size (Image *image)
 {
   gint i;
@@ -530,16 +719,23 @@ get_image_meta_data_size (Image *image)
   return len;
 }
 
-guint
+static guint
 get_image_pixel_data_size (Image *image)
 {
-  if (image->has_pixdata)
-    return image->pixdata.length + 8;
+  if (image->pixel_data_size == 0)
+    {
+      if (image->image_data && 
+         image->image_data->has_pixdata)
+       {
+         image->pixel_data_size = image->image_data->pixel_data_size;
+         image->image_data->pixel_data_size = 0;
+       }
+    }
 
-  return 0;
+  return image->pixel_data_size;
 }
 
-guint
+static guint
 get_image_data_size (Image *image)
 {
   guint len;
@@ -549,13 +745,13 @@ get_image_data_size (Image *image)
   len += get_image_pixel_data_size (image);
   len += get_image_meta_data_size (image);
   
-  if (len > 0)
+  if (len > 0 || (image->image_data && image->image_data->has_pixdata))
     len += 8;
 
   return len;
 }
 
-guint
+static guint
 get_single_node_size (HashNode *node, gboolean include_image_data)
 {
   int len = 0;
@@ -575,18 +771,17 @@ get_single_node_size (HashNode *node, gboolean include_image_data)
     for (list = node->image_list; list; list = list->next)
       {
        Image *image = list->data;
-       
+
        len += get_image_data_size (image);
       }
   
   return len;
 }
 
-guint
+static guint
 get_bucket_size (HashNode *node)
 {
   int len = 0;
-
   while (node)
     {
       len += get_single_node_size (node, TRUE);
@@ -597,7 +792,7 @@ get_bucket_size (HashNode *node)
   return len;
 }
 
-gboolean
+static gboolean
 write_bucket (FILE *cache, HashNode *node, int *offset)
 {
   while (node != NULL)
@@ -648,7 +843,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
        {
          Image *image = list->data;
          int image_data_size = get_image_data_size (image);
-         
+
          /* Directory index */
          if (!write_card16 (cache, image->dir_index))
            return FALSE;
@@ -664,7 +859,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
                return FALSE;
              data_offset += image_data_size;
            }
-         else
+         else 
            {
              if (!write_card32 (cache, 0))
                return FALSE;
@@ -681,7 +876,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
          int pixel_data_size = get_image_pixel_data_size (image);
          int meta_data_size = get_image_meta_data_size (image);
 
-         if (meta_data_size + pixel_data_size == 0)
+         if (get_image_data_size (image) == 0)
            continue;
 
          /* Pixel data */
@@ -689,10 +884,19 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
            {
              if (!write_card32 (cache, image_data_offset + 8))
                return FALSE;
+
+             image->image_data->offset = image_data_offset + 8;
            }
          else
            {
-             if (!write_card32 (cache, 0))
+             gint offset;
+
+             if (image->image_data)
+               offset = image->image_data->offset;
+             else
+               offset = 0;
+
+             if (!write_card32 (cache, offset))
                return FALSE;
            }
 
@@ -709,7 +913,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
 
          if (pixel_data_size > 0)
            {
-             if (!write_pixdata (cache, &image->pixdata))
+             if (!write_pixdata (cache, &image->image_data->pixdata))
                return FALSE;
            }
          
@@ -808,7 +1012,7 @@ write_bucket (FILE *cache, HashNode *node, int *offset)
   return TRUE;
 }
 
-gboolean
+static gboolean
 write_hash_table (FILE *cache, HashContext *context, int *new_offset)
 {
   int offset = HASH_OFFSET;
@@ -856,7 +1060,7 @@ write_hash_table (FILE *cache, HashContext *context, int *new_offset)
   return TRUE;
 }
 
-gboolean
+static gboolean
 write_dir_index (FILE *cache, int offset, GList *directories)
 {
   int n_dirs;
@@ -890,7 +1094,7 @@ write_dir_index (FILE *cache, int offset, GList *directories)
   return TRUE;
 }
 
-gboolean
+static gboolean
 write_file (FILE *cache, GHashTable *files, GList *directories)
 {
   HashContext context;
@@ -909,19 +1113,19 @@ write_file (FILE *cache, GHashTable *files, GList *directories)
    * back and change it later */
   if (!write_header (cache, 0))
     {
-      g_printerr ("Failed to write header\n");
+      g_printerr (_("Failed to write header\n"));
       return FALSE;
     }
 
   if (!write_hash_table (cache, &context, &new_offset))
     {
-      g_printerr ("Failed to write hash table\n");
+      g_printerr (_("Failed to write hash table\n"));
       return FALSE;
     }
 
   if (!write_dir_index (cache, new_offset, directories))
     {
-      g_printerr ("Failed to write directory index\n");
+      g_printerr (_("Failed to write directory index\n"));
       return FALSE;
     }
   
@@ -929,17 +1133,20 @@ write_file (FILE *cache, GHashTable *files, GList *directories)
 
   if (!write_header (cache, new_offset))
     {
-      g_printerr ("Failed to rewrite header\n");
+      g_printerr (_("Failed to rewrite header\n"));
       return FALSE;
     }
     
   return TRUE;
 }
 
-void
+static void
 build_cache (const gchar *path)
 {
   gchar *cache_path, *tmp_cache_path;
+#ifdef G_OS_WIN32
+  gchar *bak_cache_path = NULL;
+#endif
   GHashTable *files;
   gboolean retval;
   FILE *cache;
@@ -952,11 +1159,12 @@ build_cache (const gchar *path)
   
   if (!cache)
     {
-      g_printerr ("Failed to write cache file: %s\n", g_strerror (errno));
+      g_printerr (_("Failed to write cache file: %s\n"), g_strerror (errno));
       exit (1);
     }
 
   files = g_hash_table_new (g_str_hash, g_str_equal);
+  image_data_hash = g_hash_table_new (g_str_hash, g_str_equal);
   
   directories = scan_directory (path, NULL, files, NULL, 0);
 
@@ -984,28 +1192,98 @@ build_cache (const gchar *path)
 
   cache_path = g_build_filename (path, CACHE_NAME, NULL);
 
+#ifdef G_OS_WIN32
+  if (g_file_test (cache_path, G_FILE_TEST_EXISTS))
+    {
+      bak_cache_path = g_strconcat (cache_path, ".bak", NULL);
+      g_unlink (bak_cache_path);
+      if (g_rename (cache_path, bak_cache_path) == -1)
+       {
+         g_printerr (_("Could not rename %s to %s: %s, removing %s then.\n"),
+                     cache_path, bak_cache_path,
+                     g_strerror (errno),
+                     cache_path);
+         g_unlink (cache_path);
+         bak_cache_path = NULL;
+       }
+    }
+#endif
+
   if (g_rename (tmp_cache_path, cache_path) == -1)
     {
+      g_printerr (_("Could not rename %s to %s: %s\n"),
+                 tmp_cache_path, cache_path,
+                 g_strerror (errno));
       g_unlink (tmp_cache_path);
+#ifdef G_OS_WIN32
+      if (bak_cache_path != NULL)
+       if (g_rename (bak_cache_path, cache_path) == -1)
+         g_printerr (_("Could not rename %s back to %s: %s.\n"),
+                     bak_cache_path, cache_path,
+                     g_strerror (errno));
+#endif
       exit (1);
     }
+#ifdef G_OS_WIN32
+  if (bak_cache_path != NULL)
+    g_unlink (bak_cache_path);
+#endif
 
   /* Update time */
   /* FIXME: What do do if an error occurs here? */
-  g_stat (path, &path_stat);
-  g_stat (cache_path, &cache_stat);
+  if (g_stat (path, &path_stat) < 0 ||
+      g_stat (cache_path, &cache_stat))
+    exit (1);
 
   utime_buf.actime = path_stat.st_atime;
   utime_buf.modtime = cache_stat.st_mtime;
   utime (path, &utime_buf);
   
   if (!quiet)
-    g_printerr ("Cache file created successfully.\n");
+    g_printerr (_("Cache file created successfully.\n"));
+}
+
+static void
+write_csource (const gchar *path)
+{
+  gchar *cache_path;
+  gchar *data;
+  gsize len;
+  gint i;
+
+  cache_path = g_build_filename (path, CACHE_NAME, NULL);
+  if (!g_file_get_contents (cache_path, &data, &len, NULL))
+    exit (1);
+  
+  g_printf ("#ifdef __SUNPRO_C\n");
+  g_printf ("#pragma align 4 (%s)\n", var_name);   
+  g_printf ("#endif\n");
+  
+  g_printf ("#ifdef __GNUC__\n");
+  g_printf ("static const guint8 %s[] __attribute__ ((__aligned__ (4))) = \n", var_name);
+  g_printf ("#else\n");
+  g_printf ("static const guint8 %s[] = \n", var_name);
+  g_printf ("#endif\n");
+
+  g_printf ("{\n");
+  for (i = 0; i < len - 1; i++)
+    {
+      if (i %12 == 0)
+       g_printf ("  ");
+      g_printf ("0x%02x, ", (guint8)data[i]);
+      if (i % 12 == 11)
+        g_printf ("\n");
+    }
+  
+  g_printf ("0x%02x\n};\n", (guint8)data[i]);
 }
 
 static GOptionEntry args[] = {
-  { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, "Overwrite an existing cache, even if uptodate", NULL },
-  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, "Turn off verbose output", NULL },
+  { "force", 'f', 0, G_OPTION_ARG_NONE, &force_update, N_("Overwrite an existing cache, even if up to date"), NULL },
+  { "ignore-theme-index", 't', 0, G_OPTION_ARG_NONE, &ignore_theme_index, N_("Don't check for the existence of index.theme"), NULL },
+  { "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don't include image data in the cache"), NULL },
+  { "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
+  { "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
   { NULL }
 };
 
@@ -1017,9 +1295,12 @@ main (int argc, char **argv)
 
   if (argc < 2)
     return 0;
+  
+  bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR);
+  bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
 
   context = g_option_context_new ("ICONPATH");
-  g_option_context_add_main_entries (context, args, NULL);
+  g_option_context_add_main_entries (context, args, GETTEXT_PACKAGE);
 
   g_option_context_parse (context, &argc, &argv, NULL);
   
@@ -1028,11 +1309,21 @@ main (int argc, char **argv)
   path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
 #endif
   
+  if (!ignore_theme_index && !has_theme_index (path))
+    {
+      g_printerr (_("No theme index file in '%s'.\n"
+                   "If you really want to create an icon cache here, use --ignore-theme-index.\n"), path);
+      return 1;
+    }
+  
   if (!force_update && is_cache_up_to_date (path))
     return 0;
 
   g_type_init ();
   build_cache (path);
-  
+
+  if (strcmp (var_name, "-") != 0)
+    write_csource (path);
+
   return 0;
 }