1 /* LIBGIMP - The GIMP Library
2 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3 *
4 * gimpconfig-path.c
5 * Copyright (C) 2001 Sven Neumann <sven@gimp.org>
6 *
7 * This library is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 3 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see
19 * <https://www.gnu.org/licenses/>.
20 */
21
22 #include "config.h"
23
24 #include <stdio.h>
25 #include <string.h>
26
27 #include <gio/gio.h>
28
29 #include "libgimpbase/gimpbase.h"
30
31 #include "gimpconfig-error.h"
32 #include "gimpconfig-path.h"
33
34 #include "libgimp/libgimp-intl.h"
35
36
37 /**
38 * SECTION: gimpconfig-path
39 * @title: GimpConfig-path
40 * @short_description: File path utilities for libgimpconfig.
41 *
42 * File path utilities for libgimpconfig.
43 **/
44
45
46 /**
47 * gimp_config_path_get_type:
48 *
49 * Reveals the object type
50 *
51 * Returns: the #GType for a GimpConfigPath string property
52 *
53 * Since: 2.4
54 **/
55 GType
gimp_config_path_get_type(void)56 gimp_config_path_get_type (void)
57 {
58 static GType path_type = 0;
59
60 if (! path_type)
61 {
62 const GTypeInfo type_info = { 0, };
63
64 path_type = g_type_register_static (G_TYPE_STRING, "GimpConfigPath",
65 &type_info, 0);
66 }
67
68 return path_type;
69 }
70
71
72 /*
73 * GIMP_TYPE_PARAM_CONFIG_PATH
74 */
75
76 #define GIMP_PARAM_SPEC_CONFIG_PATH(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), GIMP_TYPE_PARAM_CONFIG_PATH, GimpParamSpecConfigPath))
77
78 typedef struct _GimpParamSpecConfigPath GimpParamSpecConfigPath;
79
80 struct _GimpParamSpecConfigPath
81 {
82 GParamSpecString parent_instance;
83
84 GimpConfigPathType type;
85 };
86
87 static void gimp_param_config_path_class_init (GParamSpecClass *class);
88
89 /**
90 * gimp_param_config_path_get_type:
91 *
92 * Reveals the object type
93 *
94 * Returns: the #GType for a directory path object
95 *
96 * Since: 2.4
97 **/
98 GType
gimp_param_config_path_get_type(void)99 gimp_param_config_path_get_type (void)
100 {
101 static GType spec_type = 0;
102
103 if (! spec_type)
104 {
105 const GTypeInfo type_info =
106 {
107 sizeof (GParamSpecClass),
108 NULL, NULL,
109 (GClassInitFunc) gimp_param_config_path_class_init,
110 NULL, NULL,
111 sizeof (GimpParamSpecConfigPath),
112 0, NULL, NULL
113 };
114
115 spec_type = g_type_register_static (G_TYPE_PARAM_STRING,
116 "GimpParamConfigPath",
117 &type_info, 0);
118 }
119
120 return spec_type;
121 }
122
123 static void
gimp_param_config_path_class_init(GParamSpecClass * class)124 gimp_param_config_path_class_init (GParamSpecClass *class)
125 {
126 class->value_type = GIMP_TYPE_CONFIG_PATH;
127 }
128
129 /**
130 * gimp_param_spec_config_path:
131 * @name: Canonical name of the param
132 * @nick: Nickname of the param
133 * @blurb: Brief description of param.
134 * @type: a #GimpConfigPathType value.
135 * @default_value: Value to use if none is assigned.
136 * @flags: a combination of #GParamFlags
137 *
138 * Creates a param spec to hold a filename, dir name,
139 * or list of file or dir names.
140 * See g_param_spec_internal() for more information.
141 *
142 * Returns: a newly allocated #GParamSpec instance
143 *
144 * Since: 2.4
145 **/
146 GParamSpec *
gimp_param_spec_config_path(const gchar * name,const gchar * nick,const gchar * blurb,GimpConfigPathType type,const gchar * default_value,GParamFlags flags)147 gimp_param_spec_config_path (const gchar *name,
148 const gchar *nick,
149 const gchar *blurb,
150 GimpConfigPathType type,
151 const gchar *default_value,
152 GParamFlags flags)
153 {
154 GParamSpecString *pspec;
155
156 pspec = g_param_spec_internal (GIMP_TYPE_PARAM_CONFIG_PATH,
157 name, nick, blurb, flags);
158
159 pspec->default_value = g_strdup (default_value);
160
161 GIMP_PARAM_SPEC_CONFIG_PATH (pspec)->type = type;
162
163 return G_PARAM_SPEC (pspec);
164 }
165
166 /**
167 * gimp_param_spec_config_path_type:
168 * @pspec: A #GParamSpec for a path param
169 *
170 * Tells whether the path param encodes a filename,
171 * dir name, or list of file or dir names.
172 *
173 * Returns: a #GimpConfigPathType value
174 *
175 * Since: 2.4
176 **/
177 GimpConfigPathType
gimp_param_spec_config_path_type(GParamSpec * pspec)178 gimp_param_spec_config_path_type (GParamSpec *pspec)
179 {
180 g_return_val_if_fail (GIMP_IS_PARAM_SPEC_CONFIG_PATH (pspec), 0);
181
182 return GIMP_PARAM_SPEC_CONFIG_PATH (pspec)->type;
183 }
184
185
186 /*
187 * GimpConfig path utilities
188 */
189
190 static gchar * gimp_config_path_expand_only (const gchar *path,
191 GError **error) G_GNUC_MALLOC;
192 static inline gchar * gimp_config_path_extract_token (const gchar **str);
193 static gchar * gimp_config_path_unexpand_only (const gchar *path) G_GNUC_MALLOC;
194
195
196 /**
197 * gimp_config_build_data_path:
198 * @name: directory name (in UTF-8 encoding)
199 *
200 * Creates a search path as it is used in the gimprc file. The path
201 * returned by gimp_config_build_data_path() includes a directory
202 * below the user's gimp directory and one in the system-wide data
203 * directory.
204 *
205 * Note that you cannot use this path directly with gimp_path_parse().
206 * As it is in the gimprc notation, you first need to expand and
207 * recode it using gimp_config_path_expand().
208 *
209 * Returns: a newly allocated string
210 *
211 * Since: 2.4
212 **/
213 gchar *
gimp_config_build_data_path(const gchar * name)214 gimp_config_build_data_path (const gchar *name)
215 {
216 return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name,
217 G_SEARCHPATH_SEPARATOR_S,
218 "${gimp_data_dir}", G_DIR_SEPARATOR_S, name,
219 NULL);
220 }
221
222 /**
223 * gimp_config_build_plug_in_path:
224 * @name: directory name (in UTF-8 encoding)
225 *
226 * Creates a search path as it is used in the gimprc file. The path
227 * returned by gimp_config_build_plug_in_path() includes a directory
228 * below the user's gimp directory and one in the system-wide plug-in
229 * directory.
230 *
231 * Note that you cannot use this path directly with gimp_path_parse().
232 * As it is in the gimprc notation, you first need to expand and
233 * recode it using gimp_config_path_expand().
234 *
235 * Returns: a newly allocated string
236 *
237 * Since: 2.4
238 **/
239 gchar *
gimp_config_build_plug_in_path(const gchar * name)240 gimp_config_build_plug_in_path (const gchar *name)
241 {
242 return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name,
243 G_SEARCHPATH_SEPARATOR_S,
244 "${gimp_plug_in_dir}", G_DIR_SEPARATOR_S, name,
245 NULL);
246 }
247
248 /**
249 * gimp_config_build_writable_path:
250 * @name: directory name (in UTF-8 encoding)
251 *
252 * Creates a search path as it is used in the gimprc file. The path
253 * returned by gimp_config_build_writable_path() is just the writable
254 * parts of the search path constructed by gimp_config_build_data_path().
255 *
256 * Note that you cannot use this path directly with gimp_path_parse().
257 * As it is in the gimprc notation, you first need to expand and
258 * recode it using gimp_config_path_expand().
259 *
260 * Returns: a newly allocated string
261 *
262 * Since: 2.4
263 **/
264 gchar *
gimp_config_build_writable_path(const gchar * name)265 gimp_config_build_writable_path (const gchar *name)
266 {
267 return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name, NULL);
268 }
269
270
271 /**
272 * gimp_config_path_expand:
273 * @path: a NUL-terminated string in UTF-8 encoding
274 * @recode: whether to convert to the filesystem's encoding
275 * @error: return location for errors
276 *
277 * Paths as stored in gimprc and other config files have to be treated
278 * special. The string may contain special identifiers such as for
279 * example ${gimp_dir} that have to be substituted before use. Also
280 * the user's filesystem may be in a different encoding than UTF-8
281 * (which is what is used for the gimprc). This function does the
282 * variable substitution for you and can also attempt to convert to
283 * the filesystem encoding.
284 *
285 * To reverse the expansion, use gimp_config_path_unexpand().
286 *
287 * Return value: a newly allocated NUL-terminated string
288 *
289 * Since: 2.4
290 **/
291 gchar *
gimp_config_path_expand(const gchar * path,gboolean recode,GError ** error)292 gimp_config_path_expand (const gchar *path,
293 gboolean recode,
294 GError **error)
295 {
296 g_return_val_if_fail (path != NULL, NULL);
297 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
298
299 if (recode)
300 {
301 gchar *retval;
302 gchar *expanded = gimp_config_path_expand_only (path, error);
303
304 if (! expanded)
305 return NULL;
306
307 retval = g_filename_from_utf8 (expanded, -1, NULL, NULL, error);
308
309 g_free (expanded);
310
311 return retval;
312 }
313
314 return gimp_config_path_expand_only (path, error);
315 }
316
317 /**
318 * gimp_config_path_expand_to_files:
319 * @path: a NUL-terminated string in UTF-8 encoding
320 * @error: return location for errors
321 *
322 * Paths as stored in the gimprc have to be treated special. The
323 * string may contain special identifiers such as for example
324 * ${gimp_dir} that have to be substituted before use. Also the user's
325 * filesystem may be in a different encoding than UTF-8 (which is what
326 * is used for the gimprc).
327 *
328 * This function runs @path through gimp_config_path_expand() and
329 * gimp_path_parse(), then turns the filenames returned by
330 * gimp_path_parse() into GFile using g_file_new_for_path().
331 *
332 * Return value: a #GList of newly allocated #GFile objects.
333 *
334 * Since: 2.10
335 **/
336 GList *
gimp_config_path_expand_to_files(const gchar * path,GError ** error)337 gimp_config_path_expand_to_files (const gchar *path,
338 GError **error)
339 {
340 GList *files;
341 GList *list;
342 gchar *expanded;
343
344 g_return_val_if_fail (path != NULL, NULL);
345 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
346
347 expanded = gimp_config_path_expand (path, TRUE, error);
348
349 if (! expanded)
350 return NULL;
351
352 files = gimp_path_parse (expanded, 256, FALSE, NULL);
353
354 g_free (expanded);
355
356 for (list = files; list; list = g_list_next (list))
357 {
358 gchar *dir = list->data;
359
360 list->data = g_file_new_for_path (dir);
361 g_free (dir);
362 }
363
364 return files;
365 }
366
367 /**
368 * gimp_config_path_unexpand:
369 * @path: a NUL-terminated string
370 * @recode: whether @path is in filesystem encoding or UTF-8
371 * @error: return location for errors
372 *
373 * The inverse operation of gimp_config_path_expand()
374 *
375 * This function takes a @path and tries to substitute the first
376 * elements by well-known special identifiers such as for example
377 * ${gimp_dir}. The unexpanded path can then be stored in gimprc and
378 * other config files.
379 *
380 * If @recode is %TRUE then @path is in local filesystem encoding,
381 * if @recode is %FALSE then @path is assumed to be UTF-8.
382 *
383 * Return value: a newly allocated NUL-terminated UTF-8 string
384 *
385 * Since: 2.10
386 **/
387 gchar *
gimp_config_path_unexpand(const gchar * path,gboolean recode,GError ** error)388 gimp_config_path_unexpand (const gchar *path,
389 gboolean recode,
390 GError **error)
391 {
392 g_return_val_if_fail (path != NULL, NULL);
393 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
394
395 if (recode)
396 {
397 gchar *retval;
398 gchar *utf8 = g_filename_to_utf8 (path, -1, NULL, NULL, error);
399
400 if (! utf8)
401 return NULL;
402
403 retval = gimp_config_path_unexpand_only (utf8);
404
405 g_free (utf8);
406
407 return retval;
408 }
409
410 return gimp_config_path_unexpand_only (path);
411 }
412
413 /**
414 * gimp_file_new_for_config_path:
415 * @path: a NUL-terminated string in UTF-8 encoding
416 * @error: return location for errors
417 *
418 * Expands @path using gimp_config_path_expand() and returns a #GFile
419 * for the expanded path.
420 *
421 * To reverse the expansion, use gimp_file_get_config_path().
422 *
423 * Return value: a newly allocated #GFile, or %NULL if the expansion failed.
424 *
425 * Since: 2.10
426 **/
427 GFile *
gimp_file_new_for_config_path(const gchar * path,GError ** error)428 gimp_file_new_for_config_path (const gchar *path,
429 GError **error)
430 {
431 GFile *file = NULL;
432 gchar *expanded;
433
434 g_return_val_if_fail (path != NULL, NULL);
435 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
436
437 expanded = gimp_config_path_expand (path, TRUE, error);
438
439 if (expanded)
440 {
441 file = g_file_new_for_path (expanded);
442 g_free (expanded);
443 }
444
445 return file;
446 }
447
448 /**
449 * gimp_file_get_config_path:
450 * @file: a #GFile
451 * @error: return location for errors
452 *
453 * Unexpands @file's path using gimp_config_path_unexpand() and
454 * returns the unexpanded path.
455 *
456 * The inverse operation of gimp_file_new_for_config_path().
457 *
458 * Return value: a newly allocated NUL-terminated UTF-8 string, or %NULL if
459 * unexpanding failed.
460 *
461 * Since: 2.10
462 **/
463 gchar *
gimp_file_get_config_path(GFile * file,GError ** error)464 gimp_file_get_config_path (GFile *file,
465 GError **error)
466 {
467 gchar *unexpanded = NULL;
468 gchar *path;
469
470 g_return_val_if_fail (G_IS_FILE (file), NULL);
471 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
472
473 path = g_file_get_path (file);
474
475 if (path)
476 {
477 unexpanded = gimp_config_path_unexpand (path, TRUE, error);
478 g_free (path);
479 }
480 else
481 {
482 g_set_error_literal (error, 0, 0,
483 _("File has no path representation"));
484 }
485
486 return unexpanded;
487 }
488
489
490 /* private functions */
491
492 #define SUBSTS_ALLOC 4
493
494 static gchar *
gimp_config_path_expand_only(const gchar * path,GError ** error)495 gimp_config_path_expand_only (const gchar *path,
496 GError **error)
497 {
498 const gchar *home;
499 const gchar *p;
500 const gchar *s;
501 gchar *n;
502 gchar *token;
503 gchar *filename = NULL;
504 gchar *expanded = NULL;
505 gchar **substs = NULL;
506 guint n_substs = 0;
507 gint length = 0;
508 gint i;
509
510 home = g_get_home_dir ();
511 if (home)
512 home = gimp_filename_to_utf8 (home);
513
514 p = path;
515
516 while (*p)
517 {
518 if (*p == '~' && home)
519 {
520 length += strlen (home);
521 p += 1;
522 }
523 else if ((token = gimp_config_path_extract_token (&p)) != NULL)
524 {
525 for (i = 0; i < n_substs; i++)
526 if (strcmp (substs[2*i], token) == 0)
527 break;
528
529 if (i < n_substs)
530 {
531 s = substs[2*i+1];
532 }
533 else
534 {
535 s = NULL;
536
537 if (strcmp (token, "gimp_dir") == 0)
538 s = gimp_directory ();
539 else if (strcmp (token, "gimp_data_dir") == 0)
540 s = gimp_data_directory ();
541 else if (strcmp (token, "gimp_plug_in_dir") == 0 ||
542 strcmp (token, "gimp_plugin_dir") == 0)
543 s = gimp_plug_in_directory ();
544 else if (strcmp (token, "gimp_sysconf_dir") == 0)
545 s = gimp_sysconf_directory ();
546 else if (strcmp (token, "gimp_installation_dir") == 0)
547 s = gimp_installation_directory ();
548 else if (strcmp (token, "gimp_cache_dir") == 0)
549 s = gimp_cache_directory ();
550 else if (strcmp (token, "gimp_temp_dir") == 0)
551 s = gimp_temp_directory ();
552
553 if (!s)
554 s = g_getenv (token);
555
556 #ifdef G_OS_WIN32
557 /* The default user gimprc on Windows references
558 * ${TEMP}, but not all Windows installations have that
559 * environment variable, even if it should be kinda
560 * standard. So special-case it.
561 */
562 if (!s && strcmp (token, "TEMP") == 0)
563 s = g_get_tmp_dir ();
564 #endif /* G_OS_WIN32 */
565 }
566
567 if (!s)
568 {
569 g_set_error (error, GIMP_CONFIG_ERROR, GIMP_CONFIG_ERROR_PARSE,
570 _("Cannot expand ${%s}"), token);
571 g_free (token);
572 goto cleanup;
573 }
574
575 if (n_substs % SUBSTS_ALLOC == 0)
576 substs = g_renew (gchar *, substs, 2 * (n_substs + SUBSTS_ALLOC));
577
578 substs[2*n_substs] = token;
579 substs[2*n_substs + 1] = (gchar *) gimp_filename_to_utf8 (s);
580
581 length += strlen (substs[2*n_substs + 1]);
582
583 n_substs++;
584 }
585 else
586 {
587 length += g_utf8_skip[(const guchar) *p];
588 p = g_utf8_next_char (p);
589 }
590 }
591
592 if (n_substs == 0)
593 return g_strdup (path);
594
595 expanded = g_new (gchar, length + 1);
596
597 p = path;
598 n = expanded;
599
600 while (*p)
601 {
602 if (*p == '~' && home)
603 {
604 *n = '\0';
605 strcat (n, home);
606 n += strlen (home);
607 p += 1;
608 }
609 else if ((token = gimp_config_path_extract_token (&p)) != NULL)
610 {
611 for (i = 0; i < n_substs; i++)
612 {
613 if (strcmp (substs[2*i], token) == 0)
614 {
615 s = substs[2*i+1];
616
617 *n = '\0';
618 strcat (n, s);
619 n += strlen (s);
620
621 break;
622 }
623 }
624
625 g_free (token);
626 }
627 else
628 {
629 *n++ = *p++;
630 }
631 }
632
633 *n = '\0';
634
635 cleanup:
636 for (i = 0; i < n_substs; i++)
637 g_free (substs[2*i]);
638
639 g_free (substs);
640 g_free (filename);
641
642 return expanded;
643 }
644
645 static inline gchar *
gimp_config_path_extract_token(const gchar ** str)646 gimp_config_path_extract_token (const gchar **str)
647 {
648 const gchar *p;
649 gchar *token;
650
651 if (strncmp (*str, "${", 2))
652 return NULL;
653
654 p = *str + 2;
655
656 while (*p && (*p != '}'))
657 p = g_utf8_next_char (p);
658
659 if (! *p)
660 return NULL;
661
662 token = g_strndup (*str + 2, g_utf8_pointer_to_offset (*str + 2, p));
663
664 *str = p + 1; /* after the closing bracket */
665
666 return token;
667 }
668
669 static gchar *
gimp_config_path_unexpand_only(const gchar * path)670 gimp_config_path_unexpand_only (const gchar *path)
671 {
672 const struct
673 {
674 const gchar *id;
675 const gchar *prefix;
676 }
677 identifiers[] =
678 {
679 { "${gimp_plug_in_dir}", gimp_plug_in_directory () },
680 { "${gimp_data_dir}", gimp_data_directory () },
681 { "${gimp_sysconf_dir}", gimp_sysconf_directory () },
682 { "${gimp_installation_dir}", gimp_installation_directory () },
683 { "${gimp_cache_dir}", gimp_cache_directory () },
684 { "${gimp_temp_dir}", gimp_temp_directory () },
685 { "${gimp_dir}", gimp_directory () }
686 };
687
688 GList *files;
689 GList *list;
690 gchar *unexpanded;
691
692 files = gimp_path_parse (path, 256, FALSE, NULL);
693
694 for (list = files; list; list = g_list_next (list))
695 {
696 gchar *dir = list->data;
697 gint i;
698
699 for (i = 0; i < G_N_ELEMENTS (identifiers); i++)
700 {
701 if (g_str_has_prefix (dir, identifiers[i].prefix))
702 {
703 gchar *tmp = g_strconcat (identifiers[i].id,
704 dir + strlen (identifiers[i].prefix),
705 NULL);
706
707 g_free (dir);
708 list->data = tmp;
709
710 break;
711 }
712 }
713 }
714
715 unexpanded = gimp_path_to_str (files);
716
717 gimp_path_free (files);
718
719 return unexpanded;
720 }
721