Statistics
| Revision:

root / intl / l10nflist.c @ 1

History | View | Annotate | Download (11.7 KB)

1
/* Copyright (C) 1995-1999, 2000-2003 Free Software Foundation, Inc.
2
   Contributed by Ulrich Drepper <drepper@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., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
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 (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
88
argz_count__ (const char *argz, size_t len)
89
{
90
  size_t count = 0;
91
  while (len > 0)
92
    {
93
      size_t part_len = strlen (argz);
94
      argz += part_len + 1;
95
      len -= part_len + 1;
96
      count++;
97
    }
98
  return count;
99
}
100
# undef __argz_count
101
# define __argz_count(argz, len) argz_count__ (argz, len)
102
#else
103
# ifdef _LIBC
104
#  define __argz_count(argz, len) INTUSE(__argz_count) (argz, len)
105
# endif
106
#endif        /* !_LIBC && !HAVE___ARGZ_COUNT */
107

    
108
#if !defined _LIBC && !defined HAVE___ARGZ_STRINGIFY
109
/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
110
   except the last into the character SEP.  */
111
static void
112
argz_stringify__ (char *argz, size_t len, int sep)
113
{
114
  while (len > 0)
115
    {
116
      size_t part_len = strlen (argz);
117
      argz += part_len;
118
      len -= part_len + 1;
119
      if (len > 0)
120
        *argz++ = sep;
121
    }
122
}
123
# undef __argz_stringify
124
# define __argz_stringify(argz, len, sep) argz_stringify__ (argz, len, sep)
125
#else
126
# ifdef _LIBC
127
#  define __argz_stringify(argz, len, sep) \
128
  INTUSE(__argz_stringify) (argz, len, sep)
129
# endif
130
#endif        /* !_LIBC && !HAVE___ARGZ_STRINGIFY */
131

    
132
#if !defined _LIBC && !defined HAVE___ARGZ_NEXT
133
static char *
134
argz_next__ (char *argz, size_t argz_len, const char *entry)
135
{
136
  if (entry)
137
    {
138
      if (entry < argz + argz_len)
139
        entry = strchr (entry, '\0') + 1;
140

    
141
      return entry >= argz + argz_len ? NULL : (char *) entry;
142
    }
143
  else
144
    if (argz_len > 0)
145
      return argz;
146
    else
147
      return 0;
148
}
149
# undef __argz_next
150
# define __argz_next(argz, len, entry) argz_next__ (argz, len, entry)
151
#endif        /* !_LIBC && !HAVE___ARGZ_NEXT */
152

    
153

    
154
/* Return number of bits set in X.  */
155
static inline int
156
pop (int x)
157
{
158
  /* We assume that no more than 16 bits are used.  */
159
  x = ((x & ~0x5555) >> 1) + (x & 0x5555);
160
  x = ((x & ~0x3333) >> 2) + (x & 0x3333);
161
  x = ((x >> 4) + x) & 0x0f0f;
162
  x = ((x >> 8) + x) & 0xff;
163

    
164
  return x;
165
}
166

    
167
 
168
struct loaded_l10nfile *
169
_nl_make_l10nflist (struct loaded_l10nfile **l10nfile_list,
170
                    const char *dirlist, size_t dirlist_len,
171
                    int mask, const char *language, const char *territory,
172
                    const char *codeset, const char *normalized_codeset,
173
                    const char *modifier, const char *special,
174
                    const char *sponsor, const char *revision,
175
                    const char *filename, int do_allocate)
