summaryrefslogtreecommitdiffstats
path: root/libgimpthumb/gimpthumb-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgimpthumb/gimpthumb-utils.c')
-rw-r--r--libgimpthumb/gimpthumb-utils.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/libgimpthumb/gimpthumb-utils.c b/libgimpthumb/gimpthumb-utils.c
new file mode 100644
index 0000000..59dd830
--- /dev/null
+++ b/libgimpthumb/gimpthumb-utils.c
@@ -0,0 +1,870 @@
+/* LIBGIMP - The GIMP Library
+ * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
+ *
+ * Thumbnail handling according to the Thumbnail Managing Standard.
+ * https://specifications.freedesktop.org/thumbnail-spec/
+ *
+ * Copyright (C) 2001-2003 Sven Neumann <sven@gimp.org>
+ * Michael Natterer <mitch@gimp.org>
+ *
+ * This library is free software: you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+
+#ifdef PLATFORM_OSX
+#include <AppKit/AppKit.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+
+#ifdef G_OS_WIN32
+#include "libgimpbase/gimpwin32-io.h"
+#endif
+
+#include "gimpthumb-error.h"
+#include "gimpthumb-types.h"
+#include "gimpthumb-utils.h"
+
+#include "libgimp/libgimp-intl.h"
+
+
+/**
+ * SECTION: gimpthumb-utils
+ * @title: GimpThumb-utils
+ * @short_description: Utility functions provided and used by libgimpthumb
+ *
+ * Utility functions provided and used by libgimpthumb
+ **/
+
+
+static gint gimp_thumb_size (GimpThumbSize size);
+static gchar * gimp_thumb_png_lookup (const gchar *name,
+ const gchar *basedir,
+ GimpThumbSize *size) G_GNUC_MALLOC;
+static const gchar * gimp_thumb_png_name (const gchar *uri);
+static void gimp_thumb_exit (void);
+
+
+
+static gboolean gimp_thumb_initialized = FALSE;
+static gint thumb_num_sizes = 0;
+static gint *thumb_sizes = NULL;
+static const gchar **thumb_sizenames = NULL;
+static gchar *thumb_dir = NULL;
+static gchar **thumb_subdirs = NULL;
+static gchar *thumb_fail_subdir = NULL;
+
+
+/**
+ * gimp_thumb_init:
+ * @creator: an ASCII string that identifies the thumbnail creator
+ * @thumb_basedir: an absolute path or %NULL to use the default
+ *
+ * This function initializes the thumbnail system. It must be called
+ * before any other functions from libgimpthumb are used. You may call
+ * it more than once if you want to change the @thumb_basedir but if
+ * you do that, you should make sure that no thread is still using the
+ * library. Apart from this function, libgimpthumb is multi-thread
+ * safe.
+ *
+ * The @creator string must be 7bit ASCII and should contain the name
+ * of the software that creates the thumbnails. It is used to handle
+ * thumbnail creation failures. See the spec for more details.
+ *
+ * Usually you will pass %NULL for @thumb_basedir. Thumbnails will
+ * then be stored in the user's personal thumbnail directory as
+ * defined in the spec. If you wish to use libgimpthumb to store
+ * application-specific thumbnails, you can specify a different base
+ * directory here.
+ *
+ * Return value: %TRUE if the library was successfully initialized.
+ **/
+gboolean
+gimp_thumb_init (const gchar *creator,
+ const gchar *thumb_basedir)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ gint i;
+
+ g_return_val_if_fail (creator != NULL, FALSE);
+ g_return_val_if_fail (thumb_basedir == NULL ||
+ g_path_is_absolute (thumb_basedir), FALSE);
+
+ if (gimp_thumb_initialized)
+ gimp_thumb_exit ();
+
+ if (thumb_basedir)
+ {
+ thumb_dir = g_strdup (thumb_basedir);
+ }
+ else
+ {
+#ifdef PLATFORM_OSX
+
+ NSAutoreleasePool *pool;
+ NSArray *path;
+ NSString *cache_dir;
+
+ pool = [[NSAutoreleasePool alloc] init];
+
+ path = NSSearchPathForDirectoriesInDomains (NSCachesDirectory,
+ NSUserDomainMask, YES);
+ cache_dir = [path objectAtIndex:0];
+
+ thumb_dir = g_build_filename ([cache_dir UTF8String], "org.freedesktop.thumbnails",
+ NULL);
+
+ [pool drain];
+
+#else
+
+ const gchar *cache_dir = g_get_user_cache_dir ();
+
+ if (cache_dir && g_file_test (cache_dir, G_FILE_TEST_IS_DIR))
+ {
+ thumb_dir = g_build_filename (cache_dir, "thumbnails", NULL);
+ }
+
+#endif
+
+ if (! thumb_dir)
+ {
+ gchar *name = g_filename_display_name (g_get_tmp_dir ());
+
+ g_message (_("Cannot determine a valid thumbnails directory.\n"
+ "Thumbnails will be stored in the folder for "
+ "temporary files (%s) instead."), name);
+ g_free (name);
+
+ thumb_dir = g_build_filename (g_get_tmp_dir (), ".thumbnails", NULL);
+ }
+ }
+
+ enum_class = g_type_class_ref (GIMP_TYPE_THUMB_SIZE);
+
+ thumb_num_sizes = enum_class->n_values;
+ thumb_sizes = g_new (gint, thumb_num_sizes);
+ thumb_sizenames = g_new (const gchar *, thumb_num_sizes);
+ thumb_subdirs = g_new (gchar *, thumb_num_sizes);
+
+ for (i = 0, enum_value = enum_class->values;
+ i < enum_class->n_values;
+ i++, enum_value++)
+ {
+ thumb_sizes[i] = enum_value->value;
+ thumb_sizenames[i] = enum_value->value_nick;
+ thumb_subdirs[i] = g_build_filename (thumb_dir,
+ enum_value->value_nick, NULL);
+ }
+
+ thumb_fail_subdir = thumb_subdirs[0];
+ thumb_subdirs[0] = g_build_filename (thumb_fail_subdir, creator, NULL);
+
+ g_type_class_unref (enum_class);
+
+ gimp_thumb_initialized = TRUE;
+
+ return gimp_thumb_initialized;
+}
+
+/**
+ * gimp_thumb_get_thumb_base_dir:
+ *
+ * Returns the base directory of thumbnails cache.
+ * It uses the Freedesktop Thumbnail Managing Standard on UNIX,
+ * "~/Library/Caches/org.freedesktop.thumbnails" on OSX, and a cache
+ * folder determined by glib on Windows (currently the common repository
+ * for temporary Internet files).
+ * The returned string belongs to GIMP and must not be changed nor freed.
+ *
+ * Returns: the thumbnails cache directory.
+ *
+ * Since: 2.10
+ **/
+const gchar *
+gimp_thumb_get_thumb_base_dir (void)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+
+ return thumb_dir;
+}
+
+/**
+ * gimp_thumb_get_thumb_dir:
+ * @size: a GimpThumbSize
+ *
+ * Retrieve the name of the thumbnail folder for a specific size. The
+ * returned pointer will become invalid if gimp_thumb_init() is used
+ * again. It must not be changed or freed.
+ *
+ * Return value: the thumbnail directory in the encoding of the filesystem
+ **/
+const gchar *
+gimp_thumb_get_thumb_dir (GimpThumbSize size)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+
+ size = gimp_thumb_size (size);
+
+ return thumb_subdirs[size];
+}
+
+/**
+ * gimp_thumb_get_thumb_dir_local:
+ * @dirname: the basename of the dir, without the actual dirname itself
+ * @size: a GimpThumbSize
+ *
+ * Retrieve the name of the local thumbnail folder for a specific
+ * size. Unlike gimp_thumb_get_thumb_dir() the returned string is not
+ * constant and should be free'd when it is not any longer needed.
+ *
+ * Return value: the thumbnail directory in the encoding of the filesystem
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_thumb_get_thumb_dir_local (const gchar *dirname,
+ GimpThumbSize size)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (dirname != NULL, NULL);
+ g_return_val_if_fail (size > GIMP_THUMB_SIZE_FAIL, NULL);
+
+ size = gimp_thumb_size (size);
+
+ return g_build_filename (dirname, thumb_sizenames[size], NULL);
+}
+
+/**
+ * gimp_thumb_ensure_thumb_dir:
+ * @size: a GimpThumbSize
+ * @error: return location for possible errors
+ *
+ * This function checks if the directory that is required to store
+ * thumbnails for a particular @size exist and attempts to create it
+ * if necessary.
+ *
+ * You shouldn't have to call this function directly since
+ * gimp_thumbnail_save_thumb() and gimp_thumbnail_save_failure() will
+ * do this for you.
+ *
+ * Return value: %TRUE is the directory exists, %FALSE if it could not
+ * be created
+ **/
+gboolean
+gimp_thumb_ensure_thumb_dir (GimpThumbSize size,
+ GError **error)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ size = gimp_thumb_size (size);
+
+ if (g_file_test (thumb_subdirs[size], G_FILE_TEST_IS_DIR))
+ return TRUE;
+
+ if (g_file_test (thumb_dir, G_FILE_TEST_IS_DIR) ||
+ (g_mkdir_with_parents (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0))
+ {
+ if (size == 0)
+ g_mkdir_with_parents (thumb_fail_subdir, S_IRUSR | S_IWUSR | S_IXUSR);
+
+ g_mkdir_with_parents (thumb_subdirs[size], S_IRUSR | S_IWUSR | S_IXUSR);
+ }
+
+ if (g_file_test (thumb_subdirs[size], G_FILE_TEST_IS_DIR))
+ return TRUE;
+
+ g_set_error (error,
+ GIMP_THUMB_ERROR, GIMP_THUMB_ERROR_MKDIR,
+ _("Failed to create thumbnail folder '%s'."),
+ thumb_subdirs[size]);
+
+ return FALSE;
+}
+
+/**
+ * gimp_thumb_ensure_thumb_dir_local:
+ * @dirname: the basename of the dir, without the actual dirname itself
+ * @size: a GimpThumbSize
+ * @error: return location for possible errors
+ *
+ * This function checks if the directory that is required to store
+ * local thumbnails for a particular @size exist and attempts to
+ * create it if necessary.
+ *
+ * You shouldn't have to call this function directly since
+ * gimp_thumbnail_save_thumb_local() will do this for you.
+ *
+ * Return value: %TRUE is the directory exists, %FALSE if it could not
+ * be created
+ *
+ * Since: 2.2
+ **/
+gboolean
+gimp_thumb_ensure_thumb_dir_local (const gchar *dirname,
+ GimpThumbSize size,
+ GError **error)
+{
+ gchar *basedir;
+ gchar *subdir;
+
+ g_return_val_if_fail (gimp_thumb_initialized, FALSE);
+ g_return_val_if_fail (dirname != NULL, FALSE);
+ g_return_val_if_fail (g_path_is_absolute (dirname), FALSE);
+ g_return_val_if_fail (size > GIMP_THUMB_SIZE_FAIL, FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ size = gimp_thumb_size (size);
+
+ subdir = g_build_filename (dirname,
+ ".thumblocal", thumb_sizenames[size],
+ NULL);
+
+ if (g_file_test (subdir, G_FILE_TEST_IS_DIR))
+ {
+ g_free (subdir);
+ return TRUE;
+ }
+
+ basedir = g_build_filename (dirname, ".thumblocal", NULL);
+
+ if (g_file_test (basedir, G_FILE_TEST_IS_DIR) ||
+ (g_mkdir (thumb_dir, S_IRUSR | S_IWUSR | S_IXUSR) == 0))
+ {
+ g_mkdir (subdir, S_IRUSR | S_IWUSR | S_IXUSR);
+ }
+
+ g_free (basedir);
+
+ if (g_file_test (subdir, G_FILE_TEST_IS_DIR))
+ {
+ g_free (subdir);
+ return TRUE;
+ }
+
+ g_set_error (error,
+ GIMP_THUMB_ERROR, GIMP_THUMB_ERROR_MKDIR,
+ _("Failed to create thumbnail folder '%s'."),
+ subdir);
+ g_free (subdir);
+
+ return FALSE;
+}
+
+/**
+ * gimp_thumb_name_from_uri:
+ * @uri: an escaped URI
+ * @size: a #GimpThumbSize
+ *
+ * Creates the name of the thumbnail file of the specified @size that
+ * belongs to an image file located at the given @uri.
+ *
+ * Return value: a newly allocated filename in the encoding of the
+ * filesystem or %NULL if @uri points to the user's
+ * thumbnail repository.
+ **/
+gchar *
+gimp_thumb_name_from_uri (const gchar *uri,
+ GimpThumbSize size)
+{
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ if (strstr (uri, thumb_dir))
+ return NULL;
+
+ size = gimp_thumb_size (size);
+
+ return g_build_filename (thumb_subdirs[size],
+ gimp_thumb_png_name (uri),
+ NULL);
+}
+
+/**
+ * gimp_thumb_name_from_uri_local:
+ * @uri: an escaped URI
+ * @size: a #GimpThumbSize
+ *
+ * Creates the name of a local thumbnail file of the specified @size
+ * that belongs to an image file located at the given @uri. Local
+ * thumbnails have been introduced with version 0.7 of the spec.
+ *
+ * Return value: a newly allocated filename in the encoding of the
+ * filesystem or %NULL if @uri is a remote file or
+ * points to the user's thumbnail repository.
+ *
+ * Since: 2.2
+ **/
+gchar *
+gimp_thumb_name_from_uri_local (const gchar *uri,
+ GimpThumbSize size)
+{
+ gchar *filename;
+ gchar *result = NULL;
+
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (size > GIMP_THUMB_SIZE_FAIL, NULL);
+
+ if (strstr (uri, thumb_dir))
+ return NULL;
+
+ filename = _gimp_thumb_filename_from_uri (uri);
+
+ if (filename)
+ {
+ const gchar *baseuri = strrchr (uri, '/');
+
+ if (baseuri && baseuri[0] && baseuri[1])
+ {
+ gchar *dirname = g_path_get_dirname (filename);
+ gint i = gimp_thumb_size (size);
+
+ result = g_build_filename (dirname,
+ ".thumblocal", thumb_sizenames[i],
+ gimp_thumb_png_name (uri),
+ NULL);
+
+ g_free (dirname);
+ }
+
+ g_free (filename);
+ }
+
+ return result;
+}
+
+/**
+ * gimp_thumb_find_thumb:
+ * @uri: an escaped URI
+ * @size: pointer to a #GimpThumbSize
+ *
+ * This function attempts to locate a thumbnail for the given
+ * @uri. First it tries the size that is stored at @size. If no
+ * thumbnail of that size is found, it will look for a larger
+ * thumbnail, then falling back to a smaller size.
+ *
+ * If the user's thumbnail repository doesn't provide a thumbnail but
+ * a local thumbnail repository exists for the folder the image is
+ * located in, the same search is done among the local thumbnails (if
+ * there are any).
+ *
+ * If a thumbnail is found, it's size is written to the variable
+ * pointer to by @size and the file location is returned.
+ *
+ * Return value: a newly allocated string in the encoding of the
+ * filesystem or %NULL if no thumbnail for @uri was found
+ **/
+gchar *
+gimp_thumb_find_thumb (const gchar *uri,
+ GimpThumbSize *size)
+{
+ gchar *result;
+
+ g_return_val_if_fail (gimp_thumb_initialized, NULL);
+ g_return_val_if_fail (uri != NULL, NULL);
+ g_return_val_if_fail (size != NULL, NULL);
+ g_return_val_if_fail (*size > GIMP_THUMB_SIZE_FAIL, NULL);
+
+ result = gimp_thumb_png_lookup (gimp_thumb_png_name (uri), NULL, size);
+
+ if (! result)
+ {
+ gchar *filename = _gimp_thumb_filename_from_uri (uri);
+
+ if (filename)
+ {
+ const gchar *baseuri = strrchr (uri, '/');
+
+ if (baseuri && baseuri[0] && baseuri[1])
+ {
+ gchar *dirname = g_path_get_dirname (filename);
+
+ result = gimp_thumb_png_lookup (gimp_thumb_png_name (baseuri + 1),
+ dirname, size);
+
+ g_free (dirname);
+ }
+
+ g_free (filename);
+ }
+ }
+
+ return result;
+}
+
+/**
+ * gimp_thumb_file_test:
+ * @filename: a filename in the encoding of the filesystem
+ * @mtime: return location for modification time
+ * @size: return location for file size
+ * @err_no: return location for system "errno"
+ *
+ * This is a convenience and portability wrapper around stat(). It
+ * checks if the given @filename exists and returns modification time
+ * and file size in 64bit integer values.
+ *
+ * Return value: The type of the file, or #GIMP_THUMB_FILE_TYPE_NONE if
+ * the file doesn't exist.
+ **/
+GimpThumbFileType
+gimp_thumb_file_test (const gchar *filename,
+ gint64 *mtime,
+ gint64 *size,
+ gint *err_no)
+{
+ GimpThumbFileType type = GIMP_THUMB_FILE_TYPE_NONE;
+ GFile *file;
+ GFileInfo *info;
+
+ g_return_val_if_fail (filename != NULL, FALSE);
+
+ file = g_file_new_for_path (filename);
+
+ info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE ","
+ G_FILE_ATTRIBUTE_STANDARD_SIZE ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_QUERY_INFO_NONE,
+ NULL, NULL);
+
+ if (info)
+ {
+ if (mtime)
+ *mtime =
+ g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+
+ if (size)
+ *size = g_file_info_get_size (info);
+
+ if (err_no)
+ *err_no = 0;
+
+ switch (g_file_info_get_attribute_uint32 (info,
+ G_FILE_ATTRIBUTE_STANDARD_TYPE))
+ {
+ case G_FILE_TYPE_REGULAR:
+ type = GIMP_THUMB_FILE_TYPE_REGULAR;
+ break;
+
+ case G_FILE_TYPE_DIRECTORY:
+ type = GIMP_THUMB_FILE_TYPE_FOLDER;
+ break;
+
+ default:
+ type = GIMP_THUMB_FILE_TYPE_SPECIAL;
+ break;
+ }
+
+ g_object_unref (info);
+ }
+ else
+ {
+ if (mtime) *mtime = 0;
+ if (size) *size = 0;
+ if (err_no) *err_no = ENOENT;
+ }
+
+ g_object_unref (file);
+
+ return type;
+}
+
+/**
+ * gimp_thumbs_delete_for_uri:
+ * @uri: an escaped URI
+ *
+ * Deletes all thumbnails for the image file specified by @uri from the
+ * user's thumbnail repository.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_thumbs_delete_for_uri (const gchar *uri)
+{
+ gint i;
+
+ g_return_if_fail (gimp_thumb_initialized);
+ g_return_if_fail (uri != NULL);
+
+ for (i = 0; i < thumb_num_sizes; i++)
+ {
+ gchar *filename = gimp_thumb_name_from_uri (uri, thumb_sizes[i]);
+
+ if (filename)
+ {
+ g_unlink (filename);
+ g_free (filename);
+ }
+ }
+}
+
+/**
+ * gimp_thumbs_delete_for_uri_local:
+ * @uri: an escaped URI
+ *
+ * Deletes all thumbnails for the image file specified by @uri from
+ * the local thumbnail repository.
+ *
+ * Since: 2.2
+ **/
+void
+gimp_thumbs_delete_for_uri_local (const gchar *uri)
+{
+ gint i;
+
+ g_return_if_fail (gimp_thumb_initialized);
+ g_return_if_fail (uri != NULL);
+
+ for (i = 0; i < thumb_num_sizes; i++)
+ {
+ gchar *filename = gimp_thumb_name_from_uri_local (uri, thumb_sizes[i]);
+
+ if (filename)
+ {
+ g_unlink (filename);
+ g_free (filename);
+ }
+ }
+}
+
+void
+_gimp_thumbs_delete_others (const gchar *uri,
+ GimpThumbSize size)
+{
+ gint i;
+
+ g_return_if_fail (gimp_thumb_initialized);
+ g_return_if_fail (uri != NULL);
+
+ size = gimp_thumb_size (size);
+
+ for (i = 0; i < thumb_num_sizes; i++)
+ {
+ gchar *filename;
+
+ if (i == size)
+ continue;
+
+ filename = gimp_thumb_name_from_uri (uri, thumb_sizes[i]);
+ if (filename)
+ {
+ g_unlink (filename);
+ g_free (filename);
+ }
+ }
+}
+
+gchar *
+_gimp_thumb_filename_from_uri (const gchar *uri)
+{
+ gchar *filename;
+ gchar *hostname;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ filename = g_filename_from_uri (uri, &hostname, NULL);
+
+ if (!filename)
+ return NULL;
+
+ if (hostname)
+ {
+ /* we have a file: URI with a hostname */
+
+#ifdef G_OS_WIN32
+ /* on Win32, create a valid UNC path and use it as the filename */
+ gchar *tmp = g_build_filename ("//", hostname, filename, NULL);
+
+ g_free (filename);
+ filename = tmp;
+#else
+ /* otherwise return NULL, caller should use URI then */
+ g_free (filename);
+ filename = NULL;
+#endif
+
+ g_free (hostname);
+ }
+
+ return filename;
+}
+
+static void
+gimp_thumb_exit (void)
+{
+ gint i;
+
+ g_free (thumb_dir);
+ g_free (thumb_sizes);
+ g_free (thumb_sizenames);
+ for (i = 0; i < thumb_num_sizes; i++)
+ g_free (thumb_subdirs[i]);
+ g_free (thumb_subdirs);
+ g_free (thumb_fail_subdir);
+
+ thumb_num_sizes = 0;
+ thumb_sizes = NULL;
+ thumb_sizenames = NULL;
+ thumb_dir = NULL;
+ thumb_subdirs = NULL;
+ thumb_fail_subdir = NULL;
+ gimp_thumb_initialized = FALSE;
+}
+
+static gint
+gimp_thumb_size (GimpThumbSize size)
+{
+ gint i = 0;
+
+ if (size > GIMP_THUMB_SIZE_FAIL)
+ {
+ for (i = 1;
+ i < thumb_num_sizes && thumb_sizes[i] < size;
+ i++)
+ /* nothing */;
+
+ if (i == thumb_num_sizes)
+ i--;
+ }
+
+ return i;
+}
+
+static gchar *
+gimp_thumb_png_lookup (const gchar *name,
+ const gchar *basedir,
+ GimpThumbSize *size)
+{
+ gchar *thumb_name = NULL;
+ gchar **subdirs = NULL;
+ gint i, n;
+
+ if (basedir)
+ {
+ gchar *dir = g_build_filename (basedir, ".thumblocal", NULL);
+
+ if (g_file_test (basedir, G_FILE_TEST_IS_DIR))
+ {
+ gint i;
+
+ subdirs = g_new (gchar *, thumb_num_sizes);
+
+ subdirs[0] = NULL; /* GIMP_THUMB_SIZE_FAIL */
+
+ for (i = 1; i < thumb_num_sizes; i++)
+ subdirs[i] = g_build_filename (dir, thumb_sizenames[i], NULL);
+ }
+
+ g_free (dir);
+ }
+ else
+ {
+ subdirs = thumb_subdirs;
+ }
+
+ if (! subdirs)
+ return NULL;
+
+ i = n = gimp_thumb_size (*size);
+
+ for (; i < thumb_num_sizes; i++)
+ {
+ if (! subdirs[i])
+ continue;
+
+ thumb_name = g_build_filename (subdirs[i], name, NULL);
+
+ if (gimp_thumb_file_test (thumb_name,
+ NULL, NULL,
+ NULL) == GIMP_THUMB_FILE_TYPE_REGULAR)
+ {
+ *size = thumb_sizes[i];
+ goto finish;
+ }
+
+ g_free (thumb_name);
+ }
+
+ for (i = n - 1; i >= 0; i--)
+ {
+ if (! subdirs[i])
+ continue;
+
+ thumb_name = g_build_filename (subdirs[i], name, NULL);
+
+ if (gimp_thumb_file_test (thumb_name,
+ NULL, NULL,
+ NULL) == GIMP_THUMB_FILE_TYPE_REGULAR)
+ {
+ *size = thumb_sizes[i];
+ goto finish;
+ }
+
+ g_free (thumb_name);
+ }
+
+ thumb_name = NULL;
+
+ finish:
+ if (basedir)
+ {
+ for (i = 0; i < thumb_num_sizes; i++)
+ g_free (subdirs[i]);
+ g_free (subdirs);
+ }
+
+ return thumb_name;
+}
+
+static const gchar *
+gimp_thumb_png_name (const gchar *uri)
+{
+ static gchar name[40];
+
+ GChecksum *checksum;
+ guchar digest[16];
+ gsize len = sizeof (digest);
+ gsize i;
+
+ checksum = g_checksum_new (G_CHECKSUM_MD5);
+ g_checksum_update (checksum, (const guchar *) uri, -1);
+ g_checksum_get_digest (checksum, digest, &len);
+ g_checksum_free (checksum);
+
+ for (i = 0; i < len; i++)
+ {
+ guchar n;
+
+ n = (digest[i] >> 4) & 0xF;
+ name[i * 2] = (n > 9) ? 'a' + n - 10 : '0' + n;
+
+ n = digest[i] & 0xF;
+ name[i * 2 + 1] = (n > 9) ? 'a' + n - 10 : '0' + n;
+ }
+
+ strncpy (name + 32, ".png", 5);
+
+ return (const gchar *) name;
+}