1 /* Routines for locating data files
2 Copyright (C) 2016, 2019 Free Software Foundation, Inc.
3
4 This file was written by Daiki Ueno <ueno@gnu.org>, 2016.
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <https://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 /* Specification. */
24 #include "search-path.h"
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "concat-filename.h"
30 #include "relocatable.h"
31 #include "xalloc.h"
32 #include "xmemdup0.h"
33 #include "xvasprintf.h"
34
35 typedef void (* foreach_function_ty) (const char *dir, size_t len, void *data);
36
37 struct path_array_ty {
38 char **ptr;
39 size_t len;
40 /* Transient argument for fill(). */
41 const char *sub;
42 };
43
44 static void
foreach_elements(const char * dirs,foreach_function_ty function,void * data)45 foreach_elements (const char *dirs, foreach_function_ty function, void *data)
46 {
47 const char *start = dirs;
48
49 /* Count the number of valid elements in DIRS. */
50 while (*start != '\0')
51 {
52 char *end = strchrnul (start, ':');
53
54 /* Skip empty element. */
55 if (start != end)
56 function (start, end - start, data);
57
58 if (*end == '\0')
59 break;
60
61 start = end + 1;
62 }
63 }
64
65 static void
increment(const char * dir,size_t len,void * data)66 increment (const char *dir, size_t len, void *data)
67 {
68 size_t *count = data;
69 (*count)++;
70 }
71
72 static void
fill(const char * dir,size_t len,void * data)73 fill (const char *dir, size_t len, void *data)
74 {
75 struct path_array_ty *array = data;
76 char *base, *name;
77
78 base = xmemdup0 (dir, len);
79 if (array->sub == NULL)
80 name = base;
81 else
82 {
83 name = xconcatenated_filename (base, array->sub, NULL);
84 free (base);
85 }
86
87 array->ptr[array->len++] = name;
88 }
89
90 /* Find the standard search path for data files. If SUB is not NULL, append it
91 to each directory.
92 Returns a freshly allocated NULL terminated list of freshly allocated
93 strings.
94
95 The order in the path is as follows:
96
97 1. $GETTEXTDATADIR or GETTEXTDATADIR
98 2. $GETTEXTDATADIRS
99 3. $XDG_DATA_DIRS, where each element is suffixed with "gettext"
100 4. $GETTEXTDATADIR or GETTEXTDATADIR, suffixed with PACKAGE_SUFFIX */
101 char **
get_search_path(const char * sub)102 get_search_path (const char *sub)
103 {
104 const char *gettextdatadir;
105 const char *gettextdatadirs;
106 const char *xdgdatadirs;
107 struct path_array_ty array;
108
109 /* Count how many array elements are needed. */
110 size_t count = 2;
111
112 gettextdatadirs = getenv ("GETTEXTDATADIRS");
113 if (gettextdatadirs != NULL)
114 foreach_elements (gettextdatadirs, increment, &count);
115
116 xdgdatadirs = getenv ("XDG_DATA_DIRS");
117 if (xdgdatadirs != NULL)
118 foreach_elements (xdgdatadirs, increment, &count);
119
120 /* Allocate the array. */
121 array.ptr = XCALLOC (count + 1, char *);
122 array.len = 0;
123
124 /* Fill the array. */
125 {
126 gettextdatadir = getenv ("GETTEXTDATADIR");
127 if (gettextdatadir == NULL || gettextdatadir[0] == '\0')
128 /* Make it possible to override the locator file location. This
129 is necessary for running the testsuite before "make
130 install". */
131 gettextdatadir = relocate (GETTEXTDATADIR);
132
133 /* Append element from GETTEXTDATADIR. */
134 {
135 char *name;
136 if (sub == NULL)
137 name = xstrdup (gettextdatadir);
138 else
139 name = xconcatenated_filename (gettextdatadir, sub, NULL);
140 array.ptr[array.len++] = name;
141 }
142
143 /* Append elements from GETTEXTDATADIRS. */
144 if (gettextdatadirs != NULL)
145 {
146 array.sub = sub;
147 foreach_elements (gettextdatadirs, fill, &array);
148 }
149
150 /* Append elements from XDG_DATA_DIRS. Note that each element needs
151 to have "gettext" suffix. */
152 if (xdgdatadirs != NULL)
153 {
154 char *combined_sub;
155 if (sub == NULL)
156 combined_sub = xstrdup ("gettext");
157 else
158 combined_sub = xconcatenated_filename ("gettext", sub, NULL);
159
160 array.sub = combined_sub;
161 foreach_elements (xdgdatadirs, fill, &array);
162
163 free (combined_sub);
164 }
165
166 /* Append version specific directory. */
167 {
168 char *base = xasprintf ("%s%s", gettextdatadir, PACKAGE_SUFFIX);
169 char *name;
170 if (sub == NULL)
171 name = base;
172 else
173 {
174 name = xconcatenated_filename (base, sub, NULL);
175 free (base);
176 }
177 array.ptr[array.len++] = name;
178 }
179 }
180
181 return array.ptr;
182 }
183