• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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