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