/* Routines for locating data files Copyright (C) 2016, 2019 Free Software Foundation, Inc. This file was written by Daiki Ueno , 2016. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif /* Specification. */ #include "search-path.h" #include #include #include "concat-filename.h" #include "relocatable.h" #include "xalloc.h" #include "xmemdup0.h" #include "xvasprintf.h" typedef void (* foreach_function_ty) (const char *dir, size_t len, void *data); struct path_array_ty { char **ptr; size_t len; /* Transient argument for fill(). */ const char *sub; }; static void foreach_elements (const char *dirs, foreach_function_ty function, void *data) { const char *start = dirs; /* Count the number of valid elements in DIRS. */ while (*start != '\0') { char *end = strchrnul (start, ':'); /* Skip empty element. */ if (start != end) function (start, end - start, data); if (*end == '\0') break; start = end + 1; } } static void increment (const char *dir, size_t len, void *data) { size_t *count = data; (*count)++; } static void fill (const char *dir, size_t len, void *data) { struct path_array_ty *array = data; char *base, *name; base = xmemdup0 (dir, len); if (array->sub == NULL) name = base; else { name = xconcatenated_filename (base, array->sub, NULL); free (base); } array->ptr[array->len++] = name; } /* Find the standard search path for data files. If SUB is not NULL, append it to each directory. Returns a freshly allocated NULL terminated list of freshly allocated strings. The order in the path is as follows: 1. $GETTEXTDATADIR or GETTEXTDATADIR 2. $GETTEXTDATADIRS 3. $XDG_DATA_DIRS, where each element is suffixed with "gettext" 4. $GETTEXTDATADIR or GETTEXTDATADIR, suffixed with PACKAGE_SUFFIX */ char ** get_search_path (const char *sub) { const char *gettextdatadir; const char *gettextdatadirs; const char *xdgdatadirs; struct path_array_ty array; /* Count how many array elements are needed. */ size_t count = 2; gettextdatadirs = getenv ("GETTEXTDATADIRS"); if (gettextdatadirs != NULL) foreach_elements (gettextdatadirs, increment, &count); xdgdatadirs = getenv ("XDG_DATA_DIRS"); if (xdgdatadirs != NULL) foreach_elements (xdgdatadirs, increment, &count); /* Allocate the array. */ array.ptr = XCALLOC (count + 1, char *); array.len = 0; /* Fill the array. */ { gettextdatadir = getenv ("GETTEXTDATADIR"); if (gettextdatadir == NULL || gettextdatadir[0] == '\0') /* Make it possible to override the locator file location. This is necessary for running the testsuite before "make install". */ gettextdatadir = relocate (GETTEXTDATADIR); /* Append element from GETTEXTDATADIR. */ { char *name; if (sub == NULL) name = xstrdup (gettextdatadir); else name = xconcatenated_filename (gettextdatadir, sub, NULL); array.ptr[array.len++] = name; } /* Append elements from GETTEXTDATADIRS. */ if (gettextdatadirs != NULL) { array.sub = sub; foreach_elements (gettextdatadirs, fill, &array); } /* Append elements from XDG_DATA_DIRS. Note that each element needs to have "gettext" suffix. */ if (xdgdatadirs != NULL) { char *combined_sub; if (sub == NULL) combined_sub = xstrdup ("gettext"); else combined_sub = xconcatenated_filename ("gettext", sub, NULL); array.sub = combined_sub; foreach_elements (xdgdatadirs, fill, &array); free (combined_sub); } /* Append version specific directory. */ { char *base = xasprintf ("%s%s", gettextdatadir, PACKAGE_SUFFIX); char *name; if (sub == NULL) name = base; else { name = xconcatenated_filename (base, sub, NULL); free (base); } array.ptr[array.len++] = name; } } return array.ptr; }