source: trunk/libs/newlib/src/newlib/libc/sys/linux/iconv/gconv_conf.c

Last change on this file was 444, checked in by satin@…, 6 years ago

add newlib,libalmos-mkh, restructure shared_syscalls.h and mini-libc

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