/* Handle configuration data. Copyright (C) 1997 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper , 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. 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. 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. */ #include #include #include #include #include #include #include #include #include /* This is the default path where we look for module lists. */ static const char default_gconv_path[] = GCONV_PATH; /* Name of the file containing the module information in the directories along the path. */ static const char gconv_conf_filename[] = "gconv-modules"; /* Filename extension for the modules. */ #ifndef MODULE_EXT # define MODULE_EXT ".so" #endif static const char gconv_module_ext[] = MODULE_EXT; /* We have a few builtin transformations. */ static struct gconv_module builtin_modules[] = { #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \ Fct, Init, End) \ { \ from_pattern: From, \ from_constpfx: ConstPfx, \ from_constpfx_len: ConstLen, \ from_regex: NULL, \ to_string: To, \ cost: Cost, \ module_name: Name \ }, #include "gconv_builtin.h" }; /* Function for searching module. */ static int module_compare (const void *p1, const void *p2) { struct gconv_module *s1 = (struct gconv_module *) p1; struct gconv_module *s2 = (struct gconv_module *) p2; int result; if (s1->from_pattern == NULL) { if (s2->from_pattern == NULL) result = strcmp (s1->from_constpfx, s2->from_constpfx); else result = -1; } 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; } /* Add new alias. */ static inline void add_alias (char *rp) { /* 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)) ++rp; from = wp = rp; while (*rp != '\0' && !isspace (*rp)) ++rp; if (*rp == '\0') /* There is no `to' string on the line. Ignore it. */ return; *rp++ = '\0'; to = wp = rp; while (isspace (*rp)) ++rp; while (*rp != '\0' && !isspace (*rp)) *wp++ = *rp++; if (to == wp) /* No `to' string, ignore the line. */ return; *wp++ = '\0'; new_alias = (struct gconv_alias *) malloc (sizeof (struct gconv_alias) + (wp - from)); if (new_alias != NULL) { new_alias->fromname = memcpy ((char *) new_alias + sizeof (struct gconv_alias), from, wp - from); new_alias->toname = new_alias->fromname + (to - from); if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare) == NULL) /* Something went wrong, free this entry. */ free (new_alias); } } /* Add new module. */ static inline void add_module (char *rp, const char *directory, size_t dir_len, void **modules, size_t *nmodules) { /* We expect now 1. `from' name 2. `to' name 3. filename of the module 4. an optional cost value */ struct gconv_module *new_module; char *from, *to, *module, *wp; size_t const_len; int from_is_regex; int need_ext; int cost; while (isspace (*rp)) ++rp; from = rp; from_is_regex = 0; while (*rp != '\0' && !isspace (*rp)) { if (!isalnum (*rp) && *rp != '-' && *rp != '/' && *rp != '.' && *rp != '_') from_is_regex = 1; ++rp; } if (*rp == '\0') return; *rp++ = '\0'; to = wp = rp; while (isspace (*rp)) ++rp; while (*rp != '\0' && !isspace (*rp)) *wp++ = *rp++; if (*rp == '\0') return; *wp++ = '\0'; do ++rp; while (isspace (*rp)); module = wp; while (*rp != '\0' && !isspace (*rp)) *wp++ = *rp++; if (*rp == '\0') { /* There is no cost, use one by default. */ *wp++ = '\0'; cost = 1; } else { /* There might be a cost value. */ char *endp; *wp++ = '\0'; cost = strtol (rp, &endp, 10); if (rp == endp) /* No useful information. */ cost = 1; } if (module[0] == '\0') /* No module name given. */ 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) || 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. */ 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; new_module = (struct gconv_module *) malloc (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->to_string = memcpy ((char *) new_module->from_constpfx + (to - from), to, module - to); new_module->cost = cost; new_module->module_name = (char *) new_module->to_string + (module - to); if (dir_len == 0) tmp = (char *) new_module->module_name; else { tmp = __mempcpy ((char *) new_module->module_name, directory, dir_len - 1); *tmp++ = '/'; } tmp = __mempcpy (tmp, module, wp - module); if (need_ext) memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext)); 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; } } 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"); char *line = NULL; size_t line_len = 0; /* Don't complain if a file is not present or readable, simply silently ignore it. */ if (fp == NULL) return; /* Process the known entries of the file. Comments start with `#' and end with the end of the line. Empty lines are ignored. */ while (!feof (fp)) { char *rp, *endp, *word; ssize_t n = __getdelim (&line, &line_len, '\n', fp); if (n < 0) /* An error occurred. */ break; rp = line; /* Terminate the line (excluding comments or newline) by an NUL byte to simplify the following code. */ endp = strchr (rp, '#'); if (endp != NULL) *endp = '\0'; else if (rp[n - 1] == '\n') rp[n - 1] = '\0'; while (isspace (*rp)) ++rp; /* If this is an empty line go on with the next one. */ if (rp == endp) continue; word = rp; while (*rp != '\0' && !isspace (*rp)) ++rp; if (rp - word == sizeof ("alias") - 1 && memcmp (word, "alias", sizeof ("alias") - 1) == 0) add_alias (rp); else if (rp - word == sizeof ("module") - 1 && memcmp (word, "module", sizeof ("module") - 1) == 0) add_module (rp, directory, dir_len, modules, nmodules); /* else */ /* Otherwise ignore the line. */ } if (line != NULL) free (line); fclose (fp); } /* Read all configuration files found in the user-specified and the default path. */ void __gconv_read_conf (void) { const char *user_path = __secure_getenv ("GCONV_PATH"); char *gconv_path, *elem; void *modules = NULL; size_t nmodules = 0; int save_errno = errno; if (user_path == NULL) /* No user-defined path. Make a modifiable copy of the default path. */ gconv_path = strdupa (default_gconv_path); else { /* Append the default path to the user-defined path. */ size_t user_len = strlen (user_path); char *tmp; 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)); } elem = strtok_r (gconv_path, ":", &gconv_path); while (elem != NULL) { char real_elem[MAXPATHLEN]; if (realpath (elem, real_elem) != NULL) { size_t elem_len = strlen (real_elem); char *filename, *tmp; 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)); /* 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); } /* 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 { __gconv_modules_db = (struct gconv_module **) malloc (nmodules * sizeof (struct gconv_module)); if (__gconv_modules_db != NULL) { size_t cnt; /* Insert all module entries into the array. */ __twalk (modules, insert_module); /* No remove the tree data structure. */ __tdestroy (modules, nothing); /* 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]; } } /* Restore the error number. */ __set_errno (save_errno); }