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.
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.
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.
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.
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. */
30 #include <sys/types.h>
32 #if defined STDC_HEADERS || defined _LIBC
42 #if defined HAVE_STRING_H || defined _LIBC
47 #if !HAVE_STRCHR && !defined _LIBC
53 #if defined HAVE_UNISTD_H || defined _LIBC
62 # include "libgettext.h"
65 /* @@ end of prolog @@ */
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)
74 /* Encoding of locale name parts. */
75 #define CEN_REVISION 1
78 #define XPG_NORM_CODESET 8
79 #define XPG_CODESET 16
81 #define CEN_AUDIENCE 64
82 #define XPG_MODIFIER 128
84 #define CEN_SPECIFIC (CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE)
85 #define XPG_SPECIFIC (XPG_CODESET|XPG_NORM_CODESET|XPG_MODIFIER)
88 /* List of already loaded domains. */
89 static struct loaded_domain *_nl_loaded_domains;
91 /* Prototypes for local functions. */
92 static struct loaded_domain *make_entry_rec PARAMS ((const char *dirname,
95 const char *territory,
97 const char *normalized_codeset,
101 const char *revision,
102 const char *domainname,
105 /* Normalize name of selected codeset. */
106 static const char *normalize_codeset PARAMS ((const char *codeset));
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)
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)
122 const char *domainname;
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;
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:
142 language[_territory[.codeset]][@modifier]
144 and six parts for the CEN syntax:
146 language[_territory][+audience][+special][,sponsor][_revision]
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
156 (5) normalized codeset
158 (7) audience/modifier
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);
167 /* We know something about this locale. */
170 if (retval->decided == 0)
171 _nl_load_domain (retval); /* @@@ */
173 if (retval->data != NULL)
176 for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
178 if (retval->successor[cnt]->decided == 0)
179 _nl_load_domain (retval->successor[cnt]);
181 if (retval->successor[cnt]->data != NULL)
185 /* We really found some usable information. */
186 return cnt >= 0 ? retval : NULL;
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
193 alias_value = _nl_expand_alias (locale);
194 if (alias_value != NULL)
196 size_t len = strlen (alias_value) + 1;
197 locale = (char *) malloc (len);
201 memcpy (locale, alias_value, len);
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. */
209 language = cp = locale;
210 while (cp[0] != '\0' && cp[0] != '_' && cp[0] != '@'
211 && cp[0] != '+' && cp[0] != ',')
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] == '_')
220 /* Next is the territory. */
224 while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@'
225 && cp[0] != '+' && cp[0] != ',' && cp[0] != '_')
232 /* Next is the codeset. */
237 while (cp[0] != '\0' && cp[0] != '@')
242 if (codeset != cp && codeset[0] != '\0')
244 normalized_codeset = normalize_codeset (codeset);
245 if (strcmp (codeset, normalized_codeset) == 0)
246 free ((char *) normalized_codeset);
248 mask |= XPG_NORM_CODESET;
253 if (cp[0] == '@' || (syntax != xpg && cp[0] == '+'))
255 /* Next is the modifier. */
256 syntax = cp[0] == '@' ? xpg : cen;
260 while (syntax == cen && cp[0] != '\0' && cp[0] != '+'
261 && cp[0] != ',' && cp[0] != '_')
264 mask |= XPG_MODIFIER | CEN_AUDIENCE;
267 if (syntax != xpg && (cp[0] == '+' || cp[0] == ',' || cp[0] == '_'))
273 /* Next is special application (CEN syntax). */
277 while (cp[0] != '\0' && cp[0] != ',' && cp[0] != '_')
285 /* Next is sponsor (CEN syntax). */
289 while (cp[0] != '\0' && cp[0] != '_')
297 /* Next is revision (CEN syntax). */
301 mask |= CEN_REVISION;
305 /* For CEN sytnax values it might be important to have the
306 separator character in the file name, not for XPG syntax. */
309 if (territory != NULL && territory[0] == '\0')
312 if (codeset != NULL && codeset[0] == '\0')
313 mask &= ~XPG_CODESET;
315 if (modifier != NULL && modifier[0] == '\0')
316 mask &= ~XPG_MODIFIER;
319 /* Create all possible locale entries which might be interested in
321 retval = make_entry_rec (dirname, mask, language, territory, codeset,
322 normalized_codeset, modifier, special, sponsor,
323 revision, domainname, 1);
325 /* This means we are out of core. */
328 if (retval->decided == 0)
329 _nl_load_domain (retval);
330 if (retval->data == NULL)
333 for (cnt = 0; retval->successor[cnt] != NULL; ++cnt)
335 if (retval->successor[cnt]->decided == 0)
336 _nl_load_domain (retval->successor[cnt]);
337 if (retval->successor[cnt]->data != NULL)
340 /* Signal that locale is not available. */
341 retval->successor[cnt] = NULL;
343 if (retval->successor[cnt] == NULL)
347 /* The room for an alias was dynamically allocated. Free it now. */
348 if (alias_value != NULL)
355 static struct loaded_domain *
356 make_entry_rec (dirname, mask, language, territory, codeset,
357 normalized_codeset, modifier, special, sponsor, revision,
361 const char *language;
362 const char *territory;
364 const char *normalized_codeset;
365 const char *modifier;
368 const char *revision;
372 char *filename = NULL;
373 struct loaded_domain *last = NULL;
374 struct loaded_domain *retval;
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))
388 /* Allocate room for the full file name. */
389 filename = (char *) malloc (strlen (dirname) + 1
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);
407 if (filename == NULL)
413 /* Construct file name. */
414 cp = stpcpy (filename, dirname);
416 cp = stpcpy (cp, language);
418 if ((mask & TERRITORY) != 0)
421 cp = stpcpy (cp, territory);
423 if ((mask & XPG_CODESET) != 0)
426 cp = stpcpy (cp, codeset);
428 if ((mask & XPG_NORM_CODESET) != 0)
431 cp = stpcpy (cp, normalized_codeset);
433 if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0)
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);
440 if ((mask & CEN_SPECIAL) != 0)
443 cp = stpcpy (cp, special);
445 if ((mask & CEN_SPONSOR) != 0)
448 cp = stpcpy (cp, sponsor);
450 if ((mask & CEN_REVISION) != 0)
453 cp = stpcpy (cp, revision);
459 /* Look in list of already loaded domains whether it is already
462 for (retval = _nl_loaded_domains; retval != NULL; retval = retval->next)
463 if (retval->filename != NULL)
465 int compare = strcmp (retval->filename, filename);
471 /* It's not in the list. */
479 if (retval != NULL || do_allocate == 0)
486 retval = (struct loaded_domain *) malloc (sizeof (*retval));
490 retval->filename = filename;
495 retval->next = _nl_loaded_domains;
496 _nl_loaded_domains = retval;
500 retval->next = last->next;
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,
516 retval->successor[entries] = NULL;
523 normalize_codeset (codeset)
532 for (cp = codeset; cp[0] != '\0'; ++cp)
541 retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
546 wp = stpcpy (retval, "ISO");
550 for (cp = codeset; cp[0] != '\0'; ++cp)
552 *wp++ = toupper (cp[0]);
553 else if (isdigit (cp[0]))
559 return (const char *) retval;
563 /* @@ begin of epilog @@ */
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
569 #if !_LIBC && !HAVE_STPCPY
575 while ((*dest++ = *src++) != '\0')