(insert_module): Take extra parameter to decide
[kopensolaris-gnu/glibc.git] / iconv / gconv_conf.c
1 /* Handle configuration data.
2    Copyright (C) 1997, 1998, 1999, 2000 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 <assert.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <search.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/param.h>
31
32 #include <bits/libc-lock.h>
33 #include <gconv_int.h>
34
35
36 /* This is the default path where we look for module lists.  */
37 static const char default_gconv_path[] = GCONV_PATH;
38
39 /* The path elements, as determined by the __gconv_get_path function.
40    All path elements end in a slash.  */
41 const struct path_elem *__gconv_path_elem;
42 /* Maximum length of a single path element in __gconv_path_elem.  */
43 size_t __gconv_max_path_elem_len;
44
45 /* We use the following struct if we couldn't allocate memory.  */
46 static const struct path_elem empty_path_elem;
47
48 /* Name of the file containing the module information in the directories
49    along the path.  */
50 static const char gconv_conf_filename[] = "gconv-modules";
51
52 /* Filename extension for the modules.  */
53 #ifndef MODULE_EXT
54 # define MODULE_EXT ".so"
55 #endif
56 static const char gconv_module_ext[] = MODULE_EXT;
57
58 /* We have a few builtin transformations.  */
59 static struct gconv_module builtin_modules[] =
60 {
61 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, Init, End, MinF, \
62                                MaxF, MinT, MaxT) \
63   {                                                                           \
64     from_string: From,                                                        \
65     to_string: To,                                                            \
66     cost_hi: Cost,                                                            \
67     cost_lo: INT_MAX,                                                         \
68     module_name: Name                                                         \
69   },
70 #define BUILTIN_ALIAS(From, To)
71
72 #include "gconv_builtin.h"
73 };
74
75 #undef BUILTIN_TRANSFORMATION
76 #undef BUILTIN_ALIAS
77
78 static const char *builtin_aliases[] =
79 {
80 #define BUILTIN_TRANSFORMATION(From, To, Cost, Name, Fct, Init, End, MinF, \
81                                MaxF, MinT, MaxT)
82 #define BUILTIN_ALIAS(From, To) From " " To,
83
84 #include "gconv_builtin.h"
85 };
86
87 #ifdef USE_IN_LIBIO
88 # include <libio/libioP.h>
89 # define __getdelim(line, len, c, fp) _IO_getdelim (line, len, c, fp)
90 #endif
91
92
93 /* Test whether there is already a matching module known.  */
94 static int
95 internal_function
96 detect_conflict (const char *alias)
97 {
98   struct gconv_module *node = __gconv_modules_db;
99
100   while (node != NULL)
101     {
102       int cmpres = strcmp (alias, node->from_string);
103
104       if (cmpres == 0)
105         /* We have a conflict.  */
106         return 1;
107       else if (cmpres < 0)
108         node = node->left;
109       else
110         node = node->right;
111     }
112
113   return node != NULL;
114 }
115
116
117 /* Add new alias.  */
118 static inline void
119 add_alias (char *rp, void *modules)
120 {
121   /* We now expect two more string.  The strings are normalized
122      (converted to UPPER case) and strored in the alias database.  */
123   struct gconv_alias *new_alias;
124   char *from, *to, *wp;
125
126   while (isspace (*rp))
127     ++rp;
128   from = wp = rp;
129   while (*rp != '\0' && !isspace (*rp))
130     *wp++ = toupper (*rp++);
131   if (*rp == '\0')
132     /* There is no `to' string on the line.  Ignore it.  */
133     return;
134   *wp++ = '\0';
135   to = ++rp;
136   while (isspace (*rp))
137     ++rp;
138   while (*rp != '\0' && !isspace (*rp))
139     *wp++ = toupper (*rp++);
140   if (to == wp)
141     /* No `to' string, ignore the line.  */
142     return;
143   *wp++ = '\0';
144
145   /* Test whether this alias conflicts with any available module.  */
146   if (detect_conflict (from))
147     /* It does conflict, don't add the alias.  */
148     return;
149
150   new_alias = (struct gconv_alias *)
151     malloc (sizeof (struct gconv_alias) + (wp - from));
152   if (new_alias != NULL)
153     {
154       void **inserted;
155
156       new_alias->fromname = memcpy ((char *) new_alias
157                                     + sizeof (struct gconv_alias),
158                                     from, wp - from);
159       new_alias->toname = new_alias->fromname + (to - from);
160
161       inserted = (void **) __tsearch (new_alias, &__gconv_alias_db,
162                                       __gconv_alias_compare);
163       if (inserted == NULL || *inserted != new_alias)
164         /* Something went wrong, free this entry.  */
165         free (new_alias);
166     }
167 }
168
169
170 /* Insert a data structure for a new module in the search tree.  */
171 static inline void
172 internal_function
173 insert_module (struct gconv_module *newp, int tobefreed)
174 {
175   struct gconv_module **rootp = &__gconv_modules_db;
176
177   while (*rootp != NULL)
178     {
179       struct gconv_module *root = *rootp;
180       int cmpres;
181
182       cmpres = strcmp (newp->from_string, root->from_string);
183       if (cmpres == 0)
184         {
185           /* Both strings are identical.  Insert the string at the
186              end of the `same' list if it is not already there.  */
187           while (strcmp (newp->from_string, root->from_string) != 0
188                  || strcmp (newp->to_string, root->to_string) != 0)
189             {
190               rootp = &root->same;
191               root = *rootp;
192               if (root == NULL)
193                 break;
194             }
195
196           if (root != NULL)
197             {
198               /* This is a no new conversion.  */
199               if (tobefreed)
200                 free (newp);
201               return;
202             }
203
204           break;
205         }
206       else if (cmpres < 0)
207         rootp = &root->left;
208       else
209         rootp = &root->right;
210     }
211
212   /* Plug in the new node here.  */
213   *rootp = newp;
214 }
215
216
217 /* Add new module.  */
218 static void
219 internal_function
220 add_module (char *rp, const char *directory, size_t dir_len, void **modules,
221             size_t *nmodules, int modcounter)
222 {
223   /* We expect now
224      1. `from' name
225      2. `to' name
226      3. filename of the module
227      4. an optional cost value
228   */
229   struct gconv_alias fake_alias;
230   struct gconv_module *new_module;
231   char *from, *to, *module, *wp;
232   int need_ext;
233   int cost_hi;
234
235   while (isspace (*rp))
236     ++rp;
237   from = rp;
238   while (*rp != '\0' && !isspace (*rp))
239     {
240       *rp = toupper (*rp);
241       ++rp;
242     }
243   if (*rp == '\0')
244     return;
245   *rp++ = '\0';
246   to = wp = rp;
247   while (isspace (*rp))
248     ++rp;
249   while (*rp != '\0' && !isspace (*rp))
250     *wp++ = toupper (*rp++);
251   if (*rp == '\0')
252     return;
253   *wp++ = '\0';
254   do
255     ++rp;
256   while (isspace (*rp));
257   module = wp;
258   while (*rp != '\0' && !isspace (*rp))
259     *wp++ = *rp++;
260   if (*rp == '\0')
261     {
262       /* There is no cost, use one by default.  */
263       *wp++ = '\0';
264       cost_hi = 1;
265     }
266   else
267     {
268       /* There might be a cost value.  */
269       char *endp;
270
271       *wp++ = '\0';
272       cost_hi = strtol (rp, &endp, 10);
273       if (rp == endp || cost_hi < 1)
274         /* No useful information.  */
275         cost_hi = 1;
276     }
277
278   if (module[0] == '\0')
279     /* No module name given.  */
280     return;
281   if (module[0] == '/')
282     dir_len = 0;
283
284   /* See whether we must add the ending.  */
285   need_ext = 0;
286   if (wp - module < sizeof (gconv_module_ext)
287       || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
288                  sizeof (gconv_module_ext)) != 0)
289     /* We must add the module extension.  */
290     need_ext = sizeof (gconv_module_ext) - 1;
291
292   /* See whether we have already an alias with this name defined.  */
293   fake_alias.fromname = strndupa (from, to - from);
294
295   if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare) != NULL)
296     /* This module duplicates an alias.  */
297     return;
298
299   new_module = (struct gconv_module *) calloc (1,
300                                                sizeof (struct gconv_module)
301                                                + (wp - from)
302                                                + dir_len + need_ext);
303   if (new_module != NULL)
304     {
305       char *tmp;
306
307       new_module->from_string = memcpy ((char *) new_module
308                                         + sizeof (struct gconv_module),
309                                         from, to - from);
310
311       new_module->to_string = memcpy ((char *) new_module->from_string
312                                       + (to - from), to, module - to);
313
314       new_module->cost_hi = cost_hi;
315       new_module->cost_lo = modcounter;
316
317       new_module->module_name = (char *) new_module->to_string + (module - to);
318
319       if (dir_len == 0)
320         tmp = (char *) new_module->module_name;
321       else
322         tmp = __mempcpy ((char *) new_module->module_name,
323                          directory, dir_len);
324
325       tmp = __mempcpy (tmp, module, wp - module);
326
327       if (need_ext)
328         memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
329
330       /* Now insert the new module data structure in our search tree.  */
331       insert_module (new_module, 1);
332     }
333 }
334
335
336 /* Read the next configuration file.  */
337 static void
338 internal_function
339 read_conf_file (const char *filename, const char *directory, size_t dir_len,
340                 void **modules, size_t *nmodules)
341 {
342   FILE *fp = fopen (filename, "r");
343   char *line = NULL;
344   size_t line_len = 0;
345   int modcounter = 0;
346
347   /* Don't complain if a file is not present or readable, simply silently
348      ignore it.  */
349   if (fp == NULL)
350     return;
351
352   /* Process the known entries of the file.  Comments start with `#' and
353      end with the end of the line.  Empty lines are ignored.  */
354   while (!feof_unlocked (fp))
355     {
356       char *rp, *endp, *word;
357       ssize_t n = __getdelim (&line, &line_len, '\n', fp);
358       if (n < 0)
359         /* An error occurred.  */
360         break;
361
362       rp = line;
363       /* Terminate the line (excluding comments or newline) by an NUL byte
364          to simplify the following code.  */
365       endp = strchr (rp, '#');
366       if (endp != NULL)
367         *endp = '\0';
368       else
369         if (rp[n - 1] == '\n')
370           rp[n - 1] = '\0';
371
372       while (isspace (*rp))
373         ++rp;
374
375       /* If this is an empty line go on with the next one.  */
376       if (rp == endp)
377         continue;
378
379       word = rp;
380       while (*rp != '\0' && !isspace (*rp))
381         ++rp;
382
383       if (rp - word == sizeof ("alias") - 1
384           && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
385         add_alias (rp, *modules);
386       else if (rp - word == sizeof ("module") - 1
387                && memcmp (word, "module", sizeof ("module") - 1) == 0)
388         add_module (rp, directory, dir_len, modules, nmodules, modcounter++);
389       /* else */
390         /* Otherwise ignore the line.  */
391     }
392
393   free (line);
394
395   fclose (fp);
396 }
397
398
399 /* Determine the directories we are looking for data in.  */
400 void
401 __gconv_get_path (void)
402 {
403   struct path_elem *result;
404   __libc_lock_define_initialized (static, lock);
405
406   __libc_lock_lock (lock);
407
408   /* Make sure there wasn't a second thread doing it already.  */
409   result = (struct path_elem *) __gconv_path_elem;
410   if (result == NULL)
411     {
412       /* Determine the complete path first.  */
413       const char *user_path;
414       char *gconv_path;
415       size_t gconv_path_len;
416       char *elem;
417       char *oldp;
418       char *cp;
419       int nelems;
420
421       user_path = __secure_getenv ("GCONV_PATH");
422       if (user_path == NULL)
423         {
424           /* No user-defined path.  Make a modifiable copy of the
425              default path.  */
426           gconv_path = strdupa (default_gconv_path);
427           gconv_path_len = sizeof (default_gconv_path);
428         }
429       else
430         {
431           /* Append the default path to the user-defined path.  */
432           size_t user_len = strlen (user_path);
433
434           gconv_path_len = user_len + 1 + sizeof (default_gconv_path);
435           gconv_path = alloca (gconv_path_len);
436           __mempcpy (__mempcpy (__mempcpy (gconv_path, user_path, user_len),
437                                 ":", 1),
438                      default_gconv_path, sizeof (default_gconv_path));
439         }
440
441       /* In a first pass we calculate the number of elements.  */
442       oldp = NULL;
443       cp = strchr (gconv_path, ':');
444       nelems = 1;
445       while (cp != NULL)
446         {
447           if (cp != oldp + 1)
448             ++nelems;
449           oldp = cp;
450           cp =  strchr (cp + 1, ':');
451         }
452
453       /* Allocate the memory for the result.  */
454       result = (struct path_elem *) malloc ((nelems + 1)
455                                             * sizeof (struct path_elem)
456                                             + gconv_path_len + nelems);
457       if (result != NULL)
458         {
459           char *strspace = (char *) &result[nelems + 1];
460           int n = 0;
461
462           /* Separate the individual parts.  */
463           __gconv_max_path_elem_len = 0;
464           elem = __strtok_r (gconv_path, ":", &gconv_path);
465           assert (elem != NULL);
466           do
467             {
468               result[n].name = strspace;
469               strspace = __stpcpy (strspace, elem);
470               if (strspace[-1] != '/')
471                 *strspace++ = '/';
472
473               result[n].len = strspace - result[n].name;
474               if (result[n].len > __gconv_max_path_elem_len)
475                 __gconv_max_path_elem_len = result[n].len;
476
477               *strspace++ = '\0';
478               ++n;
479             }
480           while ((elem = __strtok_r (NULL, ":", &gconv_path)) != NULL);
481
482           result[n].name = NULL;
483           result[n].len = 0;
484         }
485
486       __gconv_path_elem = result ?: &empty_path_elem;
487     }
488
489   __libc_lock_unlock (lock);
490 }
491
492
493 /* Read all configuration files found in the user-specified and the default
494    path.  */
495 void
496 __gconv_read_conf (void)
497 {
498   void *modules = NULL;
499   size_t nmodules = 0;
500   int save_errno = errno;
501   size_t cnt;
502
503   /* Find out where we have to look.  */
504   if (__gconv_path_elem == NULL)
505     __gconv_get_path ();
506
507   for (cnt = 0; __gconv_path_elem[cnt].name != NULL; ++cnt)
508     {
509       const char *elem = __gconv_path_elem[cnt].name;
510       size_t elem_len = __gconv_path_elem[cnt].len;
511       char *filename;
512
513       /* No slash needs to be inserted between elem and gconv_conf_filename;
514          elem already ends in a slash.  */
515       filename = alloca (elem_len + sizeof (gconv_conf_filename));
516       __mempcpy (__mempcpy (filename, elem, elem_len),
517                  gconv_conf_filename, sizeof (gconv_conf_filename));
518
519       /* Read the next configuration file.  */
520       read_conf_file (filename, elem, elem_len, &modules, &nmodules);
521     }
522
523   /* Add the internal modules.  */
524   for (cnt = 0; cnt < sizeof (builtin_modules) / sizeof (builtin_modules[0]);
525        ++cnt)
526     {
527       struct gconv_alias fake_alias;
528
529       fake_alias.fromname = builtin_modules[cnt].from_string;
530
531       if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare)
532           != NULL)
533         /* It'll conflict so don't add it.  */
534         continue;
535
536       insert_module (&builtin_modules[cnt], 0);
537     }
538
539   /* Add aliases for builtin conversions.  */
540   cnt = sizeof (builtin_aliases) / sizeof (builtin_aliases[0]);
541   while (cnt > 0)
542     {
543       char *copy = strdupa (builtin_aliases[--cnt]);
544       add_alias (copy, modules);
545     }
546
547   /* Restore the error number.  */
548   __set_errno (save_errno);
549 }
550
551
552
553 /* Free all resources if necessary.  */
554 static void __attribute__ ((unused))
555 free_mem (void)
556 {
557   if (__gconv_path_elem != NULL && __gconv_path_elem != &empty_path_elem)
558     free ((void *) __gconv_path_elem);
559 }
560
561 text_set_element (__libc_subfreeres, free_mem);