/* Handle configuration data.
- Copyright (C) 1997, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1997-2019 Free Software Foundation, Inc.
This file is part of the GNU C Library.
Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
The GNU C Library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Library General Public License as
- published by the Free Software Foundation; either version 2 of the
- License, or (at your option) any later version.
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
The GNU C 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.
+ Lesser General Public License for more details.
- You should have received a copy of the GNU Library General Public
- License along with the GNU C Library; see the file COPYING.LIB. If not,
- write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
- Boston, MA 02111-1307, USA. */
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
+#include <locale.h>
#include <search.h>
+#include <stddef.h>
#include <stdio.h>
+#include <stdio_ext.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/param.h>
+#include <libc-lock.h>
#include <gconv_int.h>
/* This is the default path where we look for module lists. */
static const char default_gconv_path[] = GCONV_PATH;
+/* Type to represent search path. */
+struct path_elem
+{
+ const char *name;
+ size_t len;
+};
+
+/* The path elements, as determined by the __gconv_get_path function.
+ All path elements end in a slash. */
+struct path_elem *__gconv_path_elem;
+/* Maximum length of a single path element in __gconv_path_elem. */
+size_t __gconv_max_path_elem_len;
+
+/* We use the following struct if we couldn't allocate memory. */
+static const struct path_elem empty_path_elem = { NULL, 0 };
+
/* Name of the file containing the module information in the directories
along the path. */
static const char gconv_conf_filename[] = "gconv-modules";
/* We have a few builtin transformations. */
static struct gconv_module builtin_modules[] =
{
-#define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
- Fct, Init, End, MinF, MaxF, MinT, MaxT) \
+#define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
+ MinF, MaxF, MinT, MaxT) \
{ \
- from_pattern: From, \
- from_constpfx: ConstPfx, \
- from_constpfx_len: ConstLen, \
- from_regex: NULL, \
- to_string: To, \
- cost_hi: Cost, \
- cost_lo: INT_MAX, \
- module_name: Name \
+ .from_string = From, \
+ .to_string = To, \
+ .cost_hi = Cost, \
+ .cost_lo = INT_MAX, \
+ .module_name = Name \
},
#define BUILTIN_ALIAS(From, To)
#include "gconv_builtin.h"
-};
#undef BUILTIN_TRANSFORMATION
#undef BUILTIN_ALIAS
+};
-static const char *
-builtin_aliases[] =
+static const char builtin_aliases[] =
{
-#define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
- Fct, Init, End, MinF, MaxF, MinT, MaxT)
-#define BUILTIN_ALIAS(From, To) From " " To,
+#define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, BtowcFct, \
+ MinF, MaxF, MinT, MaxT)
+#define BUILTIN_ALIAS(From, To) From "\0" To "\0"
#include "gconv_builtin.h"
+
+#undef BUILTIN_TRANSFORMATION
+#undef BUILTIN_ALIAS
};
-#ifdef USE_IN_LIBIO
-# include <libio/libioP.h>
-# define __getdelim(line, len, c, fp) _IO_getdelim (line, len, c, fp)
-#endif
+#include <libio/libioP.h>
+#define __getdelim(line, len, c, fp) _IO_getdelim (line, len, c, fp)
+
+
+/* Value of the GCONV_PATH environment variable. */
+const char *__gconv_path_envvar;
-/* Function for searching module. */
+/* Test whether there is already a matching module known. */
static int
-module_compare (const void *p1, const void *p2)
+detect_conflict (const char *alias)
{
- const struct gconv_module *s1 = (const struct gconv_module *) p1;
- const struct gconv_module *s2 = (const struct gconv_module *) p2;
- int result;
+ struct gconv_module *node = __gconv_modules_db;
- if (s1->from_pattern == NULL)
+ while (node != NULL)
{
- if (s2->from_pattern == NULL)
- result = strcmp (s1->from_constpfx, s2->from_constpfx);
+ int cmpres = strcmp (alias, node->from_string);
+
+ if (cmpres == 0)
+ /* We have a conflict. */
+ return 1;
+ else if (cmpres < 0)
+ node = node->left;
else
- result = -1;
+ node = node->right;
}
- else if (s2->from_pattern == NULL)
- result = 1;
- else
- result = strcmp (s1->from_pattern, s2->from_pattern);
- if (result == 0)
- result = strcmp (s1->to_string, s2->to_string);
-
- return result;
+ return node != NULL;
}
-/* This function is used to test for a conflict which could be introduced
- if adding a new alias.
-
- This function is a *very* ugly hack. The action-function is not
- supposed to alter the parameter. But we have to do this. We will if
- necessary compile the regular expression so that we can see whether it
- matches the alias name. This is safe in this environment and for the
- sake of performance we do it this way. The alternative would be to
- compile all regular expressions right from the start or to forget about
- the compilation though we might need it later.
-
- The second ugliness is that we have no possibility to pass parameters
- to the function. Therefore we use a global variable. This is no problem
- since we are for sure alone even in multi-threaded applications. */
-
-/* This is alias we want to check. */
-static const char *alias_to_test;
-
-/* This variable is set to a nonzero value once we have found a matching
- entry. */
-static int abort_conflict_search;
-
+/* The actual code to add aliases. */
static void
-detect_conflict (const void *p, VISIT value, int level)
+add_alias2 (const char *from, const char *to, const char *wp, void *modules)
{
- struct gconv_module *s = *(struct gconv_module **) p;
-
- if ((value != endorder && value != leaf) || s->from_constpfx == NULL
- || abort_conflict_search)
+ /* Test whether this alias conflicts with any available module. */
+ if (detect_conflict (from))
+ /* It does conflict, don't add the alias. */
return;
- /* Before we test the whole expression (if this is a regular expression)
- make sure the constant prefix matches. In case this is no regular
- expression this is the whole string. */
- if (strcmp (alias_to_test, s->from_constpfx) == 0)
+ struct gconv_alias *new_alias = (struct gconv_alias *)
+ malloc (sizeof (struct gconv_alias) + (wp - from));
+ if (new_alias != NULL)
{
- if (s->from_pattern == NULL)
- /* This is a simple string and therefore we have a conflict. */
- abort_conflict_search = 1;
- else
- {
- /* Make sure the regular expression is compiled (if possible). */
- if (s->from_regex == NULL)
- {
- /* Beware, this is what I warned you about in the comment
- above. We are modifying the object. */
- if (__regcomp (&s->from_regex_mem, s->from_pattern,
- REG_EXTENDED | REG_ICASE) != 0)
- /* Something is wrong. Remember this. */
- s->from_regex = (regex_t *) -1L;
- else
- s->from_regex = &s->from_regex_mem;
- }
+ void **inserted;
- if (s->from_regex != (regex_t *) -1L)
- {
- regmatch_t match[1];
+ new_alias->fromname = memcpy ((char *) new_alias
+ + sizeof (struct gconv_alias),
+ from, wp - from);
+ new_alias->toname = new_alias->fromname + (to - from);
- if (__regexec (s->from_regex, alias_to_test, 1, match, 0) == 0
- && match[0].rm_so == 0
- && alias_to_test[match[0].rm_eo] == '\0')
- /* The whole string matched. This is also a conflict. */
- abort_conflict_search = 1;
- }
- }
+ inserted = (void **) __tsearch (new_alias, &__gconv_alias_db,
+ __gconv_alias_compare);
+ if (inserted == NULL || *inserted != new_alias)
+ /* Something went wrong, free this entry. */
+ free (new_alias);
}
}
/* Add new alias. */
-static inline void
+static void
add_alias (char *rp, void *modules)
{
/* We now expect two more string. The strings are normalized
(converted to UPPER case) and strored in the alias database. */
- struct gconv_alias *new_alias;
char *from, *to, *wp;
- while (isspace (*rp))
+ while (__isspace_l (*rp, _nl_C_locobj_ptr))
++rp;
from = wp = rp;
- while (*rp != '\0' && !isspace (*rp))
- ++rp;
+ while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
+ *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
if (*rp == '\0')
/* There is no `to' string on the line. Ignore it. */
return;
- *rp++ = '\0';
- to = wp = rp;
- while (isspace (*rp))
+ *wp++ = '\0';
+ to = ++rp;
+ while (__isspace_l (*rp, _nl_C_locobj_ptr))
++rp;
- while (*rp != '\0' && !isspace (*rp))
- *wp++ = *rp++;
+ while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
+ *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
if (to == wp)
/* No `to' string, ignore the line. */
return;
*wp++ = '\0';
- /* Test whether this alias conflicts with any available module. See
- the comment before the function `detect_conflict' for a description
- of this ugly hack. */
- alias_to_test = from;
- abort_conflict_search = 0;
- __twalk (modules, detect_conflict);
- if (abort_conflict_search)
- /* It does conflict, don't add the alias. */
- return;
+ add_alias2 (from, to, wp, modules);
+}
- new_alias = (struct gconv_alias *)
- malloc (sizeof (struct gconv_alias) + (wp - from));
- if (new_alias != NULL)
+
+/* Insert a data structure for a new module in the search tree. */
+static void
+insert_module (struct gconv_module *newp, int tobefreed)
+{
+ struct gconv_module **rootp = &__gconv_modules_db;
+
+ while (*rootp != NULL)
{
- new_alias->fromname = memcpy ((char *) new_alias
- + sizeof (struct gconv_alias),
- from, wp - from);
- new_alias->toname = new_alias->fromname + (to - from);
+ struct gconv_module *root = *rootp;
+ int cmpres;
- if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare)
- == NULL)
- /* Something went wrong, free this entry. */
- free (new_alias);
+ cmpres = strcmp (newp->from_string, root->from_string);
+ if (cmpres == 0)
+ {
+ /* Both strings are identical. Insert the string at the
+ end of the `same' list if it is not already there. */
+ while (strcmp (newp->from_string, root->from_string) != 0
+ || strcmp (newp->to_string, root->to_string) != 0)
+ {
+ rootp = &root->same;
+ root = *rootp;
+ if (root == NULL)
+ break;
+ }
+
+ if (root != NULL)
+ {
+ /* This is a no new conversion. But maybe the cost is
+ better. */
+ if (newp->cost_hi < root->cost_hi
+ || (newp->cost_hi == root->cost_hi
+ && newp->cost_lo < root->cost_lo))
+ {
+ newp->left = root->left;
+ newp->right = root->right;
+ newp->same = root->same;
+ *rootp = newp;
+
+ free (root);
+ }
+ else if (tobefreed)
+ free (newp);
+ return;
+ }
+
+ break;
+ }
+ else if (cmpres < 0)
+ rootp = &root->left;
+ else
+ rootp = &root->right;
}
+
+ /* Plug in the new node here. */
+ *rootp = newp;
}
/* Add new module. */
-static inline void
+static void
add_module (char *rp, const char *directory, size_t dir_len, void **modules,
size_t *nmodules, int modcounter)
{
3. filename of the module
4. an optional cost value
*/
+ struct gconv_alias fake_alias;
struct gconv_module *new_module;
char *from, *to, *module, *wp;
- size_t const_len;
- int from_is_regex;
int need_ext;
int cost_hi;
- while (isspace (*rp))
+ while (__isspace_l (*rp, _nl_C_locobj_ptr))
++rp;
from = rp;
- from_is_regex = 0;
- while (*rp != '\0' && !isspace (*rp))
+ while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
{
- if (!isalnum (*rp) && *rp != '-' && *rp != '/' && *rp != '.'
- && *rp != '_')
- from_is_regex = 1;
+ *rp = __toupper_l (*rp, _nl_C_locobj_ptr);
++rp;
}
if (*rp == '\0')
return;
*rp++ = '\0';
to = wp = rp;
- while (isspace (*rp))
+ while (__isspace_l (*rp, _nl_C_locobj_ptr))
++rp;
- while (*rp != '\0' && !isspace (*rp))
- *wp++ = *rp++;
+ while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
+ *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
if (*rp == '\0')
return;
*wp++ = '\0';
do
++rp;
- while (isspace (*rp));
+ while (__isspace_l (*rp, _nl_C_locobj_ptr));
module = wp;
- while (*rp != '\0' && !isspace (*rp))
+ while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
*wp++ = *rp++;
if (*rp == '\0')
{
*wp++ = '\0';
cost_hi = strtol (rp, &endp, 10);
- if (rp == endp)
+ if (rp == endp || cost_hi < 1)
/* No useful information. */
cost_hi = 1;
}
return;
if (module[0] == '/')
dir_len = 0;
- else
- /* Increment by one for the slash. */
- ++dir_len;
/* See whether we must add the ending. */
need_ext = 0;
- if (wp - module < sizeof (gconv_module_ext)
+ if (wp - module < (ptrdiff_t) sizeof (gconv_module_ext)
|| memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
sizeof (gconv_module_ext)) != 0)
/* We must add the module extension. */
need_ext = sizeof (gconv_module_ext) - 1;
- /* We've collected all the information, now create an entry. */
+ /* See whether we have already an alias with this name defined. */
+ fake_alias.fromname = strndupa (from, to - from);
- if (from_is_regex)
- {
- const_len = 0;
- while (isalnum (from[const_len]) || from[const_len] == '-'
- || from[const_len] == '/' || from[const_len] == '.'
- || from[const_len] == '_')
- ++const_len;
- }
- else
- const_len = to - from - 1;
+ if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare) != NULL)
+ /* This module duplicates an alias. */
+ return;
- new_module = (struct gconv_module *) malloc (sizeof (struct gconv_module)
+ new_module = (struct gconv_module *) calloc (1,
+ sizeof (struct gconv_module)
+ (wp - from)
+ dir_len + need_ext);
if (new_module != NULL)
{
char *tmp;
- new_module->from_constpfx = memcpy ((char *) new_module
- + sizeof (struct gconv_module),
- from, to - from);
- if (from_is_regex)
- new_module->from_pattern = new_module->from_constpfx;
- else
- new_module->from_pattern = NULL;
-
- new_module->from_constpfx_len = const_len;
-
- new_module->from_regex = NULL;
+ new_module->from_string = tmp = (char *) (new_module + 1);
+ tmp = __mempcpy (tmp, from, to - from);
- new_module->to_string = memcpy ((char *) new_module->from_constpfx
- + (to - from), to, module - to);
+ new_module->to_string = tmp;
+ tmp = __mempcpy (tmp, to, module - to);
new_module->cost_hi = cost_hi;
new_module->cost_lo = modcounter;
- new_module->module_name = (char *) new_module->to_string + (module - to);
+ new_module->module_name = tmp;
- if (dir_len == 0)
- tmp = (char *) new_module->module_name;
- else
- {
- tmp = __mempcpy ((char *) new_module->module_name,
- directory, dir_len - 1);
- *tmp++ = '/';
- }
+ if (dir_len != 0)
+ tmp = __mempcpy (tmp, directory, dir_len);
tmp = __mempcpy (tmp, module, wp - module);
if (need_ext)
memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
- /* See whether we have already an alias with this name defined.
- We do allow regular expressions matching this any alias since
- this expression can also match other names and we test for aliases
- before testing for modules. */
- if (! from_is_regex)
- {
- struct gconv_alias fake_alias;
-
- fake_alias.fromname = new_module->from_constpfx;
-
- if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare)
- != NULL)
- {
- /* This module duplicates an alias. */
- free (new_module);
- return;
- }
- }
-
- if (__tfind (new_module, modules, module_compare) == NULL)
- {
- if (__tsearch (new_module, modules, module_compare) == NULL)
- /* Something went wrong while inserting the new module. */
- free (new_module);
- else
- ++*nmodules;
- }
+ /* Now insert the new module data structure in our search tree. */
+ insert_module (new_module, 1);
}
}
-static void
-insert_module (const void *nodep, VISIT value, int level)
-{
- if (value == preorder || value == leaf)
- __gconv_modules_db[__gconv_nmodules++] = *(struct gconv_module **) nodep;
-}
-
-static void
-nothing (void *unused __attribute__ ((unused)))
-{
-}
-
-
/* Read the next configuration file. */
static void
-internal_function
read_conf_file (const char *filename, const char *directory, size_t dir_len,
void **modules, size_t *nmodules)
{
- FILE *fp = fopen (filename, "r");
+ /* Note the file is opened with cancellation in the I/O functions
+ disabled. */
+ FILE *fp = fopen (filename, "rce");
char *line = NULL;
size_t line_len = 0;
- int modcounter = 0;
+ static int modcounter;
/* Don't complain if a file is not present or readable, simply silently
ignore it. */
if (fp == NULL)
return;
+ /* No threads reading from this stream. */
+ __fsetlocking (fp, FSETLOCKING_BYCALLER);
+
/* Process the known entries of the file. Comments start with `#' and
end with the end of the line. Empty lines are ignored. */
- while (!feof_unlocked (fp))
+ while (!__feof_unlocked (fp))
{
char *rp, *endp, *word;
ssize_t n = __getdelim (&line, &line_len, '\n', fp);
if (rp[n - 1] == '\n')
rp[n - 1] = '\0';
- while (isspace (*rp))
+ while (__isspace_l (*rp, _nl_C_locobj_ptr))
++rp;
/* If this is an empty line go on with the next one. */
continue;
word = rp;
- while (*rp != '\0' && !isspace (*rp))
+ while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
++rp;
if (rp - word == sizeof ("alias") - 1
/* Otherwise ignore the line. */
}
- if (line != NULL)
- free (line);
+ free (line);
+
fclose (fp);
}
-/* Read all configuration files found in the user-specified and the default
- path. */
-void
-__gconv_read_conf (void)
+/* Determine the directories we are looking for data in. This function should
+ only be called from __gconv_read_conf. */
+static void
+__gconv_get_path (void)
{
- const char *user_path = __secure_getenv ("GCONV_PATH");
- char *gconv_path, *elem;
- void *modules = NULL;
- size_t nmodules = 0;
- int save_errno = errno;
- size_t cnt;
-
- if (user_path == NULL)
- /* No user-defined path. Make a modifiable copy of the default path. */
- gconv_path = strdupa (default_gconv_path);
+ struct path_elem *result;
+
+ /* This function is only ever called when __gconv_path_elem is NULL. */
+ result = __gconv_path_elem;
+ assert (result == NULL);
+
+ /* Determine the complete path first. */
+ char *gconv_path;
+ size_t gconv_path_len;
+ char *elem;
+ char *oldp;
+ char *cp;
+ int nelems;
+ char *cwd;
+ size_t cwdlen;
+
+ if (__gconv_path_envvar == NULL)
+ {
+ /* No user-defined path. Make a modifiable copy of the
+ default path. */
+ gconv_path = strdupa (default_gconv_path);
+ gconv_path_len = sizeof (default_gconv_path);
+ cwd = NULL;
+ cwdlen = 0;
+ }
else
{
/* Append the default path to the user-defined path. */
- size_t user_len = strlen (user_path);
- char *tmp;
+ size_t user_len = strlen (__gconv_path_envvar);
+
+ gconv_path_len = user_len + 1 + sizeof (default_gconv_path);
+ gconv_path = alloca (gconv_path_len);
+ __mempcpy (__mempcpy (__mempcpy (gconv_path, __gconv_path_envvar,
+ user_len),
+ ":", 1),
+ default_gconv_path, sizeof (default_gconv_path));
+ cwd = __getcwd (NULL, 0);
+ cwdlen = __glibc_unlikely (cwd == NULL) ? 0 : strlen (cwd);
+ }
+ assert (default_gconv_path[0] == '/');
- gconv_path = alloca (user_len + 1 + sizeof (default_gconv_path));
- tmp = __mempcpy (gconv_path, user_path, user_len);
- *tmp++ = ':';
- __mempcpy (tmp, default_gconv_path, sizeof (default_gconv_path));
+ /* In a first pass we calculate the number of elements. */
+ oldp = NULL;
+ cp = strchr (gconv_path, ':');
+ nelems = 1;
+ while (cp != NULL)
+ {
+ if (cp != oldp + 1)
+ ++nelems;
+ oldp = cp;
+ cp = strchr (cp + 1, ':');
}
- elem = strtok_r (gconv_path, ":", &gconv_path);
- while (elem != NULL)
+ /* Allocate the memory for the result. */
+ result = malloc ((nelems + 1)
+ * sizeof (struct path_elem)
+ + gconv_path_len + nelems
+ + (nelems - 1) * (cwdlen + 1));
+ if (result != NULL)
{
-#ifndef MAXPATHLEN
- /* We define a reasonable limit. */
-# define MAXPATHLEN 4096
-#endif
- char real_elem[MAXPATHLEN];
+ char *strspace = (char *) &result[nelems + 1];
+ int n = 0;
+
+ /* Separate the individual parts. */
+ __gconv_max_path_elem_len = 0;
+ elem = __strtok_r (gconv_path, ":", &gconv_path);
+ assert (elem != NULL);
+ do
+ {
+ result[n].name = strspace;
+ if (elem[0] != '/')
+ {
+ assert (cwd != NULL);
+ strspace = __mempcpy (strspace, cwd, cwdlen);
+ *strspace++ = '/';
+ }
+ strspace = __stpcpy (strspace, elem);
+ if (strspace[-1] != '/')
+ *strspace++ = '/';
+
+ result[n].len = strspace - result[n].name;
+ if (result[n].len > __gconv_max_path_elem_len)
+ __gconv_max_path_elem_len = result[n].len;
+
+ *strspace++ = '\0';
+ ++n;
+ }
+ while ((elem = __strtok_r (NULL, ":", &gconv_path)) != NULL);
+
+ result[n].name = NULL;
+ result[n].len = 0;
+ }
- if (__realpath (elem, real_elem) != NULL)
- {
- size_t elem_len = strlen (real_elem);
- char *filename, *tmp;
+ __gconv_path_elem = result ?: (struct path_elem *) &empty_path_elem;
- filename = alloca (elem_len + 1 + sizeof (gconv_conf_filename));
- tmp = __mempcpy (filename, real_elem, elem_len);
- *tmp++ = '/';
- __mempcpy (tmp, gconv_conf_filename, sizeof (gconv_conf_filename));
+ free (cwd);
+}
- /* Read the next configuration file. */
- read_conf_file (filename, real_elem, elem_len, &modules, &nmodules);
- }
- /* Get next element in the path. */
- elem = strtok_r (NULL, ":", &gconv_path);
+/* Read all configuration files found in the user-specified and the default
+ path. This function should only be called once during the program's
+ lifetime. It disregards locking and synchronization because its only
+ caller, __gconv_load_conf, handles this. */
+static void
+__gconv_read_conf (void)
+{
+ void *modules = NULL;
+ size_t nmodules = 0;
+ int save_errno = errno;
+ size_t cnt;
+
+ /* First see whether we should use the cache. */
+ if (__gconv_load_cache () == 0)
+ {
+ /* Yes, we are done. */
+ __set_errno (save_errno);
+ return;
}
- /* If the configuration files do not contain any valid module specification
- remember this by setting the pointer to the module array to NULL. */
- nmodules += sizeof (builtin_modules) / sizeof (builtin_modules[0]);
- if (nmodules == 0)
- __gconv_modules_db = NULL;
- else
+#ifndef STATIC_GCONV
+ /* Find out where we have to look. */
+ __gconv_get_path ();
+
+ for (cnt = 0; __gconv_path_elem[cnt].name != NULL; ++cnt)
{
- __gconv_modules_db =
- (struct gconv_module **) malloc (nmodules
- * sizeof (struct gconv_module));
- if (__gconv_modules_db != NULL)
- {
- size_t cnt;
+ const char *elem = __gconv_path_elem[cnt].name;
+ size_t elem_len = __gconv_path_elem[cnt].len;
+ char *filename;
+
+ /* No slash needs to be inserted between elem and gconv_conf_filename;
+ elem already ends in a slash. */
+ filename = alloca (elem_len + sizeof (gconv_conf_filename));
+ __mempcpy (__mempcpy (filename, elem, elem_len),
+ gconv_conf_filename, sizeof (gconv_conf_filename));
+
+ /* Read the next configuration file. */
+ read_conf_file (filename, elem, elem_len, &modules, &nmodules);
+ }
+#endif
- /* Insert all module entries into the array. */
- __twalk (modules, insert_module);
+ /* Add the internal modules. */
+ for (cnt = 0; cnt < sizeof (builtin_modules) / sizeof (builtin_modules[0]);
+ ++cnt)
+ {
+ struct gconv_alias fake_alias;
- /* Finally insert the builtin transformations. */
- for (cnt = 0; cnt < (sizeof (builtin_modules)
- / sizeof (struct gconv_module)); ++cnt)
- __gconv_modules_db[__gconv_nmodules++] = &builtin_modules[cnt];
- }
+ fake_alias.fromname = (char *) builtin_modules[cnt].from_string;
+
+ if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare)
+ != NULL)
+ /* It'll conflict so don't add it. */
+ continue;
+
+ insert_module (&builtin_modules[cnt], 0);
}
/* Add aliases for builtin conversions. */
- cnt = sizeof (builtin_aliases) / sizeof (builtin_aliases[0]);
- while (cnt > 0)
+ const char *cp = builtin_aliases;
+ do
{
- char *copy = strdupa (builtin_aliases[--cnt]);
- add_alias (copy, modules);
+ const char *from = cp;
+ const char *to = __rawmemchr (from, '\0') + 1;
+ cp = __rawmemchr (to, '\0') + 1;
+
+ add_alias2 (from, to, cp, modules);
}
-
- if (nmodules != 0)
- /* Now remove the tree data structure. */
- __tdestroy (modules, nothing);
+ while (*cp != '\0');
/* Restore the error number. */
__set_errno (save_errno);
}
+
+
+/* This "once" variable is used to do a one-time load of the configuration. */
+__libc_once_define (static, once);
+
+
+/* Read all configuration files found in the user-specified and the default
+ path, but do it only "once" using __gconv_read_conf to do the actual
+ work. This is the function that must be called when reading iconv
+ configuration. */
+void
+__gconv_load_conf (void)
+{
+ __libc_once (once, __gconv_read_conf);
+}
+
+
+/* Free all resources if necessary. */
+libc_freeres_fn (free_mem)
+{
+ if (__gconv_path_elem != NULL && __gconv_path_elem != &empty_path_elem)
+ free ((void *) __gconv_path_elem);
+}