(module_compare): Make s1 and s2 const.
[kopensolaris-gnu/glibc.git] / iconv / gconv_conf.c
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.
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 /* Function for searching module.  */
87 static int
88 module_compare (const void *p1, const void *p2)
89 {
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;
93
94   if (s1->from_pattern == NULL)
95     {
96       if (s2->from_pattern == NULL)
97         result = strcmp (s1->from_constpfx, s2->from_constpfx);
98       else
99         result = -1;
100     }
101   else if (s2->from_pattern == NULL)
102     result = 1;
103   else
104     result = strcmp (s1->from_pattern, s2->from_pattern);
105
106   if (result == 0)
107     result = strcmp (s1->to_string, s2->to_string);
108
109   return result;
110 }
111
112
113 /* This function is used to test for a conflict which could be introduced
114    if adding a new alias.
115
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.
123
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.  */
127
128 /* This is alias we want to check.  */
129 static const char *alias_to_test;
130
131 /* This variable is set to a nonzero value once we have found a matching
132    entry.  */
133 static int abort_conflict_search;
134
135 static void
136 detect_conflict (const void *p, VISIT value, int level)
137 {
138   struct gconv_module *s = *(struct gconv_module **) p;
139
140   if ((value != endorder && value != leaf) || s->from_constpfx == NULL
141       || abort_conflict_search)
142     return;
143
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)
148     {
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
153         {
154           /* Make sure the regular expression is compiled (if possible).  */
155           if (s->from_regex == NULL)
156             {
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;
165             }
166
167           if (s->from_regex != (regex_t *) -1L)
168             {
169               regmatch_t match[1];
170
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;
176             }
177         }
178     }
179 }
180
181
182 /* Add new alias.  */
183 static inline void
184 add_alias (char *rp, void *modules)
185 {
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;
190
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';
209
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;
219
220   new_alias = (struct gconv_alias *)
221     malloc (sizeof (struct gconv_alias) + (wp - from));
222   if (new_alias != NULL)
223     {
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);
228
229       if (__tsearch (new_alias, &__gconv_alias_db, __gconv_alias_compare)
230           == NULL)
231         /* Something went wrong, free this entry.  */
232         free (new_alias);
233     }
234 }
235
236
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)
241 {
242   /* We expect now
243      1. `from' name
244      2. `to' name
245      3. filename of the module
246      4. an optional cost value
247   */
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;
254
255   while (isspace (*rp))
256     ++rp;
257   from = rp;
258   from_is_regex = 0;
259   while (*rp != '\0' && !isspace (*rp))
260     {
261       if (!isalnum (*rp) && *rp != '-' && *rp != '/' && *rp != '.'
262           && *rp != '_')
263         from_is_regex = 1;
264       ++rp;
265     }
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';
277   do
278     ++rp;
279   while (isspace (*rp));
280   module = wp;
281   while (*rp != '\0' && !isspace (*rp))
282     *wp++ = *rp++;
283   if (*rp == '\0')
284     {
285       /* There is no cost, use one by default.  */
286       *wp++ = '\0';
287       cost_hi = 1;
288     }
289   else
290     {
291       /* There might be a cost value.  */
292       char *endp;
293
294       *wp++ = '\0';
295       cost_hi = strtol (rp, &endp, 10);
296       if (rp == endp)
297         /* No useful information.  */
298         cost_hi = 1;
299     }
300
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;
309
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;
317
318   /* We've collected all the information, now create an entry.  */
319
320   if (from_is_regex)
321     {
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;
327     }
328   else
329     const_len = to - from - 1;
330
331   new_module = (struct gconv_module *) malloc (sizeof (struct gconv_module)
332                                                + (wp - from)
333                                                + dir_len + need_ext);
334   if (new_module != NULL)
335     {
336       char *tmp;
337
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;
345
346       new_module->from_constpfx_len = const_len;
347
348       new_module->from_regex = NULL;
349
350       new_module->to_string = memcpy ((char *) new_module->from_constpfx
351                                       + (to - from), to, module - to);
352
353       new_module->cost_hi = cost_hi;
354       new_module->cost_lo = modcounter;
355
356       new_module->module_name = (char *) new_module->to_string + (module - to);
357
358       if (dir_len == 0)
359         tmp = (char *) new_module->module_name;
360       else
361         {
362           tmp = __mempcpy ((char *) new_module->module_name,
363                            directory, dir_len - 1);
364           *tmp++ = '/';
365         }
366
367       tmp = __mempcpy (tmp, module, wp - module);
368
369       if (need_ext)
370         memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
371
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)
377         {
378           struct gconv_alias fake_alias;
379
380           fake_alias.fromname = new_module->from_constpfx;
381
382           if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare)
383               != NULL)
384             {
385               /* This module duplicates an alias.  */
386               free (new_module);
387               return;
388             }
389         }
390
391       if (__tfind (new_module, modules, module_compare) == NULL)
392         {
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;
398         }
399     }
400 }
401
402
403 static void
404 insert_module (const void *nodep, VISIT value, int level)
405 {
406   if (value == preorder || value == leaf)
407     __gconv_modules_db[__gconv_nmodules++] = *(struct gconv_module **) nodep;
408 }
409
410 static void
411 nothing (void *unused __attribute__ ((unused)))
412 {
413 }
414
415
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)
421 {
422   FILE *fp = fopen (filename, "r");
423   char *line = NULL;
424   size_t line_len = 0;
425   int modcounter = 0;
426
427   /* Don't complain if a file is not present or readable, simply silently
428      ignore it.  */
429   if (fp == NULL)
430     return;
431
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))
435     {
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;
441
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';
451
452       while (isspace (*rp))
453         ++rp;
454
455       /* If this is an empty line go on with the next one.  */
456       if (rp == endp)
457         continue;
458
459       word = rp;
460       while (*rp != '\0' && !isspace (*rp))
461         ++rp;
462
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.  */
471     }
472
473   if (line != NULL)
474     free (line);
475   fclose (fp);
476 }
477
478
479 /* Read all configuration files found in the user-specified and the default
480    path.  */
481 void
482 __gconv_read_conf (void)
483 {
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;
490
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
495     {
496       /* Append the default path to the user-defined path.  */
497       size_t user_len = strlen (user_path);
498       char *tmp;
499
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));
504     }
505
506   elem = strtok_r (gconv_path, ":", &gconv_path);
507   while (elem != NULL)
508     {
509 #ifndef MAXPATHLEN
510       /* We define a reasonable limit.  */
511 # define MAXPATHLEN 4096
512 #endif
513       char real_elem[MAXPATHLEN];
514
515       if (__realpath (elem, real_elem) != NULL)
516         {
517           size_t elem_len = strlen (real_elem);
518           char *filename, *tmp;
519
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));
524
525           /* Read the next configuration file.  */
526           read_conf_file (filename, real_elem, elem_len, &modules, &nmodules);
527         }
528
529       /* Get next element in the path.  */
530       elem = strtok_r (NULL, ":", &gconv_path);
531     }
532
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
539     {
540       __gconv_modules_db =
541         (struct gconv_module **) malloc (nmodules
542                                          * sizeof (struct gconv_module));
543       if (__gconv_modules_db != NULL)
544         {
545           size_t cnt;
546
547           /* Insert all module entries into the array.  */
548           __twalk (modules, insert_module);
549
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];
554         }
555     }
556
557   /* Add aliases for builtin conversions.  */
558   cnt = sizeof (builtin_aliases) / sizeof (builtin_aliases[0]);
559   while (cnt > 0)
560     {
561       char *copy = strdupa (builtin_aliases[--cnt]);
562       add_alias (copy, modules);
563     }
564   
565   if (nmodules != 0)
566     /* Now remove the tree data structure.  */
567     __tdestroy (modules, nothing);
568
569   /* Restore the error number.  */
570   __set_errno (save_errno);
571 }