Home | History | Annotate | Download | only in intl
      1 /* Copyright (C) 1995-1999, 2000, 2001, 2002 Free Software Foundation, Inc.
      2    Contributed by Ulrich Drepper <drepper (at) gnu.ai.mit.edu>, 1995.
      3 
      4    This program is free software; you can redistribute it and/or modify it
      5    under the terms of the GNU Library General Public License as published
      6    by the Free Software Foundation; either version 2, or (at your option)
      7    any later version.
      8 
      9    This program is distributed in the hope that it will be useful,
     10    but WITHOUT ANY WARRANTY; without even the implied warranty of
     11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     12    Library General Public License for more details.
     13 
     14    You should have received a copy of the GNU Library General Public
     15    License along with this program; if not, write to the Free Software
     16    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301,
     17    USA.  */
     18 
     19 /* Tell glibc's <string.h> to provide a prototype for stpcpy().
     20    This must come before <config.h> because <config.h> may include
     21    <features.h>, and once <features.h> has been included, it's too late.  */
     22 #ifndef _GNU_SOURCE
     23 # define _GNU_SOURCE	1
     24 #endif
     25 
     26 #ifdef HAVE_CONFIG_H
     27 # include <config.h>
     28 #endif
     29 
     30 #include <string.h>
     31 
     32 #if defined _LIBC || defined HAVE_ARGZ_H
     33 # include <argz.h>
     34 #endif
     35 #include <ctype.h>
     36 #include <sys/types.h>
     37 #include <stdlib.h>
     38 
     39 #include "loadinfo.h"
     40 
     41 /* On some strange systems still no definition of NULL is found.  Sigh!  */
     42 #ifndef NULL
     43 # if defined __STDC__ && __STDC__
     44 #  define NULL ((void *) 0)
     45 # else
     46 #  define NULL 0
     47 # endif
     48 #endif
     49 
     50 /* @@ end of prolog @@ */
     51 
     52 #ifdef _LIBC
     53 /* Rename the non ANSI C functions.  This is required by the standard
     54    because some ANSI C functions will require linking with this object
     55    file and the name space must not be polluted.  */
     56 # ifndef stpcpy
     57 #  define stpcpy(dest, src) __stpcpy(dest, src)
     58 # endif
     59 #else
     60 # ifndef HAVE_STPCPY
     61 static char *stpcpy PARAMS ((char *dest, const char *src));
     62 # endif
     63 #endif
     64 
     65 /* Pathname support.
     66    ISSLASH(C)           tests whether C is a directory separator character.
     67    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
     68                         it may be concatenated to a directory pathname.
     69  */
     70 #if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
     71   /* Win32, OS/2, DOS */
     72 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
     73 # define HAS_DEVICE(P) \
     74     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
     75      && (P)[1] == ':')
     76 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
     77 #else
     78   /* Unix */
     79 # define ISSLASH(C) ((C) == '/')
     80 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
     81 #endif
     82 
     83 /* Define function which are usually not available.  */
     84 
     85 #if !defined _LIBC && !defined HAVE___ARGZ_COUNT
     86 /* Returns the number of strings in ARGZ.  */
     87 static size_t argz_count__ PARAMS ((const char *argz, size_t len));
     88 
     89 static size_t
     90 argz_count__ (argz, len)
     91      const char *argz;
     92      size_t len;
     93 {
     94   size_t count = 0;
     95   while (len > 0)
     96     {
     97       size_t part_len = strlen (argz);
     98       argz += part_len + 1;
     99       len -= part_len + 1;
    100       count++;
    101     }
    102   return count;
    103 }
    104 # undef __argz_count
    105 # define __argz_count(argz, len) argz_count__ (argz, len)
    106 #else
    107 # ifdef _LIBC
    108 #  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
    109 # endif
    110 #endif	/* !_LIBC && !HAVE___ARGZ_COUNT */
    111 
    112 #if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
    113 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
    114    except the last into the character SEP.  */
    115 static void argz_stringify__ PARAMS ((char *argz, size_t len, int sep));
    116 
    117 static void
    118 argz_stringify__ (argz, len, sep)
    119      char *argz;
    120      size_t len;
    121      int sep;
    122 {
    123   while (len > 0)
    124     {
    125       size_t part_len = strlen (argz);
    126       argz += part_len;
    127       len -= part_len + 1;
    128       if (len > 0)
    129 	*argz++ = sep;
    130     }
    131 }
    132 # undef __argz_stringify
    133 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
    134 #else
    135 # ifdef _LIBC
    136 #  define __argz_stringify(argz, len, sep) \
    137   INTUSE(__argz_stringify) (argz, len, sep)
    138 # endif
    139 #endif	/* !_LIBC && !HAVE___ARGZ_STRINGIFY */
    140 
    141 #if !defined _LIBC && !defined HAVE___ARGZ_NEXT
    142 static char *argz_next__ PARAMS ((char *argz, size_t argz_len,
    143 				  const char *entry));
    144 
    145 static char *
    146 argz_next__ (argz, argz_len, entry)
    147      char *argz;
    148      size_t argz_len;
    149      const char *entry;
    150 {
    151   if (entry)
    152     {
    153       if (entry < argz + argz_len)
    154         entry = strchr (entry, '\0') + 1;
    155 
    156       return entry >= argz + argz_len ? NULL : (char *) entry;
    157     }
    158   else
    159     if (argz_len > 0)
    160       return argz;
    161     else
    162       return 0;
    163 }
    164 # undef __argz_next
    165 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
    166 #endif	/* !_LIBC && !HAVE___ARGZ_NEXT */
    167 
    168 
    169 /* Return number of bits set in X.  */
    170 static int pop PARAMS ((int x));
    171 
    172 static inline int
    173 pop (x)
    174      int x;
    175 {
    176   /* We assume that no more than 16 bits are used.  */
    177   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
    178   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
    179   x = ((x >> 4) + x) & 0x0f0f;
    180   x = ((x >> 8) + x) & 0xff;
    181 
    182   return x;
    183 }
    184 
    185 
    186 struct loaded_l10nfile *
    188 _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len, mask, language,
    189 		    territory, codeset, normalized_codeset, modifier, special,
    190 		    sponsor, revision, filename, do_allocate)
    191      struct loaded_l10nfile **l10nfile_list;
    192      const char *dirlist;
    193      size_t dirlist_len;
    194      int mask;
    195      const char *language;
    196      const char *territory;
    197      const char *codeset;
    198      const char *normalized_codeset;
    199      const char *modifier;
    200      const char *special;
    201      const char *sponsor;
    202      const char *revision;
    203      const char *filename;
    204      int do_allocate;
    205 {
    206   char *abs_filename;
    207   struct loaded_l10nfile **lastp;
    208   struct loaded_l10nfile *retval;
    209   char *cp;
    210   size_t dirlist_count;
    211   size_t entries;
    212   int cnt;
    213 
    214   /* If LANGUAGE contains an absolute directory specification, we ignore
    215      DIRLIST.  */
    216   if (IS_ABSOLUTE_PATH (language))
    217     dirlist_len = 0;
    218 
    219   /* Allocate room for the full file name.  */
    220   abs_filename = (char *) malloc (dirlist_len
    221 				  + strlen (language)
    222 				  + ((mask & TERRITORY) != 0
    223 				     ? strlen (territory) + 1 : 0)
    224 				  + ((mask & XPG_CODESET) != 0
    225 				     ? strlen (codeset) + 1 : 0)
    226 				  + ((mask & XPG_NORM_CODESET) != 0
    227 				     ? strlen (normalized_codeset) + 1 : 0)
    228 				  + (((mask & XPG_MODIFIER) != 0
    229 				      || (mask & CEN_AUDIENCE) != 0)
    230 				     ? strlen (modifier) + 1 : 0)
    231 				  + ((mask & CEN_SPECIAL) != 0
    232 				     ? strlen (special) + 1 : 0)
    233 				  + (((mask & CEN_SPONSOR) != 0
    234 				      || (mask & CEN_REVISION) != 0)
    235 				     ? (1 + ((mask & CEN_SPONSOR) != 0
    236 					     ? strlen (sponsor) : 0)
    237 					+ ((mask & CEN_REVISION) != 0
    238 					   ? strlen (revision) + 1 : 0)) : 0)
    239 				  + 1 + strlen (filename) + 1);
    240 
    241   if (abs_filename == NULL)
    242     return NULL;
    243 
    244   /* Construct file name.  */
    245   cp = abs_filename;
    246   if (dirlist_len > 0)
    247     {
    248       memcpy (cp, dirlist, dirlist_len);
    249       __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
    250       cp += dirlist_len;
    251       cp[-1] = '/';
    252     }
    253 
    254   cp = stpcpy (cp, language);
    255 
    256   if ((mask & TERRITORY) != 0)
    257     {
    258       *cp++ = '_';
    259       cp = stpcpy (cp, territory);
    260     }
    261   if ((mask & XPG_CODESET) != 0)
    262     {
    263       *cp++ = '.';
    264       cp = stpcpy (cp, codeset);
    265     }
    266   if ((mask & XPG_NORM_CODESET) != 0)
    267     {
    268       *cp++ = '.';
    269       cp = stpcpy (cp, normalized_codeset);
    270     }
    271   if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
    272     {
    273       /* This component can be part of both syntaces but has different
    274 	 leading characters.  For CEN we use `+', else `@'.  */
    275       *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
    276       cp = stpcpy (cp, modifier);
    277     }
    278   if ((mask & CEN_SPECIAL) != 0)
    279     {
    280       *cp++ = '+';
    281       cp = stpcpy (cp, special);
    282     }
    283   if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
    284     {
    285       *cp++ = ',';
    286       if ((mask & CEN_SPONSOR) != 0)
    287 	cp = stpcpy (cp, sponsor);
    288       if ((mask & CEN_REVISION) != 0)
    289 	{
    290 	  *cp++ = '_';
    291 	  cp = stpcpy (cp, revision);
    292 	}
    293     }
    294 
    295   *cp++ = '/';
    296   stpcpy (cp, filename);
    297 
    298   /* Look in list of already loaded domains whether it is already
    299      available.  */
    300   lastp = l10nfile_list;
    301   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
    302     if (retval->filename != NULL)
    303       {
    304 	int compare = strcmp (retval->filename, abs_filename);
    305 	if (compare == 0)
    306 	  /* We found it!  */
    307 	  break;
    308 	if (compare < 0)
    309 	  {
    310 	    /* It's not in the list.  */
    311 	    retval = NULL;
    312 	    break;
    313 	  }
    314 
    315 	lastp = &retval->next;
    316       }
    317 
    318   if (retval != NULL || do_allocate == 0)
    319     {
    320       free (abs_filename);
    321       return retval;
    322     }
    323 
    324   dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
    325 
    326   /* Allocate a new loaded_l10nfile.  */
    327   retval =
    328     (struct loaded_l10nfile *)
    329     malloc (sizeof (*retval)
    330 	    + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
    331 	       * sizeof (struct loaded_l10nfile *)));
    332   if (retval == NULL)
    333     return NULL;
    334 
    335   retval->filename = abs_filename;
    336 
    337   /* We set retval->data to NULL here; it is filled in later.
    338      Setting retval->decided to 1 here means that retval does not
    339      correspond to a real file (dirlist_count > 1) or is not worth
    340      looking up (if an unnormalized codeset was specified).  */
    341   retval->decided = (dirlist_count > 1
    342 		     || ((mask & XPG_CODESET) != 0
    343 			 && (mask & XPG_NORM_CODESET) != 0));
    344   retval->data = NULL;
    345 
    346   retval->next = *lastp;
    347   *lastp = retval;
    348 
    349   entries = 0;
    350   /* Recurse to fill the inheritance list of RETVAL.
    351      If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
    352      entry does not correspond to a real file; retval->filename contains
    353      colons.  In this case we loop across all elements of DIRLIST and
    354      across all bit patterns dominated by MASK.
    355      If the DIRLIST is a single directory or entirely redundant (i.e.
    356      DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
    357      MASK, excluding MASK itself.
    358      In either case, we loop down from MASK to 0.  This has the effect
    359      that the extra bits in the locale name are dropped in this order:
    360      first the modifier, then the territory, then the codeset, then the
    361      normalized_codeset.  */
    362   for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
    363     if ((cnt & ~mask) == 0
    364 	&& ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
    365 	&& ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
    366       {
    367 	if (dirlist_count > 1)
    368 	  {
    369 	    /* Iterate over all elements of the DIRLIST.  */
    370 	    char *dir = NULL;
    371 
    372 	    while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
    373 		   != NULL)
    374 	      retval->successor[entries++]
    375 		= _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
    376 				      cnt, language, territory, codeset,
    377 				      normalized_codeset, modifier, special,
    378 				      sponsor, revision, filename, 1);
    379 	  }
    380 	else
    381 	  retval->successor[entries++]
    382 	    = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
    383 				  cnt, language, territory, codeset,
    384 				  normalized_codeset, modifier, special,
    385 				  sponsor, revision, filename, 1);
    386       }
    387   retval->successor[entries] = NULL;
    388 
    389   return retval;
    390 }
    391 
    392 /* Normalize codeset name.  There is no standard for the codeset
    394    names.  Normalization allows the user to use any of the common
    395    names.  The return value is dynamically allocated and has to be
    396    freed by the caller.  */
    397 const char *
    398 _nl_normalize_codeset (codeset, name_len)
    399      const char *codeset;
    400      size_t name_len;
    401 {
    402   int len = 0;
    403   int only_digit = 1;
    404   char *retval;
    405   char *wp;
    406   size_t cnt;
    407 
    408   for (cnt = 0; cnt < name_len; ++cnt)
    409     if (isalnum ((unsigned char) codeset[cnt]))
    410       {
    411 	++len;
    412 
    413 	if (isalpha ((unsigned char) codeset[cnt]))
    414 	  only_digit = 0;
    415       }
    416 
    417   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
    418 
    419   if (retval != NULL)
    420     {
    421       if (only_digit)
    422 	wp = stpcpy (retval, "iso");
    423       else
    424 	wp = retval;
    425 
    426       for (cnt = 0; cnt < name_len; ++cnt)
    427 	if (isalpha ((unsigned char) codeset[cnt]))
    428 	  *wp++ = tolower ((unsigned char) codeset[cnt]);
    429 	else if (isdigit ((unsigned char) codeset[cnt]))
    430 	  *wp++ = codeset[cnt];
    431 
    432       *wp = '\0';
    433     }
    434 
    435   return (const char *) retval;
    436 }
    437 
    438 
    439 /* @@ begin of epilog @@ */
    440 
    441 /* We don't want libintl.a to depend on any other library.  So we
    442    avoid the non-standard function stpcpy.  In GNU C Library this
    443    function is available, though.  Also allow the symbol HAVE_STPCPY
    444    to be defined.  */
    445 #if !_LIBC && !HAVE_STPCPY
    446 static char *
    447 stpcpy (dest, src)
    448      char *dest;
    449      const char *src;
    450 {
    451   while ((*dest++ = *src++) != '\0')
    452     /* Do nothing. */ ;
    453   return dest - 1;
    454 }
    455 #endif
    456