diff options
Diffstat (limited to 'libgimpconfig/gimpconfig-path.c')
-rw-r--r-- | libgimpconfig/gimpconfig-path.c | 720 |
1 files changed, 720 insertions, 0 deletions
diff --git a/libgimpconfig/gimpconfig-path.c b/libgimpconfig/gimpconfig-path.c new file mode 100644 index 0000000..8d87e4a --- /dev/null +++ b/libgimpconfig/gimpconfig-path.c @@ -0,0 +1,720 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis + * + * gimpconfig-path.c + * Copyright (C) 2001 Sven Neumann <sven@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 + * Library 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 <stdio.h> +#include <string.h> + +#include <gio/gio.h> + +#include "libgimpbase/gimpbase.h" + +#include "gimpconfig-error.h" +#include "gimpconfig-path.h" + +#include "libgimp/libgimp-intl.h" + + +/** + * SECTION: gimpconfig-path + * @title: GimpConfig-path + * @short_description: File path utilities for libgimpconfig. + * + * File path utilities for libgimpconfig. + **/ + + +/** + * gimp_config_path_get_type: + * + * Reveals the object type + * + * Returns: the #GType for a GimpConfigPath string property + * + * Since: 2.4 + **/ +GType +gimp_config_path_get_type (void) +{ + static GType path_type = 0; + + if (! path_type) + { + const GTypeInfo type_info = { 0, }; + + path_type = g_type_register_static (G_TYPE_STRING, "GimpConfigPath", + &type_info, 0); + } + + return path_type; +} + + +/* + * GIMP_TYPE_PARAM_CONFIG_PATH + */ + +#define GIMP_PARAM_SPEC_CONFIG_PATH(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_CONFIG_PATH, GimpParamSpecConfigPath)) + +typedef struct _GimpParamSpecConfigPath GimpParamSpecConfigPath; + +struct _GimpParamSpecConfigPath +{ + GParamSpecString parent_instance; + + GimpConfigPathType type; +}; + +static void gimp_param_config_path_class_init (GParamSpecClass *class); + +/** + * gimp_param_config_path_get_type: + * + * Reveals the object type + * + * Returns: the #GType for a directory path object + * + * Since: 2.4 + **/ +GType +gimp_param_config_path_get_type (void) +{ + static GType spec_type = 0; + + if (! spec_type) + { + const GTypeInfo type_info = + { + sizeof (GParamSpecClass), + NULL, NULL, + (GClassInitFunc) gimp_param_config_path_class_init, + NULL, NULL, + sizeof (GimpParamSpecConfigPath), + 0, NULL, NULL + }; + + spec_type = g_type_register_static (G_TYPE_PARAM_STRING, + "GimpParamConfigPath", + &type_info, 0); + } + + return spec_type; +} + +static void +gimp_param_config_path_class_init (GParamSpecClass *class) +{ + class->value_type = GIMP_TYPE_CONFIG_PATH; +} + +/** + * gimp_param_spec_config_path: + * @name: Canonical name of the param + * @nick: Nickname of the param + * @blurb: Brief description of param. + * @type: a #GimpConfigPathType value. + * @default_value: Value to use if none is assigned. + * @flags: a combination of #GParamFlags + * + * Creates a param spec to hold a filename, dir name, + * or list of file or dir names. + * See g_param_spec_internal() for more information. + * + * Returns: a newly allocated #GParamSpec instance + * + * Since: 2.4 + **/ +GParamSpec * +gimp_param_spec_config_path (const gchar *name, + const gchar *nick, + const gchar *blurb, + GimpConfigPathType type, + const gchar *default_value, + GParamFlags flags) +{ + GParamSpecString *pspec; + + pspec = g_param_spec_internal (GIMP_TYPE_PARAM_CONFIG_PATH, + name, nick, blurb, flags); + + pspec->default_value = g_strdup (default_value); + + GIMP_PARAM_SPEC_CONFIG_PATH (pspec)->type = type; + + return G_PARAM_SPEC (pspec); +} + +/** + * gimp_param_spec_config_path_type: + * @pspec: A #GParamSpec for a path param + * + * Tells whether the path param encodes a filename, + * dir name, or list of file or dir names. + * + * Returns: a #GimpConfigPathType value + * + * Since: 2.4 + **/ +GimpConfigPathType +gimp_param_spec_config_path_type (GParamSpec *pspec) +{ + g_return_val_if_fail (GIMP_IS_PARAM_SPEC_CONFIG_PATH (pspec), 0); + + return GIMP_PARAM_SPEC_CONFIG_PATH (pspec)->type; +} + + +/* + * GimpConfig path utilities + */ + +static gchar * gimp_config_path_expand_only (const gchar *path, + GError **error) G_GNUC_MALLOC; +static inline gchar * gimp_config_path_extract_token (const gchar **str); +static gchar * gimp_config_path_unexpand_only (const gchar *path) G_GNUC_MALLOC; + + +/** + * gimp_config_build_data_path: + * @name: directory name (in UTF-8 encoding) + * + * Creates a search path as it is used in the gimprc file. The path + * returned by gimp_config_build_data_path() includes a directory + * below the user's gimp directory and one in the system-wide data + * directory. + * + * Note that you cannot use this path directly with gimp_path_parse(). + * As it is in the gimprc notation, you first need to expand and + * recode it using gimp_config_path_expand(). + * + * Returns: a newly allocated string + * + * Since: 2.4 + **/ +gchar * +gimp_config_build_data_path (const gchar *name) +{ + return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name, + G_SEARCHPATH_SEPARATOR_S, + "${gimp_data_dir}", G_DIR_SEPARATOR_S, name, + NULL); +} + +/** + * gimp_config_build_plug_in_path: + * @name: directory name (in UTF-8 encoding) + * + * Creates a search path as it is used in the gimprc file. The path + * returned by gimp_config_build_plug_in_path() includes a directory + * below the user's gimp directory and one in the system-wide plug-in + * directory. + * + * Note that you cannot use this path directly with gimp_path_parse(). + * As it is in the gimprc notation, you first need to expand and + * recode it using gimp_config_path_expand(). + * + * Returns: a newly allocated string + * + * Since: 2.4 + **/ +gchar * +gimp_config_build_plug_in_path (const gchar *name) +{ + return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name, + G_SEARCHPATH_SEPARATOR_S, + "${gimp_plug_in_dir}", G_DIR_SEPARATOR_S, name, + NULL); +} + +/** + * gimp_config_build_writable_path: + * @name: directory name (in UTF-8 encoding) + * + * Creates a search path as it is used in the gimprc file. The path + * returned by gimp_config_build_writable_path() is just the writable + * parts of the search path constructed by gimp_config_build_data_path(). + * + * Note that you cannot use this path directly with gimp_path_parse(). + * As it is in the gimprc notation, you first need to expand and + * recode it using gimp_config_path_expand(). + * + * Returns: a newly allocated string + * + * Since: 2.4 + **/ +gchar * +gimp_config_build_writable_path (const gchar *name) +{ + return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name, NULL); +} + + +/** + * gimp_config_path_expand: + * @path: a NUL-terminated string in UTF-8 encoding + * @recode: whether to convert to the filesystem's encoding + * @error: return location for errors + * + * Paths as stored in gimprc and other config files have to be treated + * special. The string may contain special identifiers such as for + * example ${gimp_dir} that have to be substituted before use. Also + * the user's filesystem may be in a different encoding than UTF-8 + * (which is what is used for the gimprc). This function does the + * variable substitution for you and can also attempt to convert to + * the filesystem encoding. + * + * To reverse the expansion, use gimp_config_path_unexpand(). + * + * Return value: a newly allocated NUL-terminated string + * + * Since: 2.4 + **/ +gchar * +gimp_config_path_expand (const gchar *path, + gboolean recode, + GError **error) +{ + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (recode) + { + gchar *retval; + gchar *expanded = gimp_config_path_expand_only (path, error); + + if (! expanded) + return NULL; + + retval = g_filename_from_utf8 (expanded, -1, NULL, NULL, error); + + g_free (expanded); + + return retval; + } + + return gimp_config_path_expand_only (path, error); +} + +/** + * gimp_config_path_expand_to_files: + * @path: a NUL-terminated string in UTF-8 encoding + * @error: return location for errors + * + * Paths as stored in the gimprc have to be treated special. The + * string may contain special identifiers such as for example + * ${gimp_dir} that have to be substituted before use. Also the user's + * filesystem may be in a different encoding than UTF-8 (which is what + * is used for the gimprc). + * + * This function runs @path through gimp_config_path_expand() and + * gimp_path_parse(), then turns the filenames returned by + * gimp_path_parse() into GFile using g_file_new_for_path(). + * + * Return value: a #GList of newly allocated #GFile objects. + * + * Since: 2.10 + **/ +GList * +gimp_config_path_expand_to_files (const gchar *path, + GError **error) +{ + GList *files; + GList *list; + gchar *expanded; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + expanded = gimp_config_path_expand (path, TRUE, error); + + if (! expanded) + return NULL; + + files = gimp_path_parse (expanded, 256, FALSE, NULL); + + g_free (expanded); + + for (list = files; list; list = g_list_next (list)) + { + gchar *dir = list->data; + + list->data = g_file_new_for_path (dir); + g_free (dir); + } + + return files; +} + +/** + * gimp_config_path_unexpand: + * @path: a NUL-terminated string + * @recode: whether @path is in filesystem encoding or UTF-8 + * @error: return location for errors + * + * The inverse operation of gimp_config_path_expand() + * + * This function takes a @path and tries to substitute the first + * elements by well-known special identifiers such as for example + * ${gimp_dir}. The unexpanded path can then be stored in gimprc and + * other config files. + * + * If @recode is %TRUE then @path is in local filesystem encoding, + * if @recode is %FALSE then @path is assumed to be UTF-8. + * + * Return value: a newly allocated NUL-terminated UTF-8 string + * + * Since: 2.10 + **/ +gchar * +gimp_config_path_unexpand (const gchar *path, + gboolean recode, + GError **error) +{ + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + if (recode) + { + gchar *retval; + gchar *utf8 = g_filename_to_utf8 (path, -1, NULL, NULL, error); + + if (! utf8) + return NULL; + + retval = gimp_config_path_unexpand_only (utf8); + + g_free (utf8); + + return retval; + } + + return gimp_config_path_unexpand_only (path); +} + +/** + * gimp_file_new_for_config_path: + * @path: a NUL-terminated string in UTF-8 encoding + * @error: return location for errors + * + * Expands @path using gimp_config_path_expand() and returns a #GFile + * for the expanded path. + * + * To reverse the expansion, use gimp_file_get_config_path(). + * + * Return value: a newly allocated #GFile, or %NULL if the expansion failed. + * + * Since: 2.10 + **/ +GFile * +gimp_file_new_for_config_path (const gchar *path, + GError **error) +{ + GFile *file = NULL; + gchar *expanded; + + g_return_val_if_fail (path != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + expanded = gimp_config_path_expand (path, TRUE, error); + + if (expanded) + { + file = g_file_new_for_path (expanded); + g_free (expanded); + } + + return file; +} + +/** + * gimp_file_get_config_path: + * @file: a #GFile + * @error: return location for errors + * + * Unexpands @file's path using gimp_config_path_unexpand() and + * returns the unexpanded path. + * + * The inverse operation of gimp_file_new_for_config_path(). + * + * Return value: a newly allocated NUL-terminated UTF-8 string, or %NULL if + * unexpanding failed. + * + * Since: 2.10 + **/ +gchar * +gimp_file_get_config_path (GFile *file, + GError **error) +{ + gchar *unexpanded = NULL; + gchar *path; + + g_return_val_if_fail (G_IS_FILE (file), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + path = g_file_get_path (file); + + if (path) + { + unexpanded = gimp_config_path_unexpand (path, TRUE, error); + g_free (path); + } + else + { + g_set_error_literal (error, 0, 0, + _("File has no path representation")); + } + + return unexpanded; +} + + +/* private functions */ + +#define SUBSTS_ALLOC 4 + +static gchar * +gimp_config_path_expand_only (const gchar *path, + GError **error) +{ + const gchar *home; + const gchar *p; + const gchar *s; + gchar *n; + gchar *token; + gchar *filename = NULL; + gchar *expanded = NULL; + gchar **substs = NULL; + guint n_substs = 0; + gint length = 0; + gint i; + + home = g_get_home_dir (); + if (home) + home = gimp_filename_to_utf8 (home); + + p = path; + + while (*p) + { + if (*p == '~' && home) + { + length += strlen (home); + p += 1; + } + else if ((token = gimp_config_path_extract_token (&p)) != NULL) + { + for (i = 0; i < n_substs; i++) + if (strcmp (substs[2*i], token) == 0) + break; + + if (i < n_substs) + { + s = substs[2*i+1]; + } + else + { + s = NULL; + + if (strcmp (token, "gimp_dir") == 0) + s = gimp_directory (); + else if (strcmp (token, "gimp_data_dir") == 0) + s = gimp_data_directory (); + else if (strcmp (token, "gimp_plug_in_dir") == 0 || + strcmp (token, "gimp_plugin_dir") == 0) + s = gimp_plug_in_directory (); + else if (strcmp (token, "gimp_sysconf_dir") == 0) + s = gimp_sysconf_directory (); + else if (strcmp (token, "gimp_installation_dir") == 0) + s = gimp_installation_directory (); + else if (strcmp (token, "gimp_cache_dir") == 0) + s = gimp_cache_directory (); + else if (strcmp (token, "gimp_temp_dir") == 0) + s = gimp_temp_directory (); + + if (!s) + s = g_getenv (token); + +#ifdef G_OS_WIN32 + /* The default user gimprc on Windows references + * ${TEMP}, but not all Windows installations have that + * environment variable, even if it should be kinda + * standard. So special-case it. + */ + if (!s && strcmp (token, "TEMP") == 0) + s = g_get_tmp_dir (); +#endif /* G_OS_WIN32 */ + } + + if (!s) + { + g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE, + _("Cannot expand ${%s}"), token); + g_free (token); + goto cleanup; + } + + if (n_substs % SUBSTS_ALLOC == 0) + substs = g_renew (gchar *, substs, 2 * (n_substs + SUBSTS_ALLOC)); + + substs[2*n_substs] = token; + substs[2*n_substs + 1] = (gchar *) gimp_filename_to_utf8 (s); + + length += strlen (substs[2*n_substs + 1]); + + n_substs++; + } + else + { + length += g_utf8_skip[(const guchar) *p]; + p = g_utf8_next_char (p); + } + } + + if (n_substs == 0) + return g_strdup (path); + + expanded = g_new (gchar, length + 1); + + p = path; + n = expanded; + + while (*p) + { + if (*p == '~' && home) + { + *n = '\0'; + strcat (n, home); + n += strlen (home); + p += 1; + } + else if ((token = gimp_config_path_extract_token (&p)) != NULL) + { + for (i = 0; i < n_substs; i++) + { + if (strcmp (substs[2*i], token) == 0) + { + s = substs[2*i+1]; + + *n = '\0'; + strcat (n, s); + n += strlen (s); + + break; + } + } + + g_free (token); + } + else + { + *n++ = *p++; + } + } + + *n = '\0'; + + cleanup: + for (i = 0; i < n_substs; i++) + g_free (substs[2*i]); + + g_free (substs); + g_free (filename); + + return expanded; +} + +static inline gchar * +gimp_config_path_extract_token (const gchar **str) +{ + const gchar *p; + gchar *token; + + if (strncmp (*str, "${", 2)) + return NULL; + + p = *str + 2; + + while (*p && (*p != '}')) + p = g_utf8_next_char (p); + + if (! *p) + return NULL; + + token = g_strndup (*str + 2, g_utf8_pointer_to_offset (*str + 2, p)); + + *str = p + 1; /* after the closing bracket */ + + return token; +} + +static gchar * +gimp_config_path_unexpand_only (const gchar *path) +{ + const struct + { + const gchar *id; + const gchar *prefix; + } + identifiers[] = + { + { "${gimp_plug_in_dir}", gimp_plug_in_directory () }, + { "${gimp_data_dir}", gimp_data_directory () }, + { "${gimp_sysconf_dir}", gimp_sysconf_directory () }, + { "${gimp_installation_dir}", gimp_installation_directory () }, + { "${gimp_cache_dir}", gimp_cache_directory () }, + { "${gimp_temp_dir}", gimp_temp_directory () }, + { "${gimp_dir}", gimp_directory () } + }; + + GList *files; + GList *list; + gchar *unexpanded; + + files = gimp_path_parse (path, 256, FALSE, NULL); + + for (list = files; list; list = g_list_next (list)) + { + gchar *dir = list->data; + gint i; + + for (i = 0; i < G_N_ELEMENTS (identifiers); i++) + { + if (g_str_has_prefix (dir, identifiers[i].prefix)) + { + gchar *tmp = g_strconcat (identifiers[i].id, + dir + strlen (identifiers[i].prefix), + NULL); + + g_free (dir); + list->data = tmp; + + break; + } + } + } + + unexpanded = gimp_path_to_str (files); + + gimp_path_free (files); + + return unexpanded; +} |