176
{
177
  char *abs_filename;
178
  struct loaded_l10nfile **lastp;
179
  struct loaded_l10nfile *retval;
180
  char *cp;
181
  size_t dirlist_count;
182
  size_t entries;
183
  int cnt;
184

    
185
  /* If LANGUAGE contains an absolute directory specification, we ignore
186
     DIRLIST.  */
187
  if (IS_ABSOLUTE_PATH (language))
188
    dirlist_len = 0;
189

    
190
  /* Allocate room for the full file name.  */
191
  abs_filename = (char *) malloc (dirlist_len
192
                                  + strlen (language)
193
                                  + ((mask & TERRITORY) != 0
194
                                     ? strlen (territory) + 1 : 0)
195
                                  + ((mask & XPG_CODESET) != 0
196
                                     ? strlen (codeset) + 1 : 0)
197
                                  + ((mask & XPG_NORM_CODESET) != 0
198
                                     ? strlen (normalized_codeset) + 1 : 0)
199
                                  + (((mask & XPG_MODIFIER) != 0
200
                                      || (mask & CEN_AUDIENCE) != 0)
201
                                     ? strlen (modifier) + 1 : 0)
202
                                  + ((mask & CEN_SPECIAL) != 0
203
                                     ? strlen (special) + 1 : 0)
204
                                  + (((mask & CEN_SPONSOR) != 0
205
                                      || (mask & CEN_REVISION) != 0)
206
                                     ? (1 + ((mask & CEN_SPONSOR) != 0
207
                                             ? strlen (sponsor) : 0)
208
                                        + ((mask & CEN_REVISION) != 0
209
                                           ? strlen (revision) + 1 : 0)) : 0)
210
                                  + 1 + strlen (filename) + 1);
211

    
212
  if (abs_filename == NULL)
213
    return NULL;
214

    
215
  /* Construct file name.  */
216
  cp = abs_filename;
217
  if (dirlist_len > 0)
218
    {
219
      memcpy (cp, dirlist, dirlist_len);
220
      __argz_stringify (cp, dirlist_len, PATH_SEPARATOR);
221
      cp += dirlist_len;
222
      cp[-1] = '/';
223
    }
224

    
225
  cp = stpcpy (cp, language);
226

    
227
  if ((mask & TERRITORY) != 0)
228
    {
229
      *cp++ = '_';
230
      cp = stpcpy (cp, territory);
231
    }
232
  if ((mask & XPG_CODESET) != 0)
233
    {
234
      *cp++ = '.';
235
      cp = stpcpy (cp, codeset);
236
    }
237
  if ((mask & XPG_NORM_CODESET) != 0)
238
    {
239
      *cp++ = '.';
240
      cp = stpcpy (cp, normalized_codeset);
241
    }
242
  if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
243
    {
244
      /* This component can be part of both syntaces but has different
245
         leading characters.  For CEN we use `+', else `@'.  */
246
      *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
247
      cp = stpcpy (cp, modifier);
248
    }
249
  if ((mask & CEN_SPECIAL) != 0)
250
    {
251
      *cp++ = '+';
252
      cp = stpcpy (cp, special);
253
    }
254
  if ((mask & (CEN_SPONSOR | CEN_REVISION)) != 0)
255
    {
256
      *cp++ = ',';
257
      if ((mask & CEN_SPONSOR) != 0)
258
        cp = stpcpy (cp, sponsor);
259
      if ((mask & CEN_REVISION) != 0)
260
        {
261
          *cp++ = '_';
262
          cp = stpcpy (cp, revision);
263
        }
264
    }
265

    
266
  *cp++ = '/';
267
  stpcpy (cp, filename);
268

    
269
  /* Look in list of already loaded domains whether it is already
270
     available.  */
271
  lastp = l10nfile_list;
272
  for (retval = *l10nfile_list; retval != NULL; retval = retval->next)
273
    if (retval->filename != NULL)
274
      {
275
        int compare = strcmp (retval->filename, abs_filename);
276
        if (compare == 0)
277
          /* We found it!  */
278
          break;
279
        if (compare < 0)
280
          {
281
            /* It's not in the list.  */
282
            retval = NULL;
283
            break;
284
          }
285

    
286
        lastp = &retval->next;
287
      }
288

    
289
  if (retval != NULL || do_allocate == 0)
290
    {
291
      free (abs_filename);
292
      return retval;
293
    }
294

    
295
  dirlist_count = (dirlist_len > 0 ? __argz_count (dirlist, dirlist_len) : 1);
296

    
297
  /* Allocate a new loaded_l10nfile.  */
298
  retval =
299
    (struct loaded_l10nfile *)
300
    malloc (sizeof (*retval)
301
            + (((dirlist_count << pop (mask)) + (dirlist_count > 1 ? 1 : 0))
302
               * sizeof (struct loaded_l10nfile *)));
303
  if (retval == NULL)
304
    return NULL;
305

    
306
  retval->filename = abs_filename;
307

    
308
  /* We set retval->data to NULL here; it is filled in later.
309
     Setting retval->decided to 1 here means that retval does not
310
     correspond to a real file (dirlist_count > 1) or is not worth
311
     looking up (if an unnormalized codeset was specified).  */
312
  retval->decided = (dirlist_count > 1
313
                     || ((mask & XPG_CODESET) != 0
314
                         && (mask & XPG_NORM_CODESET) != 0));
315
  retval->data = NULL;
316

    
317
  retval->next = *lastp;
318
  *lastp = retval;
319

    
320
  entries = 0;
321
  /* Recurse to fill the inheritance list of RETVAL.
322
     If the DIRLIST is a real list (i.e. DIRLIST_COUNT > 1), the RETVAL
323
     entry does not correspond to a real file; retval->filename contains
324
     colons.  In this case we loop across all elements of DIRLIST and
325
     across all bit patterns dominated by MASK.
326
     If the DIRLIST is a single directory or entirely redundant (i.e.
327
     DIRLIST_COUNT == 1), we loop across all bit patterns dominated by
328
     MASK, excluding MASK itself.
329
     In either case, we loop down from MASK to 0.  This has the effect
330
     that the extra bits in the locale name are dropped in this order:
331
     first the modifier, then the territory, then the codeset, then the
332
     normalized_codeset.  */
333
  for (cnt = dirlist_count > 1 ? mask : mask - 1; cnt >= 0; --cnt)
334
    if ((cnt & ~mask) == 0
335
        && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
336
        && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
337
      {
338
        if (dirlist_count > 1)
339
          {
340
            /* Iterate over all elements of the DIRLIST.  */
341
            char *dir = NULL;
342

    
343
            while ((dir = __argz_next ((char *) dirlist, dirlist_len, dir))
344
                   != NULL)
345
              retval->successor[entries++]
346
                = _nl_make_l10nflist (l10nfile_list, dir, strlen (dir) + 1,
347
                                      cnt, language, territory, codeset,
348
                                      normalized_codeset, modifier, special,
349
                                      sponsor, revision, filename, 1);
350
          }
351
        else
352
          retval->successor[entries++]
353
            = _nl_make_l10nflist (l10nfile_list, dirlist, dirlist_len,
354
                                  cnt, language, territory, codeset,
355
                                  normalized_codeset, modifier, special,
356
                                  sponsor, revision, filename, 1);
357
      }
358
  retval->successor[entries] = NULL;
359

    
360
  return retval;
361
}
362
 
363
/* Normalize codeset name.  There is no standard for the codeset
364
   names.  Normalization allows the user to use any of the common
365
   names.  The return value is dynamically allocated and has to be
366
   freed by the caller.  */
367
const char *
368
_nl_normalize_codeset (const char *codeset, size_t name_len)
369
{
370
  int len = 0;
371
  int only_digit = 1;
372
  char *retval;
373
  char *wp;
374
  size_t cnt;
375

    
376
  for (cnt = 0; cnt < name_len; ++cnt)
377
    if (isalnum ((unsigned char) codeset[cnt]))
378
      {
379
        ++len;
380

    
381
        if (isalpha ((unsigned char) codeset[cnt]))
382
          only_digit = 0;
383
      }
384

    
385
  retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
386

    
387
  if (retval != NULL)
388
    {
389
      if (only_digit)
390
        wp = stpcpy (retval, "iso");
391
      else
392
        wp = retval;
393

    
394
      for (cnt = 0; cnt < name_len; ++cnt)
395
        if (isalpha ((unsigned char) codeset[cnt]))
396
          *wp++ = tolower ((unsigned char) codeset[cnt]);
397
        else if (isdigit ((unsigned char) codeset[cnt]))
398
          *wp++ = codeset[cnt];
399

    
400
      *wp = '\0';
401
    }
402

    
403
  return (const char *) retval;
404
}
405

    
406

    
407
/* @@ begin of epilog @@ */
408

    
409
/* We don't want libintl.a to depend on any other library.  So we
410
   avoid the non-standard function stpcpy.  In GNU C Library this
411
   function is available, though.  Also allow the symbol HAVE_STPCPY
412
   to be defined.  */
413
#if !_LIBC && !HAVE_STPCPY
414
static char *
415
stpcpy (char *dest, const char *src)
416
{
417
  while ((*dest++ = *src++) != '\0')
418
    /* Do nothing. */ ;
419
  return dest - 1;
420
}
421
#endif