Back to index

courier  0.68.2
l10nflist.c
Go to the documentation of this file.
00001 /* Copyright (C) 1995-1999, 2000-2007 Free Software Foundation, Inc.
00002    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
00003 
00004    This program is free software; you can redistribute it and/or modify it
00005    under the terms of the GNU Library General Public License as published
00006    by the Free Software Foundation; either version 2, or (at your option)
00007    any later version.
00008 
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public
00015    License along with this program; if not, write to the Free Software
00016    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
00017    USA.  */
00018 
00019 /* Tell glibc's <string.h> to provide a prototype for stpcpy().
00020    This must come before <config.h> because <config.h> may include
00021    <features.h>, and once <features.h> has been included, it's too late.  */
00022 #ifndef _GNU_SOURCE
00023 # define _GNU_SOURCE 1
00024 #endif
00025 
00026 #ifdef HAVE_CONFIG_H
00027 # include <config.h>
00028 #endif
00029 
00030 #include <string.h>
00031 
00032 #if defined _LIBC || defined HAVE_ARGZ_H
00033 # include <argz.h>
00034 #endif
00035 #include <ctype.h>
00036 #include <sys/types.h>
00037 #include <stdlib.h>
00038 
00039 #include "loadinfo.h"
00040 
00041 /* On some strange systems still no definition of NULL is found.  Sigh!  */
00042 #ifndef NULL
00043 # if defined __STDC__ && __STDC__
00044 #  define NULL ((void *) 0)
00045 # else
00046 #  define NULL 0
00047 # endif
00048 #endif
00049 
00050 /* @@ end of prolog @@ */
00051 
00052 #ifdef _LIBC
00053 /* Rename the non ANSI C functions.  This is required by the standard
00054    because some ANSI C functions will require linking with this object
00055    file and the name space must not be polluted.  */
00056 # ifndef stpcpy
00057 #  define stpcpy(dest, src) __stpcpy(dest, src)
00058 # endif
00059 #else
00060 # ifndef HAVE_STPCPY
00061 static char *stpcpy (char *dest, const char *src);
00062 # endif
00063 #endif
00064 
00065 /* Pathname support.
00066    ISSLASH(C)           tests whether C is a directory separator character.
00067    IS_ABSOLUTE_PATH(P)  tests whether P is an absolute path.  If it is not,
00068                         it may be concatenated to a directory pathname.
00069  */
00070 #if defined _WIN32 || defined __WIN32__ || defined __CYGWIN__ || defined __EMX__ || defined __DJGPP__
00071   /* Win32, Cygwin, OS/2, DOS */
00072 # define ISSLASH(C) ((C) == '/' || (C) == '\\')
00073 # define HAS_DEVICE(P) \
00074     ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) \
00075      && (P)[1] == ':')
00076 # define IS_ABSOLUTE_PATH(P) (ISSLASH ((P)[0]) || HAS_DEVICE (P))
00077 #else
00078   /* Unix */
00079 # define ISSLASH(C) ((C) == '/')
00080 # define IS_ABSOLUTE_PATH(P) ISSLASH ((P)[0])
00081 #endif
00082 
00083 /* Define function which are usually not available.  */
00084 
00085 #ifdef _LIBC
00086 # define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
00087 #elif defined HAVE_ARGZ_COUNT
00088 # undef __argz_count
00089 # define __argz_count argz_count
00090 #else
00091 /* Returns the number of strings in ARGZ.  */
00092 static size_t
00093 argz_count__ (const char *argz, size_t len)
00094 {
00095   size_t count = 0;
00096   while (len > 0)
00097     {
00098       size_t part_len = strlen (argz);
00099       argz += part_len + 1;
00100       len -= part_len + 1;
00101       count++;
00102     }
00103   return count;
00104 }
00105 # undef __argz_count
00106 # define __argz_count(argz, len) argz_count__ (argz, len)
00107 #endif /* !_LIBC && !HAVE_ARGZ_COUNT */
00108 
00109 #ifdef _LIBC
00110 # define __argz_stringify(argz, len, sep) \
00111   INTUSE(__argz_stringify) (argz, len, sep)
00112 #elif defined HAVE_ARGZ_STRINGIFY
00113 # undef __argz_stringify
00114 # define __argz_stringify argz_stringify
00115 #else
00116 /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
00117    except the last into the character SEP.  */
00118 static void
00119 argz_stringify__ (char *argz, size_t len, int sep)
00120 {
00121   while (len > 0)
00122     {
00123       size_t part_len = strlen (argz);
00124       argz += part_len;
00125       len -= part_len + 1;
00126       if (len > 0)
00127        *argz++ = sep;
00128     }
00129 }
00130 # undef __argz_stringify
00131 # define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
00132 #endif /* !_LIBC && !HAVE_ARGZ_STRINGIFY */
00133 
00134 #ifdef _LIBC
00135 #elif defined HAVE_ARGZ_NEXT
00136 # undef __argz_next
00137 # define __argz_next argz_next
00138 #else
00139 static char *
00140 argz_next__ (char *argz, size_t argz_len, const char *entry)
00141 {
00142   if (entry)
00143     {
00144       if (entry < argz + argz_len)
00145         entry = strchr (entry, '\0') + 1;
00146 
00147       return entry >= argz + argz_len ? NULL : (char *) entry;
00148     }
00149   else
00150     if (argz_len > 0)
00151       return argz;
00152     else
00153       return 0;
00154 }
00155 # undef __argz_next
00156 # define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
00157 #endif /* !_LIBC && !HAVE_ARGZ_NEXT */
00158 
00159 
00160 /* Return number of bits set in X.  */
00161 static inline int
00162 pop (int x)
00163 {
00164   /* We assume that no more than 16 bits are used.  */
00165   x = ((x & ~0x5555) >> 1) + (x & 0x5555);
00166   x = ((x & ~0x3333) >> 2) + (x & 0x3333);
00167   x = ((x >> 4) + x) & 0x0f0f;
00168   x = ((x >> 8) + x) & 0xff;
00169 
00170   return x;
00171 }
00172 
00173 
00174 struct loaded_l10nfile *
00175 _nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
00176                   const char *dirlist, size_t dirlist_len,
00177                   int mask, const char *language, const char *territory,
00178                   const char *codeset, const char *normalized_codeset,
00179                   const char *modifier,
00180                   const char *filename, int do_allocate)
00181 {
00182   char *abs_filename;
00183   struct loaded_l10nfile **lastp;
00184   struct loaded_l10nfile *retval;
00185   char *cp;
00186   size_t dirlist_count;
00187   size_t entries;
00188   int cnt;
00189 
00190   /* If LANGUAGE contains an absolute directory specification, we ignore
00191      DIRLIST.  */
00192   if (IS_ABSOLUTE_PATH (language))
00193     dirlist_len = 0;
00194 
00195   /* Allocate room for the full file name.  */
00196   abs_filename = (char *) malloc (dirlist_len
00197                               + strlen (language)
00198                               + ((mask & XPG_TERRITORY) != 0
00199                                  ? strlen (territory) + 1 : 0)
00200                               + ((mask & XPG_CODESET) != 0
00201                                  ? strlen (codeset) + 1 : 0)
00202                               + ((mask & XPG_NORM_CODESET) != 0
00203                                  ? strlen (normalized_codeset) + 1 : 0)
00204                               + ((mask & XPG_MODIFIER) != 0
00205                                  ? strlen (modifier) + 1 : 0)
00206                               + 1 + strlen (filename) + 1);
00207 
00208   if (abs_filename == NULL)
00209     return NULL;
00210 
00211   /* Construct file name.  */
00212   cp = abs_filename;
00213   if (dirlist_len > 0)
00214     {
00215       memcpy (cp, dirlist, dirlist_len);
00216       __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
00217       cp += dirlist_len;
00218       cp[-1] = '/';
00219     }
00220 
00221   cp = stpcpy (cp, language);
00222 
00223   if ((mask & XPG_TERRITORY) != 0)
00224     {
00225       *cp++ = '_';
00226       cp = stpcpy (cp, territory);
00227     }
00228   if ((mask & XPG_CODESET) != 0)
00229     {
00230       *cp++ = '.';
00231       cp = stpcpy (cp, codeset);
00232     }
00233   if ((mask & XPG_NORM_CODESET) != 0)
00234     {
00235       *cp++ = '.';
00236       cp = stpcpy (cp, normalized_codeset);
00237     }
00238   if ((mask & XPG_MODIFIER) != 0)
00239     {
00240       *cp++ = '@';
00241       cp = stpcpy (cp, modifier);
00242     }
00243 
00244   *cp++ = '/';
00245   stpcpy (cp, filename);
00246 
00247   /* Look in list of already loaded domains whether it is already
00248      available.  */
00249   lastp = l10nfile_list;
00250   for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
00251     if (retval->filename != NULL)
00252       {
00253        int compare = strcmp (retval->filename, abs_filename);
00254        if (compare == 0)
00255          /* We found it!  */
00256          break;
00257        if (compare < 0)
00258          {
00259            /* It's not in the list.  */
00260            retval = NULL;
00261            break;
00262          }
00263 
00264        lastp = &retval->next;
00265       }
00266 
00267   if (retval != NULL || do_allocate == 0)
00268     {
00269       free (abs_filename);
00270       return retval;
00271     }
00272 
00273   dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
00274 
00275   /* Allocate a new loaded_l10nfile.  */
00276   retval =
00277     (struct loaded_l10nfile *)
00278     malloc (sizeof (*retval)
00279            + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
00280               * sizeof (struct loaded_l10nfile *)));
00281   if (retval == NULL)
00282     {
00283       free (abs_filename);
00284       return NULL;
00285     }
00286 
00287   retval->filename = abs_filename;
00288 
00289   /* We set retval->data to NULL here; it is filled in later.
00290      Setting retval->decided to 1 here means that retval does not
00291      correspond to a real file (dirlist_count > 1) or is not worth
00292      looking up (if an unnormalized codeset was specified).  */
00293   retval->decided = (dirlist_count > 1
00294                    || ((mask & XPG_CODESET) != 0
00295                       && (mask & XPG_NORM_CODESET) != 0));
00296   retval->data = NULL;
00297 
00298   retval->next = *lastp;
00299   *lastp = retval;
00300 
00301   entries = 0;
00302   /* Recurse to fill the inheritance list of RETVAL.
00303      If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
00304      entry does not correspond to a real file; retval->filename contains
00305      colons.  In this case we loop across all elements of DIRLIST and
00306      across all bit patterns dominated by MASK.
00307      If the DIRLIST is a single directory or entirely redundant (i.e.
00308      DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
00309      MASK, excluding MASK itself.
00310      In either case, we loop down from MASK to 0.  This has the effect
00311      that the extra bits in the locale name are dropped in this order:
00312      first the modifier, then the territory, then the codeset, then the
00313      normalized_codeset.  */
00314   for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
00315     if ((cnt & ~mask) == 0
00316        && !((cnt & XPG_CODESET) != 0 && (cnt & XPG_NORM_CODESET) != 0))
00317       {
00318        if (dirlist_count > 1)
00319          {
00320            /* Iterate over all elements of the DIRLIST.  */
00321            char *dir = NULL;
00322 
00323            while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
00324                  != NULL)
00325              retval->successor[entries++]
00326               = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
00327                                   cnt, language, territory, codeset,
00328                                   normalized_codeset, modifier, filename,
00329                                   1);
00330          }
00331        else
00332          retval->successor[entries++]
00333            = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
00334                               cnt, language, territory, codeset,
00335                               normalized_codeset, modifier, filename, 1);
00336       }
00337   retval->successor[entries] = NULL;
00338 
00339   return retval;
00340 }
00341 
00342 /* Normalize codeset name.  There is no standard for the codeset
00343    names.  Normalization allows the user to use any of the common
00344    names.  The return value is dynamically allocated and has to be
00345    freed by the caller.  */
00346 const char *
00347 _nl_normalize_codeset (const char *codeset, size_t name_len)
00348 {
00349   size_t len = 0;
00350   int only_digit = 1;
00351   char *retval;
00352   char *wp;
00353   size_t cnt;
00354 
00355   for (cnt = 0; cnt < name_len; ++cnt)
00356     if (isalnum ((unsigned char) codeset[cnt]))
00357       {
00358        ++len;
00359 
00360        if (isalpha ((unsigned char) codeset[cnt]))
00361          only_digit = 0;
00362       }
00363 
00364   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
00365 
00366   if (retval != NULL)
00367     {
00368       if (only_digit)
00369        wp = stpcpy (retval, "iso");
00370       else
00371        wp = retval;
00372 
00373       for (cnt = 0; cnt < name_len; ++cnt)
00374        if (isalpha ((unsigned char) codeset[cnt]))
00375          *wp++ = tolower ((unsigned char) codeset[cnt]);
00376        else if (isdigit ((unsigned char) codeset[cnt]))
00377          *wp++ = codeset[cnt];
00378 
00379       *wp = '\0';
00380     }
00381 
00382   return (const char *) retval;
00383 }
00384 
00385 
00386 /* @@ begin of epilog @@ */
00387 
00388 /* We don't want libintl.a to depend on any other library.  So we
00389    avoid the non-standard function stpcpy.  In GNU C Library this
00390    function is available, though.  Also allow the symbol HAVE_STPCPY
00391    to be defined.  */
00392 #if !_LIBC && !HAVE_STPCPY
00393 static char *
00394 stpcpy (char *dest, const char *src)
00395 {
00396   while ((*dest++ = *src++) != '\0')
00397     /* Do nothing. */ ;
00398   return dest - 1;
00399 }
00400 #endif