(linenum→info "unix/slp.c:2238")

glibc/2.7/iconv/gconv_conf.c

    1: /* Handle configuration data.
    2:    Copyright (C) 1997-2003, 2005, 2006 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: #ifdef USE_IN_LIBIO
   94: # include <libio/libioP.h>
   95: # define __getdelim(line, len, c, fp) _IO_getdelim (line, len, c, fp)
   96: #endif
   97: 
   98: 
   99: /* Value of the GCONV_PATH environment variable.  */
  100: const char *__gconv_path_envvar;
  101: 
  102: 
  103: /* Test whether there is already a matching module known.  */
  104: static int
  105: internal_function
  106: detect_conflict (const char *alias)
  107: {
  108:   struct gconv_module *node = __gconv_modules_db;
  109: 
  110:   while (node != NULL)
  111:     {
  112:       int cmpres = strcmp (alias, node->from_string);
  113: 
  114:       if (cmpres == 0)
  115:         /* We have a conflict.  */
  116:         return 1;
  117:       else if (cmpres < 0)
  118:         node = node->left;
  119:       else
  120:         node = node->right;
  121:     }
  122: 
  123:   return node != NULL;
  124: }
  125: 
  126: 
  127: /* The actual code to add aliases.  */
  128: static void
  129: add_alias2 (const char *from, const char *to, const char *wp, void *modules)
  130: {
  131:   /* Test whether this alias conflicts with any available module.  */
  132:   if (detect_conflict (from))
  133:     /* It does conflict, don't add the alias.  */
  134:     return;
  135: 
  136:   struct gconv_alias *new_alias = (struct gconv_alias *)
  137:     malloc (sizeof (struct gconv_alias) + (wp - from));
  138:   if (new_alias != NULL)
  139:     {
  140:       void **inserted;
  141: 
  142:       new_alias->fromname = memcpy ((char *) new_alias
  143:                                     + sizeof (struct gconv_alias),
  144:                                     from, wp - from);
  145:       new_alias->toname = new_alias->fromname + (to - from);
  146: 
  147:       inserted = (void **) __tsearch (new_alias, &__gconv_alias_db,
  148:                                       __gconv_alias_compare);
  149:       if (inserted == NULL || *inserted != new_alias)
  150:         /* Something went wrong, free this entry.  */
  151:         free (new_alias);
  152:     }
  153: }
  154: 
  155: 
  156: /* Add new alias.  */
  157: static void
  158: add_alias (char *rp, void *modules)
  159: {
  160:   /* We now expect two more string.  The strings are normalized
  161:      (converted to UPPER case) and strored in the alias database.  */
  162:   char *from, *to, *wp;
  163: 
  164:   while (__isspace_l (*rp, _nl_C_locobj_ptr))
  165:     ++rp;
  166:   from = wp = rp;
  167:   while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
  168:     *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
  169:   if (*rp == '\0')
  170:     /* There is no `to' string on the line.  Ignore it.  */
  171:     return;
  172:   *wp++ = '\0';
  173:   to = ++rp;
  174:   while (__isspace_l (*rp, _nl_C_locobj_ptr))
  175:     ++rp;
  176:   while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
  177:     *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
  178:   if (to == wp)
  179:     /* No `to' string, ignore the line.  */
  180:     return;
  181:   *wp++ = '\0';
  182: 
  183:   add_alias2 (from, to, wp, modules);
  184: }
  185: 
  186: 
  187: /* Insert a data structure for a new module in the search tree.  */
  188: static void
  189: internal_function
  190: insert_module (struct gconv_module *newp, int tobefreed)
  191: {
  192:   struct gconv_module **rootp = &__gconv_modules_db;
  193: 
  194:   while (*rootp != NULL)
  195:     {
  196:       struct gconv_module *root = *rootp;
  197:       int cmpres;
  198: 
  199:       cmpres = strcmp (newp->from_string, root->from_string);
  200:       if (cmpres == 0)
  201:         {
  202:           /* Both strings are identical.  Insert the string at the
  203:              end of the `same' list if it is not already there.  */
  204:           while (strcmp (newp->from_string, root->from_string) != 0
  205:                  || strcmp (newp->to_string, root->to_string) != 0)
  206:             {
  207:               rootp = &root->same;
  208:               root = *rootp;
  209:               if (root == NULL)
  210:                 break;
  211:             }
  212: 
  213:           if (root != NULL)
  214:             {
  215:               /* This is a no new conversion.  But maybe the cost is
  216:                  better.  */
  217:               if (newp->cost_hi < root->cost_hi
  218:                   || (newp->cost_hi == root->cost_hi
  219:                       && newp->cost_lo < root->cost_lo))
  220:                 {
  221:                   newp->left = root->left;
  222:                   newp->right = root->right;
  223:                   newp->same = root->same;
  224:                   *rootp = newp;
  225: 
  226:                   free (root);
  227:                 }
  228:               else if (tobefreed)
  229:                 free (newp);
  230:               return;
  231:             }
  232: 
  233:           break;
  234:         }
  235:       else if (cmpres < 0)
  236:         rootp = &root->left;
  237:       else
  238:         rootp = &root->right;
  239:     }
  240: 
  241:   /* Plug in the new node here.  */
  242:   *rootp = newp;
  243: }
  244: 
  245: 
  246: /* Add new module.  */
  247: static void
  248: internal_function
  249: add_module (char *rp, const char *directory, size_t dir_len, void **modules,
  250:             size_t *nmodules, int modcounter)
  251: {
  252:   /* We expect now
  253:      1. `from' name
  254:      2. `to' name
  255:      3. filename of the module
  256:      4. an optional cost value
  257:   */
  258:   struct gconv_alias fake_alias;
  259:   struct gconv_module *new_module;
  260:   char *from, *to, *module, *wp;
  261:   int need_ext;
  262:   int cost_hi;
  263: 
  264:   while (__isspace_l (*rp, _nl_C_locobj_ptr))
  265:     ++rp;
  266:   from = rp;
  267:   while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
  268:     {
  269:       *rp = __toupper_l (*rp, _nl_C_locobj_ptr);
  270:       ++rp;
  271:     }
  272:   if (*rp == '\0')
  273:     return;
  274:   *rp++ = '\0';
  275:   to = wp = rp;
  276:   while (__isspace_l (*rp, _nl_C_locobj_ptr))
  277:     ++rp;
  278:   while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
  279:     *wp++ = __toupper_l (*rp++, _nl_C_locobj_ptr);
  280:   if (*rp == '\0')
  281:     return;
  282:   *wp++ = '\0';
  283:   do
  284:     ++rp;
  285:   while (__isspace_l (*rp, _nl_C_locobj_ptr));
  286:   module = wp;
  287:   while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
  288:     *wp++ = *rp++;
  289:   if (*rp == '\0')
  290:     {
  291:       /* There is no cost, use one by default.  */
  292:       *wp++ = '\0';
  293:       cost_hi = 1;
  294:     }
  295:   else
  296:     {
  297:       /* There might be a cost value.  */
  298:       char *endp;
  299: 
  300:       *wp++ = '\0';
  301:       cost_hi = strtol (rp, &endp, 10);
  302:       if (rp == endp || cost_hi < 1)
  303:         /* No useful information.  */
  304:         cost_hi = 1;
  305:     }
  306: 
  307:   if (module[0] == '\0')
  308:     /* No module name given.  */
  309:     return;
  310:   if (module[0] == '/')
  311:     dir_len = 0;
  312: 
  313:   /* See whether we must add the ending.  */
  314:   need_ext = 0;
  315:   if (wp - module < (ptrdiff_t) sizeof (gconv_module_ext)
  316:       || memcmp (wp - sizeof (gconv_module_ext), gconv_module_ext,
  317:                  sizeof (gconv_module_ext)) != 0)
  318:     /* We must add the module extension.  */
  319:     need_ext = sizeof (gconv_module_ext) - 1;
  320: 
  321:   /* See whether we have already an alias with this name defined.  */
  322:   fake_alias.fromname = strndupa (from, to - from);
  323: 
  324:   if (__tfind (&fake_alias, &__gconv_alias_db, __gconv_alias_compare) != NULL)
  325:     /* This module duplicates an alias.  */
  326:     return;
  327: 
  328:   new_module = (struct gconv_module *) calloc (1,
  329:                                                sizeof (struct gconv_module)
  330:                                                + (wp - from)
  331:                                                + dir_len + need_ext);
  332:   if (new_module != NULL)
  333:     {
  334:       char *tmp;
  335: 
  336:       new_module->from_string = tmp = (char *) (new_module + 1);
  337:       tmp = __mempcpy (tmp, from, to - from);
  338: 
  339:       new_module->to_string = tmp;
  340:       tmp = __mempcpy (tmp, to, module - to);
  341: 
  342:       new_module->cost_hi = cost_hi;
  343:       new_module->cost_lo = modcounter;
  344: 
  345:       new_module->module_name = tmp;
  346: 
  347:       if (dir_len != 0)
  348:         tmp = __mempcpy (tmp, directory, dir_len);
  349: 
  350:       tmp = __mempcpy (tmp, module, wp - module);
  351: 
  352:       if (need_ext)
  353:         memcpy (tmp - 1, gconv_module_ext, sizeof (gconv_module_ext));
  354: 
  355:       /* Now insert the new module data structure in our search tree.  */
  356:       insert_module (new_module, 1);
  357:     }
  358: }
  359: 
  360: 
  361: /* Read the next configuration file.  */
  362: static void
  363: internal_function
  364: read_conf_file (const char *filename, const char *directory, size_t dir_len,
  365:                 void **modules, size_t *nmodules)
  366: {
  367:   /* Note the file is opened with cancellation in the I/O functions
  368:      disabled.  */
  369:   FILE *fp = fopen (filename, "rc");
  370:   char *line = NULL;
  371:   size_t line_len = 0;
  372:   static int modcounter;
  373: 
  374:   /* Don't complain if a file is not present or readable, simply silently
  375:      ignore it.  */
  376:   if (fp == NULL)
  377:     return;
  378: 
  379:   /* No threads reading from this stream.  */
  380:   __fsetlocking (fp, FSETLOCKING_BYCALLER);
  381: 
  382:   /* Process the known entries of the file.  Comments start with `#' and
  383:      end with the end of the line.  Empty lines are ignored.  */
  384:   while (!feof_unlocked (fp))
  385:     {
  386:       char *rp, *endp, *word;
  387:       ssize_t n = __getdelim (&line, &line_len, '\n', fp);
  388:       if (n < 0)
  389:         /* An error occurred.  */
  390:         break;
  391: 
  392:       rp = line;
  393:       /* Terminate the line (excluding comments or newline) by an NUL byte
  394:          to simplify the following code.  */
  395:       endp = strchr (rp, '#');
  396:       if (endp != NULL)
  397:         *endp = '\0';
  398:       else
  399:         if (rp[n - 1] == '\n')
  400:           rp[n - 1] = '\0';
  401: 
  402:       while (__isspace_l (*rp, _nl_C_locobj_ptr))
  403:         ++rp;
  404: 
  405:       /* If this is an empty line go on with the next one.  */
  406:       if (rp == endp)
  407:         continue;
  408: 
  409:       word = rp;
  410:       while (*rp != '\0' && !__isspace_l (*rp, _nl_C_locobj_ptr))
  411:         ++rp;
  412: 
  413:       if (rp - word == sizeof ("alias") - 1
  414:           && memcmp (word, "alias", sizeof ("alias") - 1) == 0)
  415:         add_alias (rp, *modules);
  416:       else if (rp - word == sizeof ("module") - 1
  417:                && memcmp (word, "module", sizeof ("module") - 1) == 0)
  418:         add_module (rp, directory, dir_len, modules, nmodules, modcounter++);
  419:       /* else */
  420:         /* Otherwise ignore the line.  */
  421:     }
  422: 
  423:   free (line);
  424: 
  425:   fclose (fp);
  426: }
  427: 
  428: 
  429: /* Determine the directories we are looking for data in.  */
  430: void
  431: internal_function
  432: __gconv_get_path (void)
  433: {
  434:   struct path_elem *result;
  435:   __libc_lock_define_initialized (static, lock);
  436: 
  437:   __libc_lock_lock (lock);
  438: 
  439:   /* Make sure there wasn't a second thread doing it already.  */
  440:   result = (struct path_elem *) __gconv_path_elem;
  441:   if (result == NULL)
  442:     {
  443:       /* Determine the complete path first.  */
  444:       char *gconv_path;
  445:       size_t gconv_path_len;
  446:       char *elem;
  447:       char *oldp;
  448:       char *cp;
  449:       int nelems;
  450:       char *cwd;
  451:       size_t cwdlen;
  452: 
  453:       if (__gconv_path_envvar == NULL)
  454:         {
  455:           /* No user-defined path.  Make a modifiable copy of the
  456:              default path.  */
  457:           gconv_path = strdupa (default_gconv_path);
  458:           gconv_path_len = sizeof (default_gconv_path);
  459:           cwd = NULL;
  460:           cwdlen = 0;
  461:         }
  462:       else
  463:         {
  464:           /* Append the default path to the user-defined path.  */
  465:           size_t user_len = strlen (__gconv_path_envvar);
  466: 
  467:           gconv_path_len = user_len + 1 + sizeof (default_gconv_path);
  468:           gconv_path = alloca (gconv_path_len);
  469:           __mempcpy (__mempcpy (__mempcpy (gconv_path, __gconv_path_envvar,
  470:                                            user_len),
  471:                                 ":", 1),
  472:                      default_gconv_path, sizeof (default_gconv_path));
  473:           cwd = __getcwd (NULL, 0);
  474:           cwdlen = strlen (cwd);
  475:         }
  476:       assert (default_gconv_path[0] == '/');
  477: 
  478:       /* In a first pass we calculate the number of elements.  */
  479:       oldp = NULL;
  480:       cp = strchr (gconv_path, ':');
  481:       nelems = 1;
  482:       while (cp != NULL)
  483:         {
  484:           if (cp != oldp + 1)
  485:             ++nelems;
  486:           oldp = cp;
  487:           cp =  strchr (cp + 1, ':');
  488:         }
  489: 
  490:       /* Allocate the memory for the result.  */
  491:       result = (struct path_elem *) malloc ((nelems + 1)
  492:                                             * sizeof (struct path_elem)
  493:                                             + gconv_path_len + nelems
  494:                                             + (nelems - 1) * (cwdlen + 1));
  495:       if (result != NULL)
  496:         {
  497:           char *strspace = (char *) &result[nelems + 1];
  498:           int n = 0;
  499: 
  500:           /* Separate the individual parts.  */
  501:           __gconv_max_path_elem_len = 0;
  502:           elem = __strtok_r (gconv_path, ":", &gconv_path);
  503:           assert (elem != NULL);
  504:           do
  505:             {
  506:               result[n].name = strspace;
  507:               if (elem[0] != '/')
  508:                 {
  509:                   assert (cwd != NULL);
  510:                   strspace = __mempcpy (strspace, cwd, cwdlen);
  511:                   *strspace++ = '/';
  512:                 }
  513:               strspace = __stpcpy (strspace, elem);
  514:               if (strspace[-1] != '/')
  515:                 *strspace++ = '/';
  516: 
  517:               result[n].len = strspace - result[n].name;
  518:               if (result[n].len > __gconv_max_path_elem_len)
  519:                 __gconv_max_path_elem_len = result[n].len;
  520: 
  521:               *strspace++ = '\0';
  522:               ++n;
  523:             }
  524:           while ((elem = __strtok_r (NULL, ":", &gconv_path)) != NULL);
  525: 
  526:           result[n].name = NULL;
  527:           result[n].len = 0;
  528:         }
  529: 
  530:       __gconv_path_elem = result ?: (struct path_elem *) &empty_path_elem;
  531: 
  532:       if (cwd != NULL)
  533:         free (cwd);
  534:     }
  535: 
  536:   __libc_lock_unlock (lock);
  537: }
  538: 
  539: 
  540: /* Read all configuration files found in the user-specified and the default
  541:    path.  */
  542: void
  543: attribute_hidden
  544: __gconv_read_conf (void)
  545: {
  546:   void *modules = NULL;
  547:   size_t nmodules = 0;
  548:   int save_errno = errno;
  549:   size_t cnt;
  550: 
  551:   /* First see whether we should use the cache.  */
  552:   if (__gconv_load_cache () == 0)
  553:     {
  554:       /* Yes, we are done.  */
  555:       __set_errno (save_errno);
  556:       return;
  557:     }
  558: 
  559: #ifndef STATIC_GCONV
  560:   /* Find out where we have to look.  */
  561:   if (__gconv_path_elem == NULL)
  562:     __gconv_get_path ();
  563: 
  564:   for (cnt = 0; __gconv_path_elem[cnt].name != NULL; ++cnt)
  565:     {
  566:       const char *elem = __gconv_path_elem[cnt].name;
  567:       size_t elem_len = __gconv_path_elem[cnt].len;
  568:       char *filename;
  569: 
  570:       /* No slash needs to be inserted between elem and gconv_conf_filename;
  571:          elem already ends in a slash.  */
  572:       filename = alloca (elem_len + sizeof (gconv_conf_filename));
  573:       __mempcpy (__mempcpy (filename, elem, elem_len),
  574:                  gconv_conf_filename, sizeof (gconv_conf_filename));
  575: 
  576:       /* Read the next configuration file.  */
  577:       read_conf_file (filename, elem, elem_len, &modules, &nmodules);