* locale/programs/xmalloc.c: Test _LIBC as well as STDC_HEADERS.
[platform/upstream/glibc.git] / intl / finddomain.c
1 /* finddomain.c -- handle list of needed message catalogs
2    Copyright (C) 1995, 1996 Free Software Foundation, Inc.
3    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
4
5 This file is part of the GNU C Library.  Its master source is NOT part of
6 the C library, however.  The master source lives in /gd/gnu/lib.
7
8 The GNU C Library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public License as
10 published by the Free Software Foundation; either version 2 of the
11 License, or (at your option) any later version.
12
13 The GNU C Library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public
19 License along with the GNU C Library; see the file COPYING.LIB.  If
20 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
21 Cambridge, MA 02139, USA.  */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #include <ctype.h>
28 #include <errno.h>
29 #include <stdio.h>
30 #include <sys/types.h>
31
32 #if defined STDC_HEADERS || defined _LIBC
33 # include <stdlib.h>
34 #else
35 # ifdef HAVE_MALLOC_H
36 #  include <malloc.h>
37 # else
38 void free ();
39 # endif
40 #endif
41
42 #if defined HAVE_STRING_H || defined _LIBC
43 # include <string.h>
44 #else
45 # include <strings.h>
46 #endif
47 #if !HAVE_STRCHR && !defined _LIBC
48 # ifndef strchr
49 #  define strchr index
50 # endif
51 #endif
52
53 #if defined HAVE_UNISTD_H || defined _LIBC
54 # include <unistd.h>
55 #endif
56
57 #include "gettext.h"
58 #include "gettextP.h"
59 #ifdef _LIBC
60 # include <libintl.h>
61 #else
62 # include "libgettext.h"
63 #endif
64
65 /* @@ end of prolog @@ */
66
67 #ifdef _LIBC
68 /* Rename the non ANSI C functions.  This is required by the standard
69    because some ANSI C functions will require linking with this object
70    file and the name space must not be polluted.  */
71 # define stpcpy(dest, src) __stpcpy(dest, src)
72 #endif
73
74 /* Encoding of locale name parts.  */
75 #define CEN_REVISION            1
76 #define CEN_SPONSOR             2
77 #define CEN_SPECIAL             4
78 #define XPG_NORM_CODESET        8
79 #define XPG_CODESET             16
80 #define TERRITORY               32
81 #define CEN_AUDIENCE            64
82 #define XPG_MODIFIER            128
83
84 #define CEN_SPECIFIC    (CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE)
85 #define XPG_SPECIFIC    (XPG_CODESET|XPG_NORM_CODESET|XPG_MODIFIER)
86
87
88 /* List of already loaded domains.  */
89 static struct loaded_domain *_nl_loaded_domains;
90
91 /* Prototypes for local functions.  */
92 static struct loaded_domain *make_entry_rec PARAMS ((const char *dirname,
93                                                      int mask,
94                                                      const char *language,
95                                                      const char *territory,
96                                                      const char *codeset,
97                                                      const char *normalized_codeset,
98                                                      const char *modifier,
99                                                      const char *special,
100                                                      const char *sponsor,
101                                                      const char *revision,
102                                                      const char *domainname,
103                                                      int do_allocate));
104
105 /* Normalize name of selected codeset.  */
106 static const char *normalize_codeset PARAMS ((const char *codeset));
107
108 /* Substitution for systems lacking this function in their C library.  */
109 #if !_LIBC && !HAVE_STPCPY
110 static char *stpcpy__ PARAMS ((char *dest, const char *src));
111 # define stpcpy(dest, src) stpcpy__ (dest, src)
112 #endif
113
114
115 /* Return a data structure describing the message catalog described by
116    the DOMAINNAME and CATEGORY parameters with respect to the currently
117    established bindings.  */
118 struct loaded_domain *
119 _nl_find_domain (dirname, locale, domainname)
120      const char *dirname;
121      char *locale;
122      const char *domainname;
123 {
124   enum { undecided, xpg, cen } syntax;
125   struct loaded_domain *retval;
126   const char *language;
127   const char *modifier = NULL;
128   const char *territory = NULL;
129   const char *codeset = NULL;
130   const char *normalized_codeset = NULL;
131   const char *special = NULL;
132   const char *sponsor = NULL;
133   const char *revision = NULL;
134   const char *alias_value = NULL;
135   char *cp;
136   int mask;
137
138   /* CATEGORYVALUE now possibly contains a colon separated list of
139      locales.  Each single locale can consist of up to four recognized
140      parts for the XPG syntax:
141
142                 language[_territory[.codeset]][@modifier]
143
144      and six parts for the CEN syntax:
145
146         language[_territory][+audience][+special][,sponsor][_revision]
147
148      Beside the first all of them are allowed to be missing.  If the
149      full specified locale is not found, the less specific one are
150      looked for.  The various part will be stripped of according to
151      the following order:
152                 (1) revision
153                 (2) sponsor
154                 (3) special
155                 (4) codeset
156                 (5) normalized codeset
157                 (6) territory
158                 (7) audience/modifier
159    */
160
161   /* If we have already tested for this locale entry there has to
162      be one data set in the list of loaded domains.  */
163   retval = make_entry_rec (dirname, 0, locale, NULL, NULL, NULL, NULL,
164                            NULL, NULL, NULL, domainname, 0);
165   if (retval != NULL)
166     {
167       /* We know something about this locale.  */
168       int cnt;
169
170       if (retval->decided == 0)
171         _nl_load_domain (retval); /* @@@ */
172
173       if (retval->data != NULL)
174         return retval;
175
176       for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
177         {
178           if (retval->successor[cnt]->decided == 0)
179             _nl_load_domain (retval->successor[cnt]);
180
181           if (retval->successor[cnt]->data != NULL)
182             break;
183         }
184
185       /* We really found some usable information.  */
186       return cnt >= 0 ? retval : NULL;
187       /* NOTREACHED */
188     }
189
190   /* See whether the locale value is an alias.  If yes its value
191      *overwrites* the alias name.  No test for the original value is
192      done.  */
193   alias_value = _nl_expand_alias (locale);
194   if (alias_value != NULL)
195     {
196       size_t len = strlen (alias_value) + 1;
197       locale = (char *) malloc (len);
198       if (locale == NULL)
199         return NULL;
200
201       memcpy (locale, alias_value, len);
202     }
203
204   /* Now we determine the single parts of the locale name.  First
205      look for the language.  Termination symbols are `_' and `@' if
206      we use XPG4 style, and `_', `+', and `,' if we use CEN syntax.  */
207   mask = 0;
208   syntax = undecided;
209   language = cp = locale;
210   while (cp[0] != '\0' && cp[0] != '_' && cp[0] != '@'
211          && cp[0] != '+' && cp[0] != ',')
212     ++cp;
213
214   if (language == cp)
215     /* This does not make sense: language has to be specified.  Use
216        this entry as it is without exploding.  Perhaps it is an alias.  */
217     cp = strchr (language, '\0');
218   else if (cp[0] == '_')
219     {
220       /* Next is the territory.  */
221       cp[0] = '\0';
222       territory = ++cp;
223
224       while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@'
225              && cp[0] != '+' && cp[0] != ',' && cp[0] != '_')
226         ++cp;
227
228       mask |= TERRITORY;
229
230       if (cp[0] == '.')
231         {
232           /* Next is the codeset.  */
233           syntax = xpg;
234           cp[0] = '\0';
235           codeset = ++cp;
236
237           while (cp[0] != '\0' && cp[0] != '@')
238             ++cp;
239
240           mask |= XPG_CODESET;
241
242           if (codeset != cp && codeset[0] != '\0')
243             {
244               normalized_codeset = normalize_codeset (codeset);
245               if (strcmp (codeset, normalized_codeset) == 0)
246                 free ((char *) normalized_codeset);
247               else
248                 mask |= XPG_NORM_CODESET;
249             }
250         }
251     }
252
253   if (cp[0] == '@' || (syntax != xpg && cp[0] == '+'))
254     {
255       /* Next is the modifier.  */
256       syntax = cp[0] == '@' ? xpg : cen;
257       cp[0] = '\0';
258       modifier = ++cp;
259
260       while (syntax == cen && cp[0] != '\0' && cp[0] != '+'
261              && cp[0] != ',' && cp[0] != '_')
262         ++cp;
263
264       mask |= XPG_MODIFIER | CEN_AUDIENCE;
265     }
266
267   if (syntax != xpg && (cp[0] == '+' || cp[0] == ',' || cp[0] == '_'))
268     {
269       syntax = cen;
270
271       if (cp[0] == '+')
272         {
273           /* Next is special application (CEN syntax).  */
274           cp[0] = '\0';
275           special = ++cp;
276
277           while (cp[0] != '\0' && cp[0] != ',' && cp[0] != '_')
278             ++cp;
279
280           mask |= CEN_SPECIAL;
281         }
282
283       if (cp[0] == ',')
284         {
285           /* Next is sponsor (CEN syntax).  */
286           cp[0] = '\0';
287           sponsor = ++cp;
288
289           while (cp[0] != '\0' && cp[0] != '_')
290             ++cp;
291
292           mask |= CEN_SPONSOR;
293         }
294
295       if (cp[0] == '_')
296         {
297           /* Next is revision (CEN syntax).  */
298           cp[0] = '\0';
299           revision = ++cp;
300
301           mask |= CEN_REVISION;
302         }
303     }
304
305   /* For CEN sytnax values it might be important to have the
306      separator character in the file name, not for XPG syntax.  */
307   if (syntax == xpg)
308     {
309       if (territory != NULL && territory[0] == '\0')
310         mask &= ~TERRITORY;
311
312       if (codeset != NULL && codeset[0] == '\0')
313         mask &= ~XPG_CODESET;
314
315       if (modifier != NULL && modifier[0] == '\0')
316         mask &= ~XPG_MODIFIER;
317     }
318
319   /* Create all possible locale entries which might be interested in
320      generalzation.  */
321   retval = make_entry_rec (dirname, mask, language, territory, codeset,
322                            normalized_codeset, modifier, special, sponsor,
323                            revision, domainname, 1);
324   if (retval == NULL)
325     /* This means we are out of core.  */
326     return NULL;
327
328   if (retval->decided == 0)
329     _nl_load_domain (retval);
330   if (retval->data == NULL)
331     {
332       int cnt;
333       for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
334         {
335           if (retval->successor[cnt]->decided == 0)
336             _nl_load_domain (retval->successor[cnt]);
337           if (retval->successor[cnt]->data != NULL)
338             break;
339
340           /* Signal that locale is not available.  */
341           retval->successor[cnt] = NULL;
342         }
343       if (retval->successor[cnt] == NULL)
344         retval = NULL;
345     }
346
347   /* The room for an alias was dynamically allocated.  Free it now.  */
348   if (alias_value != NULL)
349     free (locale);
350
351   return retval;
352 }
353
354
355 static struct loaded_domain *
356 make_entry_rec (dirname, mask, language, territory, codeset,
357                 normalized_codeset, modifier, special, sponsor, revision,
358                 domain, do_allocate)
359      const char *dirname;
360      int mask;
361      const char *language;
362      const char *territory;
363      const char *codeset;
364      const char *normalized_codeset;
365      const char *modifier;
366      const char *special;
367      const char *sponsor;
368      const char *revision;
369      const char *domain;
370      int do_allocate;
371 {
372   char *filename = NULL;
373   struct loaded_domain *last = NULL;
374   struct loaded_domain *retval;
375   char *cp;
376   size_t entries;
377   int cnt;
378
379
380   /* Process the current entry described by the MASK only when it is
381      valid.  Because the mask can have in the first call bits from
382      both syntaces set this is necessary to prevent constructing
383      illegal local names.  */
384   /* FIXME: Rewrite because test is necessary only in first round.  */
385   if ((mask & CEN_SPECIFIC) == 0 || (mask & XPG_SPECIFIC) == 0
386       || ((mask & XPG_CODESET) != 0 && (mask & XPG_NORM_CODESET) != 0))
387     {
388       /* Allocate room for the full file name.  */
389       filename = (char *) malloc (strlen (dirname) + 1
390                                   + strlen (language)
391                                   + ((mask & TERRITORY) != 0
392                                      ? strlen (territory) + 1 : 0)
393                                   + ((mask & XPG_CODESET) != 0
394                                      ? strlen (codeset) + 1 : 0)
395                                   + ((mask & XPG_NORM_CODESET) != 0
396                                      ? strlen (normalized_codeset) + 1 : 0)
397                                   + ((mask & XPG_MODIFIER) != 0 ?
398                                      strlen (modifier) + 1 : 0)
399                                   + ((mask & CEN_SPECIAL) != 0
400                                      ? strlen (special) + 1 : 0)
401                                   + ((mask & CEN_SPONSOR) != 0
402                                      ? strlen (sponsor) + 1 : 0)
403                                   + ((mask & CEN_REVISION) != 0
404                                      ? strlen (revision) + 1 : 0) + 1
405                                   + strlen (domain) + 1);
406
407       if (filename == NULL)
408         return NULL;
409
410       retval = NULL;
411       last = NULL;
412
413       /* Construct file name.  */
414       cp = stpcpy (filename, dirname);
415       *cp++ = '/';
416       cp = stpcpy (cp, language);
417
418       if ((mask & TERRITORY) != 0)
419         {
420           *cp++ = '_';
421           cp = stpcpy (cp, territory);
422         }
423       if ((mask & XPG_CODESET) != 0)
424         {
425           *cp++ = '.';
426           cp = stpcpy (cp, codeset);
427         }
428       if ((mask & XPG_NORM_CODESET) != 0)
429         {
430           *cp++ = '.';
431           cp = stpcpy (cp, normalized_codeset);
432         }
433       if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
434         {
435           /* This component can be part of both syntaces but has different
436              leading characters.  For CEN we use `+', else `@'.  */
437           *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@';
438           cp = stpcpy (cp, modifier);
439         }
440       if ((mask & CEN_SPECIAL) != 0)
441         {
442           *cp++ = '+';
443           cp = stpcpy (cp, special);
444         }
445       if ((mask & CEN_SPONSOR) != 0)
446         {
447           *cp++ = ',';
448           cp = stpcpy (cp, sponsor);
449         }
450       if ((mask & CEN_REVISION) != 0)
451         {
452           *cp++ = '_';
453           cp = stpcpy (cp, revision);
454         }
455
456       *cp++ = '/';
457       stpcpy (cp, domain);
458
459       /* Look in list of already loaded domains whether it is already
460          available.  */
461       last = NULL;
462       for (retval = _nl_loaded_domains; retval != NULL; retval = retval->next)
463         if (retval->filename != NULL)
464           {
465             int compare = strcmp (retval->filename, filename);
466             if (compare == 0)
467               /* We found it!  */
468               break;
469             if (compare < 0)
470               {
471                 /* It's not in the list.  */
472                 retval = NULL;
473                 break;
474               }
475
476             last = retval;
477           }
478
479       if (retval != NULL || do_allocate == 0)
480         {
481           free (filename);
482           return retval;
483         }
484     }
485
486   retval = (struct loaded_domain *) malloc (sizeof (*retval));
487   if (retval == NULL)
488     return NULL;
489
490   retval->filename = filename;
491   retval->decided = 0;
492
493   if (last == NULL)
494     {
495       retval->next = _nl_loaded_domains;
496       _nl_loaded_domains = retval;
497     }
498   else
499     {
500       retval->next = last->next;
501       last->next = retval;
502     }
503
504   entries = 0;
505   for (cnt = 254; cnt >= 0; --cnt)
506     if (cnt < mask && (cnt & ~mask) == 0
507         && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
508         && ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
509       retval->successor[entries++] = make_entry_rec (dirname, cnt,
510                                                      language, territory,
511                                                      codeset,
512                                                      normalized_codeset,
513                                                      modifier, special,
514                                                      sponsor, revision,
515                                                      domain, 1);
516   retval->successor[entries] = NULL;
517
518   return retval;
519 }
520
521
522 static const char *
523 normalize_codeset (codeset)
524      const char *codeset;
525 {
526   int len = 0;
527   int only_digit = 1;
528   const char *cp;
529   char *retval;
530   char *wp;
531
532   for (cp = codeset; cp[0] != '\0'; ++cp)
533     if (isalnum (cp[0]))
534       {
535         ++len;
536
537         if (isalpha (cp[0]))
538           only_digit = 0;
539       }
540
541   retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
542
543   if (retval != NULL)
544     {
545       if (only_digit)
546         wp = stpcpy (retval, "ISO");
547       else
548         wp = retval;
549
550       for (cp = codeset; cp[0] != '\0'; ++cp)
551         if (isalpha (cp[0]))
552           *wp++ = toupper (cp[0]);
553         else if (isdigit (cp[0]))
554           *wp++ = cp[0];
555
556       *wp = '\0';
557     }
558
559   return (const char *) retval;
560 }
561
562
563 /* @@ begin of epilog @@ */
564
565 /* We don't want libintl.a to depend on any other library.  So we
566    avoid the non-standard function stpcpy.  In GNU C Library this
567    function is available, though.  Also allow the symbol HAVE_STPCPY
568    to be defined.  */
569 #if !_LIBC && !HAVE_STPCPY
570 static char *
571 stpcpy__ (dest, src)
572      char *dest;
573      const char *src;
574 {
575   while ((*dest++ = *src++) != '\0')
576     /* Do nothing. */ ;
577   return dest - 1;
578 }
579 #endif