(add_alias): Convert names to uppercase before adding into search tree.
[kopensolaris-gnu/glibc.git] / iconv / gconv_conf.c
1 /* Handle configuration data.
2    Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997.
5
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.
10
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.
15
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.  */
20
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>
30
31 #include <gconv_int.h>
32
33
34 /* This is the default path where we look for module lists.  */
35 static const char default_gconv_path[] = GCONV_PATH;
36
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";
40
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;
46
47 /* We have a few builtin transformations.  */
48 static struct gconv_module builtin_modules[] =
49 {
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                                                         \
61   },
62 #define BUILTIN_ALIAS(From, To)
63
64 #include "gconv_builtin.h"
65 };
66
67 #undef BUILTIN_TRANSFORMATION
68 #undef BUILTIN_ALIAS
69
70 static const char *
71 builtin_aliases[] =
72 {
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,
76
77 #include "gconv_builtin.h"
78 };
79
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
84
85
86 /* Test whether there is already a matching module known.  */
87 static int
88 internal_function
89 detect_conflict (const char *alias, size_t alias_len)
90 {
91   struct gconv_module *node = __gconv_modules_db;
92
93   while (node != NULL)
94     {
95       int cmpres = strncmp (alias, node->from_constpfx,
96                             MIN (alias_len, node->from_constpfx_len));
97
98       if (cmpres == 0)
99         {
100           struct gconv_module *runp;
101
102           if (alias_len < node->from_constpfx_len)
103             /* Cannot possibly match.  */
104             return 0;
105
106           /* This means the prefix and the alias are identical.  If
107              there is now a simple extry or a regular expression
108              matching this name we have found a conflict.  If there is
109              no conflict with the elements in the `same' list there
110              cannot be a conflict.  */
111           runp = node;
112           do
113             {
114               if (runp->from_pattern == NULL)
115                 {
116                   /* This is a simple entry and therefore we have a
117                      conflict if the strings are really the same.  */
118                   if (alias_len == node->from_constpfx_len)
119                     return 1;
120                 }
121               else
122                 {
123                   /* Compile the regular expression if necessary.  */
124                   if (runp->from_regex == NULL)
125                     {
126                       if (__regcomp (&runp->from_regex_mem,
127                                      runp->from_pattern,
128                                      REG_EXTENDED | REG_ICASE) != 0)
129                         /* Something is wrong.  Remember this.  */
130                         runp->from_regex = (regex_t *) -1L;
131                       else
132                         runp->from_regex = &runp->from_regex_mem;
133                     }
134
135                   if (runp->from_regex != (regex_t *) -1L)
136                     {
137                       regmatch_t match[1];
138
139                       /* Try to match the regular expression.  */
140                       if (__regexec (runp->from_regex, alias, 1, match, 0) == 0
141                           && match[0].rm_so == 0
142                           && alias[match[0].rm_eo] == '\0')
143                         /* They match, therefore it is a conflict.  */
144                         return 1;
145                     }
146                 }
147
148               runp = runp->same;
149             }
150           while (runp != NULL);
151
152           if (alias_len == node->from_constpfx_len)
153               return 0;
154
155           node = node->matching;
156         }
157       else if (cmpres < 0)
158         node = node->left;
159       else
160         node = node->right;
161     }
162
163   return node != NULL;
164 }
165
166
167 /* Add new alias.  */
168 static inline void
169 add_alias (char *rp, void *modules)
170 {
171   /* We now expect two more string.  The strings are normalized
172      (converted to UPPER case) and strored in the alias database.  */
173   struct gconv_alias *new_alias;
174   char *from, *to, *wp;
175
176   while (isspace (*rp))
177     ++rp;
178   from = wp = rp;
179   while (*rp != '\0' && !isspace (*rp))
180     *wp = toupper (*rp++);
181   if (*rp == '\0')
182     /* There is no `to' string on the line.  Ignore it.  */
183     return;
184   *rp++ = '\0';
185   to = wp = rp;
186   while (isspace (*rp))
187     ++rp;
188   while (*rp != '\0' && !isspace (*rp))
189     *wp++ = toupper (*rp++);
190   if (to == wp)
191     /* No `to' string, ignore the line.  */
192     return;
193   *wp++ = '\0';
194
195   /* Test whether this alias conflicts with any available module.  */
196   if (detect_conflict (from, to - from - 1))
197     /* It does conflict, don't add the alias.  */
198     return;
199
200   new_alias = (struct gconv_alias *)
201     malloc (sizeof (struct gconv_alias) + (wp - from));
202   if (new_alias != NULL)
203     {
204       new_alias->fromname = memcpy ((char *) new_alias
205                                     + sizeof (struct gconv_alias),
206                                     from, wp - from);
207       new_alias->toname = new_alias->fromname + (to - from);
208
209       if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare)
210           == NULL)
211         /* Something went wrong, free this entry.  */
212         free (new_alias);
213     }
214 }
215
216
217 /* Insert a data structure for a new module in the search tree.  */
218 static inline void
219 internal_function
220 insert_module (struct gconv_module *newp)
221 {
222   struct gconv_module **rootp = &__gconv_modules_db;
223
224   while (*rootp != NULL)
225     {
226       struct gconv_module *root = *rootp;
227       size_t minlen = MIN (newp->from_constpfx_len, root->from_constpfx_len);
228       int cmpres;
229
230       cmpres = strncmp (newp->from_constpfx, root->from_constpfx, minlen);
231       if (cmpres == 0)
232         {
233           /* This can mean two things: the prefix is entirely the same or
234              it matches only for the minimum length of both strings.  */
235           if (newp->from_constpfx_len == root->from_constpfx_len)
236             {
237               /* Both prefixes are identical.  Insert the string at the
238                  end of the `same' list if it is not already there.  */
239               const char *from_pattern = (newp->from_pattern
240                                           ?: newp->from_constpfx);
241
242               while (strcmp (from_pattern,
243                              root->from_pattern ?: root->from_constpfx) != 0
244                      || strcmp (newp->to_string, root->to_string) != 0)
245                 {
246                   rootp = &root->same;
247                   root = *rootp;
248                   if (root == NULL)
249                     break;
250                 }
251
252               if (root != NULL)
253                 /* This is a no new conversion.  */
254                 return;
255
256               break;
257             }
258
259           /* The new element either has a prefix which is itself a
260              prefix for the prefix of the current node or vice verse.
261              In the first case we insert the node right here.  Otherwise
262              we have to descent further.  */
263           if (newp->from_constpfx_len < root->from_constpfx_len)
264             {
265               newp->matching = root;
266               break;
267             }
268
269           rootp = &root->matching;
270         }
271       else if (cmpres < 0)
272         rootp = &root->left;
273       else
274         rootp = &root->right;
275     }
276
277   /* Plug in the new node here.  */
278   *rootp = newp;
279 }
280
281
282 /* Add new module.  */
283 static inline void
284 internal_function
285 add_module (char *rp, const char *directory, size_t dir_len, void **modules,
286             size_t *nmodules, int modcounter)
287 {
288   /* We expect now
289      1. `from' name
290      2. `to' name
291      3. filename of the module
292      4. an optional cost value
293   */
294   struct gconv_module *new_module;
295   char *from, *to, *module, *wp;
296   size_t const_len;
297   int from_is_regex;
298   int need_ext;
299   int cost_hi;
300
301   while (isspace (*rp))
302     ++rp;
303   from = rp;
304   from_is_regex = 0;
305   while (*rp != '\0' && !isspace (*rp))
306     {
307       if (!isalnum (*rp) && *rp != '-' && *rp != '/' && *rp != '.'
308           && *rp != '_' && *rp != '(' && *rp != ')')
309         from_is_regex = 1;
310       *rp = toupper (*rp);
311       ++rp;
312     }
313   if (*rp == '\0')
314     return;
315   *rp++ = '\0';
316   to = wp = rp;
317   while (isspace (*rp))
318     ++rp;
319   while (*rp != '\0' && !isspace (*rp))
320     *wp++ = toupper (*rp++);
321   if (*rp == '\0')
322     return;
323   *wp++ = '\0';
324   do
325     ++rp;
326   while (isspace (*rp));
327   module = wp;
328   while (*rp != '\0' && !isspace (*rp))
329     *wp++ = *rp++;
330   if (*rp == '\0')
331     {
332       /* There is no cost, use one by default.  */
333       *wp++ = '\0';
334       cost_hi = 1;
335     }
336   else
337     {
338       /* There might be a cost value.  */
339       char *endp;
340
341       *wp++ = '\0';
342       cost_hi = strtol (rp, &endp, 10);
343       if (rp == endp || cost_hi < 1)
344         /* No useful information.  */
345         cost_hi = 1;
346     }
347
348   if (module[0] == '\0')
349     /* No module name given.  */
350     return;
351   if (module[0] == '/')
352     dir_len = 0;
353   else
354     /* Increment by one for the slash.  */
355     ++dir_len;
356
357   /* See whether we must add the ending.  */
358   need_ext = 0;
359   if (wp - module < sizeof (gconv_module_ext)
360       || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
361                  sizeof (gconv_module_ext)) != 0)
362     /* We must add the module extension.  */
363     need_ext = sizeof (gconv_module_ext) - 1;
364
365   /* We've collected all the information, now create an entry.  */
366
367   if (from_is_regex)
368     {
369       const_len = 0;
370       while (isalnum (from[const_len]) || from[const_len] == '-'
371              || from[const_len] == '/' || from[const_len] == '.'
372              || from[const_len] == '_')
373         ++const_len;
374     }
375   else
376     const_len = to - from - 1;
377
378   new_module = (struct gconv_module *) calloc (1,
379                                                sizeof (struct gconv_module)
380                                                + (wp - from)
381                                                + dir_len + need_ext);
382   if (new_module != NULL)
383     {
384       char *tmp;
385
386       new_module->from_constpfx = memcpy ((char *) new_module
387                                           + sizeof (struct gconv_module),
388                                           from, to - from);
389       if (from_is_regex)
390         new_module->from_pattern = new_module->from_constpfx;
391
392       new_module->from_constpfx_len = const_len;
393
394       new_module->to_string = memcpy ((char *) new_module->from_constpfx
395                                       + (to - from), to, module - to);
396
397       new_module->cost_hi = cost_hi;
398       new_module->cost_lo = modcounter;
399
400       new_module->module_name = (char *) new_module->to_string + (module - to);
401
402       if (dir_len == 0)
403         tmp = (char *) new_module->module_name;
404       else
405         {
406           tmp = __mempcpy ((char *) new_module->module_name,
407                            directory, dir_len - 1);
408           *tmp++ = '/';
409         }
410
411       tmp = __mempcpy (tmp, module, wp - module);
412
413       if (need_ext)
414         memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
415
416       /* See whether we have already an alias with this name defined.
417          We do allow regular expressions matching this any alias since
418          this expression can also match other names and we test for aliases
419          before testing for modules.  */
420       if (! from_is_regex)
421         {
422           struct gconv_alias fake_alias;
423
424           fake_alias.fromname = new_module->from_constpfx;
425
426           if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare)
427               != NULL)
428             {
429               /* This module duplicates an alias.  */
430               free (new_module);
431               return;
432             }
433         }
434
435       /* Now insert the new module data structure in our search tree.  */
436       insert_module (new_module);
437     }
438 }
439
440
441 /* Read the next configuration file.  */
442 static void
443 internal_function
444 read_conf_file (const char *filename, const char *directory, size_t dir_len,
445                 void **modules, size_t *nmodules)
446 {
447   FILE *fp = fopen (filename, "r");
448   char *line = NULL;
449   size_t line_len = 0;
450   int modcounter = 0;
451
452   /* Don't complain if a file is not present or readable, simply silently
453      ignore it.  */
454   if (fp == NULL)
455     return;
456
457   /* Process the known entries of the file.  Comments start with `#' and
458      end with the end of the line.  Empty lines are ignored.  */
459   while (!feof_unlocked (fp))
460     {
461       char *rp, *endp, *word;
462       ssize_t n = __getdelim (&line, &line_len, '\n', fp);
463       if (n < 0)
464         /* An error occurred.  */
465         break;
466
467       rp = line;
468       /* Terminate the line (excluding comments or newline) by an NUL byte
469          to simplify the following code.  */
470       endp = strchr (rp, '#');
471       if (endp != NULL)
472         *endp = '\0';
473       else
474         if (rp[n - 1] == '\n')
475           rp[n - 1] = '\0';
476
477       while (isspace (*rp))
478         ++rp;
479
480       /* If this is an empty line go on with the next one.  */
481       if (rp == endp)
482         continue;
483
484       word = rp;
485       while (*rp != '\0' && !isspace (*rp))
486         ++rp;
487
488       if (rp - word == sizeof ("alias") - 1
489           && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
490         add_alias (rp, *modules);
491       else if (rp - word == sizeof ("module") - 1
492                && memcmp (word, "module", sizeof ("module") - 1) == 0)
493         add_module (rp, directory, dir_len, modules, nmodules, modcounter++);
494       /* else */
495         /* Otherwise ignore the line.  */
496     }
497
498   if (line != NULL)
499     free (line);
500   fclose (fp);
501 }
502
503
504 /* Read all configuration files found in the user-specified and the default
505    path.  */
506 void
507 __gconv_read_conf (void)
508 {
509   const char *user_path = __secure_getenv ("GCONV_PATH");
510   char *gconv_path, *elem;
511   void *modules = NULL;
512   size_t nmodules = 0;
513   int save_errno = errno;
514   size_t cnt;
515
516   if (user_path == NULL)
517     /* No user-defined path.  Make a modifiable copy of the default path.  */
518     gconv_path = strdupa (default_gconv_path);
519   else
520     {
521       /* Append the default path to the user-defined path.  */
522       size_t user_len = strlen (user_path);
523
524       gconv_path = alloca (user_len + 1 + sizeof (default_gconv_path));
525       __mempcpy (__mempcpy (__mempcpy (gconv_path, user_path, user_len),
526                             ":", 1),
527                  default_gconv_path, sizeof (default_gconv_path));
528     }
529
530   elem = __strtok_r (gconv_path, ":", &gconv_path);
531   while (elem != NULL)
532     {
533 #ifndef MAXPATHLEN
534       /* We define a reasonable limit.  */
535 # define MAXPATHLEN 4096
536 #endif
537       char real_elem[MAXPATHLEN];
538
539       if (__realpath (elem, real_elem) != NULL)
540         {
541           size_t elem_len = strlen (real_elem);
542           char *filename;
543
544           filename = alloca (elem_len + 1 + sizeof (gconv_conf_filename));
545           __mempcpy (__mempcpy (__mempcpy (filename, real_elem, elem_len),
546                                 "/", 1),
547                      gconv_conf_filename, sizeof (gconv_conf_filename));
548
549           /* Read the next configuration file.  */
550           read_conf_file (filename, real_elem, elem_len, &modules, &nmodules);
551         }
552
553       /* Get next element in the path.  */
554       elem = __strtok_r (NULL, ":", &gconv_path);
555     }
556
557   /* Add the internal modules.  */
558   for (cnt = 0; cnt < sizeof (builtin_modules) / sizeof (builtin_modules[0]);
559        ++cnt)
560     {
561       if (builtin_modules[cnt].from_pattern == NULL)
562         {
563           struct gconv_alias fake_alias;
564
565           fake_alias.fromname = builtin_modules[cnt].from_constpfx;
566
567           if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare)
568               != NULL)
569             /* It'll conflict so don't add it.  */
570             continue;
571         }
572
573       insert_module (&builtin_modules[cnt]);
574     }
575
576   /* Add aliases for builtin conversions.  */
577   cnt = sizeof (builtin_aliases) / sizeof (builtin_aliases[0]);
578   while (cnt > 0)
579     {
580       char *copy = strdupa (builtin_aliases[--cnt]);
581       add_alias (copy, modules);
582     }
583
584   /* Restore the error number.  */
585   __set_errno (save_errno);
586 }