Update.
[glibc.git] / iconv / gconv_conf.c
blobd3f516effa116b29008c46f6218d3cea9c96b1a6
1 /* Handle configuration data.
2 Copyright (C) 1997, 1998 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
4 Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
6 The GNU C Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The GNU C Library 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 GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the GNU C Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 #include <ctype.h>
22 #include <errno.h>
23 #include <limits.h>
24 #include <search.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/param.h>
31 #include <gconv_int.h>
34 /* This is the default path where we look for module lists. */
35 static const char default_gconv_path[] = GCONV_PATH;
37 /* Name of the file containing the module information in the directories
38 along the path. */
39 static const char gconv_conf_filename[] = "gconv-modules";
41 /* Filename extension for the modules. */
42 #ifndef MODULE_EXT
43 # define MODULE_EXT ".so"
44 #endif
45 static const char gconv_module_ext[] = MODULE_EXT;
47 /* We have a few builtin transformations. */
48 static struct gconv_module builtin_modules[] =
50 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
51 Fct, Init, End, MinF, MaxF, MinT, MaxT) \
52 { \
53 from_pattern: From, \
54 from_constpfx: ConstPfx, \
55 from_constpfx_len: ConstLen, \
56 from_regex: NULL, \
57 to_string: To, \
58 cost_hi: Cost, \
59 cost_lo: INT_MAX, \
60 module_name: Name \
62 #define BUILTIN_ALIAS(From, To)
64 #include "gconv_builtin.h"
67 #undef BUILTIN_TRANSFORMATION
68 #undef BUILTIN_ALIAS
70 static const char *
71 builtin_aliases[] =
73 #define BUILTIN_TRANSFORMATION(From, ConstPfx, ConstLen, To, Cost, Name, \
74 Fct, Init, End, MinF, MaxF, MinT, MaxT)
75 #define BUILTIN_ALIAS(From, To) From " " To,
77 #include "gconv_builtin.h"
80 #ifdef USE_IN_LIBIO
81 # include <libio/libioP.h>
82 # define __getdelim(line, len, c, fp) _IO_getdelim (line, len, c, fp)
83 #endif
86 /* Function for searching module. */
87 static int
88 module_compare (const void *p1, const void *p2)
90 const struct gconv_module *s1 = (const struct gconv_module *) p1;
91 const struct gconv_module *s2 = (const struct gconv_module *) p2;
92 int result;
94 if (s1->from_pattern == NULL)
96 if (s2->from_pattern == NULL)
97 result = strcmp (s1->from_constpfx, s2->from_constpfx);
98 else
99 result = -1;
101 else if (s2->from_pattern == NULL)
102 result = 1;
103 else
104 result = strcmp (s1->from_pattern, s2->from_pattern);
106 if (result == 0)
107 result = strcmp (s1->to_string, s2->to_string);
109 return result;
113 /* This function is used to test for a conflict which could be introduced
114 if adding a new alias.
116 This function is a *very* ugly hack. The action-function is not
117 supposed to alter the parameter. But we have to do this. We will if
118 necessary compile the regular expression so that we can see whether it
119 matches the alias name. This is safe in this environment and for the
120 sake of performance we do it this way. The alternative would be to
121 compile all regular expressions right from the start or to forget about
122 the compilation though we might need it later.
124 The second ugliness is that we have no possibility to pass parameters
125 to the function. Therefore we use a global variable. This is no problem
126 since we are for sure alone even in multi-threaded applications. */
128 /* This is alias we want to check. */
129 static const char *alias_to_test;
131 /* This variable is set to a nonzero value once we have found a matching
132 entry. */
133 static int abort_conflict_search;
135 static void
136 detect_conflict (const void *p, VISIT value, int level)
138 struct gconv_module *s = *(struct gconv_module **) p;
140 if ((value != endorder && value != leaf) || s->from_constpfx == NULL
141 || abort_conflict_search)
142 return;
144 /* Before we test the whole expression (if this is a regular expression)
145 make sure the constant prefix matches. In case this is no regular
146 expression this is the whole string. */
147 if (strcmp (alias_to_test, s->from_constpfx) == 0)
149 if (s->from_pattern == NULL)
150 /* This is a simple string and therefore we have a conflict. */
151 abort_conflict_search = 1;
152 else
154 /* Make sure the regular expression is compiled (if possible). */
155 if (s->from_regex == NULL)
157 /* Beware, this is what I warned you about in the comment
158 above. We are modifying the object. */
159 if (__regcomp (&s->from_regex_mem, s->from_pattern,
160 REG_EXTENDED | REG_ICASE) != 0)
161 /* Something is wrong. Remember this. */
162 s->from_regex = (regex_t *) -1L;
163 else
164 s->from_regex = &s->from_regex_mem;
167 if (s->from_regex != (regex_t *) -1L)
169 regmatch_t match[1];
171 if (__regexec (s->from_regex, alias_to_test, 1, match, 0) == 0
172 && match[0].rm_so == 0
173 && alias_to_test[match[0].rm_eo] == '\0')
174 /* The whole string matched. This is also a conflict. */
175 abort_conflict_search = 1;
182 /* Add new alias. */
183 static inline void
184 add_alias (char *rp, void *modules)
186 /* We now expect two more string. The strings are normalized
187 (converted to UPPER case) and strored in the alias database. */
188 struct gconv_alias *new_alias;
189 char *from, *to, *wp;
191 while (isspace (*rp))
192 ++rp;
193 from = wp = rp;
194 while (*rp != '\0' && !isspace (*rp))
195 ++rp;
196 if (*rp == '\0')
197 /* There is no `to' string on the line. Ignore it. */
198 return;
199 *rp++ = '\0';
200 to = wp = rp;
201 while (isspace (*rp))
202 ++rp;
203 while (*rp != '\0' && !isspace (*rp))
204 *wp++ = *rp++;
205 if (to == wp)
206 /* No `to' string, ignore the line. */
207 return;
208 *wp++ = '\0';
210 /* Test whether this alias conflicts with any available module. See
211 the comment before the function `detect_conflict' for a description
212 of this ugly hack. */
213 alias_to_test = from;
214 abort_conflict_search = 0;
215 __twalk (modules, detect_conflict);
216 if (abort_conflict_search)
217 /* It does conflict, don't add the alias. */
218 return;
220 new_alias = (struct gconv_alias *)
221 malloc (sizeof (struct gconv_alias) + (wp - from));
222 if (new_alias != NULL)
224 new_alias->fromname = memcpy ((char *) new_alias
225 + sizeof (struct gconv_alias),
226 from, wp - from);
227 new_alias->toname = new_alias->fromname + (to - from);
229 if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare)
230 == NULL)
231 /* Something went wrong, free this entry. */
232 free (new_alias);
237 /* Add new module. */
238 static inline void
239 add_module (char *rp, const char *directory, size_t dir_len, void **modules,
240 size_t *nmodules, int modcounter)
242 /* We expect now
243 1. `from' name
244 2. `to' name
245 3. filename of the module
246 4. an optional cost value
248 struct gconv_module *new_module;
249 char *from, *to, *module, *wp;
250 size_t const_len;
251 int from_is_regex;
252 int need_ext;
253 int cost_hi;
255 while (isspace (*rp))
256 ++rp;
257 from = rp;
258 from_is_regex = 0;
259 while (*rp != '\0' && !isspace (*rp))
261 if (!isalnum (*rp) && *rp != '-' && *rp != '/' && *rp != '.'
262 && *rp != '_')
263 from_is_regex = 1;
264 ++rp;
266 if (*rp == '\0')
267 return;
268 *rp++ = '\0';
269 to = wp = rp;
270 while (isspace (*rp))
271 ++rp;
272 while (*rp != '\0' && !isspace (*rp))
273 *wp++ = *rp++;
274 if (*rp == '\0')
275 return;
276 *wp++ = '\0';
278 ++rp;
279 while (isspace (*rp));
280 module = wp;
281 while (*rp != '\0' && !isspace (*rp))
282 *wp++ = *rp++;
283 if (*rp == '\0')
285 /* There is no cost, use one by default. */
286 *wp++ = '\0';
287 cost_hi = 1;
289 else
291 /* There might be a cost value. */
292 char *endp;
294 *wp++ = '\0';
295 cost_hi = strtol (rp, &endp, 10);
296 if (rp == endp)
297 /* No useful information. */
298 cost_hi = 1;
301 if (module[0] == '\0')
302 /* No module name given. */
303 return;
304 if (module[0] == '/')
305 dir_len = 0;
306 else
307 /* Increment by one for the slash. */
308 ++dir_len;
310 /* See whether we must add the ending. */
311 need_ext = 0;
312 if (wp - module < sizeof (gconv_module_ext)
313 || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
314 sizeof (gconv_module_ext)) != 0)
315 /* We must add the module extension. */
316 need_ext = sizeof (gconv_module_ext) - 1;
318 /* We've collected all the information, now create an entry. */
320 if (from_is_regex)
322 const_len = 0;
323 while (isalnum (from[const_len]) || from[const_len] == '-'
324 || from[const_len] == '/' || from[const_len] == '.'
325 || from[const_len] == '_')
326 ++const_len;
328 else
329 const_len = to - from - 1;
331 new_module = (struct gconv_module *) malloc (sizeof (struct gconv_module)
332 + (wp - from)
333 + dir_len + need_ext);
334 if (new_module != NULL)
336 char *tmp;
338 new_module->from_constpfx = memcpy ((char *) new_module
339 + sizeof (struct gconv_module),
340 from, to - from);
341 if (from_is_regex)
342 new_module->from_pattern = new_module->from_constpfx;
343 else
344 new_module->from_pattern = NULL;
346 new_module->from_constpfx_len = const_len;
348 new_module->from_regex = NULL;
350 new_module->to_string = memcpy ((char *) new_module->from_constpfx
351 + (to - from), to, module - to);
353 new_module->cost_hi = cost_hi;
354 new_module->cost_lo = modcounter;
356 new_module->module_name = (char *) new_module->to_string + (module - to);
358 if (dir_len == 0)
359 tmp = (char *) new_module->module_name;
360 else
362 tmp = __mempcpy ((char *) new_module->module_name,
363 directory, dir_len - 1);
364 *tmp++ = '/';
367 tmp = __mempcpy (tmp, module, wp - module);
369 if (need_ext)
370 memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
372 /* See whether we have already an alias with this name defined.
373 We do allow regular expressions matching this any alias since
374 this expression can also match other names and we test for aliases
375 before testing for modules. */
376 if (! from_is_regex)
378 struct gconv_alias fake_alias;
380 fake_alias.fromname = new_module->from_constpfx;
382 if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare)
383 != NULL)
385 /* This module duplicates an alias. */
386 free (new_module);
387 return;
391 if (__tfind (new_module, modules, module_compare) == NULL)
393 if (__tsearch (new_module, modules, module_compare) == NULL)
394 /* Something went wrong while inserting the new module. */
395 free (new_module);
396 else
397 ++*nmodules;
403 static void
404 insert_module (const void *nodep, VISIT value, int level)
406 if (value == preorder || value == leaf)
407 __gconv_modules_db[__gconv_nmodules++] = *(struct gconv_module **) nodep;
410 static void
411 nothing (void *unused __attribute__ ((unused)))
416 /* Read the next configuration file. */
417 static void
418 internal_function
419 read_conf_file (const char *filename, const char *directory, size_t dir_len,
420 void **modules, size_t *nmodules)
422 FILE *fp = fopen (filename, "r");
423 char *line = NULL;
424 size_t line_len = 0;
425 int modcounter = 0;
427 /* Don't complain if a file is not present or readable, simply silently
428 ignore it. */
429 if (fp == NULL)
430 return;
432 /* Process the known entries of the file. Comments start with `#' and
433 end with the end of the line. Empty lines are ignored. */
434 while (!feof_unlocked (fp))
436 char *rp, *endp, *word;
437 ssize_t n = __getdelim (&line, &line_len, '\n', fp);
438 if (n < 0)
439 /* An error occurred. */
440 break;
442 rp = line;
443 /* Terminate the line (excluding comments or newline) by an NUL byte
444 to simplify the following code. */
445 endp = strchr (rp, '#');
446 if (endp != NULL)
447 *endp = '\0';
448 else
449 if (rp[n - 1] == '\n')
450 rp[n - 1] = '\0';
452 while (isspace (*rp))
453 ++rp;
455 /* If this is an empty line go on with the next one. */
456 if (rp == endp)
457 continue;
459 word = rp;
460 while (*rp != '\0' && !isspace (*rp))
461 ++rp;
463 if (rp - word == sizeof ("alias") - 1
464 && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
465 add_alias (rp, *modules);
466 else if (rp - word == sizeof ("module") - 1
467 && memcmp (word, "module", sizeof ("module") - 1) == 0)
468 add_module (rp, directory, dir_len, modules, nmodules, modcounter++);
469 /* else */
470 /* Otherwise ignore the line. */
473 if (line != NULL)
474 free (line);
475 fclose (fp);
479 /* Read all configuration files found in the user-specified and the default
480 path. */
481 void
482 __gconv_read_conf (void)
484 const char *user_path = __secure_getenv ("GCONV_PATH");
485 char *gconv_path, *elem;
486 void *modules = NULL;
487 size_t nmodules = 0;
488 int save_errno = errno;
489 size_t cnt;
491 if (user_path == NULL)
492 /* No user-defined path. Make a modifiable copy of the default path. */
493 gconv_path = strdupa (default_gconv_path);
494 else
496 /* Append the default path to the user-defined path. */
497 size_t user_len = strlen (user_path);
498 char *tmp;
500 gconv_path = alloca (user_len + 1 + sizeof (default_gconv_path));
501 tmp = __mempcpy (gconv_path, user_path, user_len);
502 *tmp++ = ':';
503 __mempcpy (tmp, default_gconv_path, sizeof (default_gconv_path));
506 elem = strtok_r (gconv_path, ":", &gconv_path);
507 while (elem != NULL)
509 #ifndef MAXPATHLEN
510 /* We define a reasonable limit. */
511 # define MAXPATHLEN 4096
512 #endif
513 char real_elem[MAXPATHLEN];
515 if (__realpath (elem, real_elem) != NULL)
517 size_t elem_len = strlen (real_elem);
518 char *filename, *tmp;
520 filename = alloca (elem_len + 1 + sizeof (gconv_conf_filename));
521 tmp = __mempcpy (filename, real_elem, elem_len);
522 *tmp++ = '/';
523 __mempcpy (tmp, gconv_conf_filename, sizeof (gconv_conf_filename));
525 /* Read the next configuration file. */
526 read_conf_file (filename, real_elem, elem_len, &modules, &nmodules);
529 /* Get next element in the path. */
530 elem = strtok_r (NULL, ":", &gconv_path);
533 /* If the configuration files do not contain any valid module specification
534 remember this by setting the pointer to the module array to NULL. */
535 nmodules += sizeof (builtin_modules) / sizeof (builtin_modules[0]);
536 if (nmodules == 0)
537 __gconv_modules_db = NULL;
538 else
540 __gconv_modules_db =
541 (struct gconv_module **) malloc (nmodules
542 * sizeof (struct gconv_module));
543 if (__gconv_modules_db != NULL)
545 size_t cnt;
547 /* Insert all module entries into the array. */
548 __twalk (modules, insert_module);
550 /* Finally insert the builtin transformations. */
551 for (cnt = 0; cnt < (sizeof (builtin_modules)
552 / sizeof (struct gconv_module)); ++cnt)
553 __gconv_modules_db[__gconv_nmodules++] = &builtin_modules[cnt];
557 /* Add aliases for builtin conversions. */
558 cnt = sizeof (builtin_aliases) / sizeof (builtin_aliases[0]);
559 while (cnt > 0)
561 char *copy = strdupa (builtin_aliases[--cnt]);
562 add_alias (copy, modules);
565 if (nmodules != 0)
566 /* Now remove the tree data structure. */
567 __tdestroy (modules, nothing);
569 /* Restore the error number. */
570 __set_errno (save_errno);