Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
glibc/iconv/gconv_conf.c
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
436 lines (370 sloc)
11.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Handle configuration data. | |
Copyright (C) 1997 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. | |
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 <ctype.h> | |
#include <errno.h> | |
#include <gconv.h> | |
#include <search.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <sys/param.h> | |
/* 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 | |
{ | |
endp = strchr (rp, '\n'); | |
if (endp != NULL) | |
*endp = '\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); | |
} |