diff options
Diffstat (limited to 'intl/finddomain.c')
-rw-r--r-- | intl/finddomain.c | 476 |
1 files changed, 476 insertions, 0 deletions
diff --git a/intl/finddomain.c b/intl/finddomain.c new file mode 100644 index 0000000000..19cf2d7581 --- /dev/null +++ b/intl/finddomain.c @@ -0,0 +1,476 @@ +/* finddomain.c -- handle list of needed message catalogs + Copyright (C) 1995 Software Foundation, Inc. + Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <errno.h> +#include <stdio.h> +#include <sys/types.h> + +#if defined STDC_HEADERS || defined _LIBC +# include <stdlib.h> +#else +# ifdef HAVE_MALLOC_H +# include <malloc.h> +# else +void free (); +# endif +#endif + +#if defined HAVE_STRING_H || defined _LIBC +# include <string.h> +#else +# include <strings.h> +#endif +#if !HAVE_STRCHR && !defined _LIBC +# ifndef strchr +# define strchr index +# endif +#endif + +#if defined HAVE_UNISTD_H || defined _LIBC +# include <unistd.h> +#endif + +#include "gettext.h" +#include "gettextP.h" +#ifdef _LIBC +# include <libintl.h> +#else +# include "libgettext.h" +#endif + +/* @@ end of prolog @@ */ + +#ifdef _LIBC +/* Rename the non ANSI C functions. This is required by the standard + because some ANSI C functions will require linking with this object + file and the name space must not be polluted. */ +# define stpcpy __stpcpy +#endif + +/* Encoding of locale name parts. */ +#define CEN_REVISION 1 +#define CEN_SPONSOR 2 +#define CEN_SPECIAL 4 +#define XPG_CODESET 8 +#define TERRITORY 16 +#define CEN_AUDIENCE 32 +#define XPG_MODIFIER 64 + +#define CEN_SPECIFIC (CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE) +#define XPG_SPECIFIC (XPG_CODESET|XPG_MODIFIER) + + +/* List of already loaded domains. */ +static struct loaded_domain *_nl_loaded_domains; + +/* Prototypes for local functions. */ +static struct loaded_domain *make_entry_rec __P ((const char *dirname, + int mask, + const char *language, + const char *territory, + const char *codeset, + const char *modifier, + const char *special, + const char *sponsor, + const char *revision, + const char *domainname, + int do_allocate)); + + +/* Return a data structure describing the message catalog described by + the DOMAINNAME and CATEGORY parameters with respect to the currently + established bindings. */ +struct loaded_domain * +_nl_find_domain (dirname, locale, domainname) + const char *dirname; + char *locale; + const char *domainname; +{ + enum { undecided, xpg, cen } syntax; + struct loaded_domain *retval; + const char *language; + const char *modifier = NULL; + const char *territory = NULL; + const char *codeset = NULL; + const char *special = NULL; + const char *sponsor = NULL; + const char *revision = NULL; + const char *alias_value = NULL; + char *cp; + int mask; + + /* CATEGORYVALUE now possibly contains a colon separated list of + locales. Each single locale can consist of up to four recognized + parts for the XPG syntax: + + language[_territory[.codeset]][@modifier] + + and six parts for the CEN syntax: + + language[_territory][+audience][+special][,sponsor][_revision] + + Beside the first all of them are allowed to be missing. If the + full specified locale is not found, the less specific one are + looked for. The various part will be stripped of according to + the following order: + (1) revision + (2) sponsor + (3) special + (4) codeset + (5) territory + (6) audience/modifier + */ + + /* If we have already tested for this locale entry there has to + be one data set in the list of loaded domains. */ + retval = make_entry_rec (dirname, 0, locale, NULL, NULL, NULL, + NULL, NULL, NULL, domainname, 0); + if (retval != NULL) + { + /* We know something about this locale. */ + int cnt; + + if (retval->decided == 0) + _nl_load_domain (retval); /* @@@ */ + + if (retval->data != NULL) + return retval; + + for (cnt = 6; cnt >= 0; --cnt) + { + if (retval->successor[cnt] == 0) + _nl_load_domain (retval->successor[cnt]); + + if (retval->successor[cnt]->data != NULL) + break; + } + + /* We really found some usable information. */ + return cnt >= 0 ? retval : NULL; + /* NOTREACHED */ + } + + /* See whether the locale value is an alias. If yes its value + *overwrites* the alias name. No test for the original value is + done. */ + alias_value = _nl_expand_alias (locale); + if (alias_value != NULL) + { + size_t len = strlen (alias_value) + 1; + locale = (char *) malloc (len); + if (locale == NULL) + return NULL; + + memcpy (locale, alias_value, len); + } + + /* Now we determine the single parts of the locale name. First + look for the language. Termination symbols are `_' and `@' if + we use XPG4 style, and `_', `+', and `,' if we use CEN syntax. */ + mask = 0; + syntax = undecided; + language = cp = locale; + while (cp[0] != '\0' && cp[0] != '_' && cp[0] != '@' + && cp[0] != '+' && cp[0] != ',') + ++cp; + + if (language == cp) + /* This does not make sense: language has to be specified. Use + this entry as it is without exploding. Perhaps it is an alias. */ + cp = strchr (language, '\0'); + else if (cp[0] == '_') + { + /* Next is the territory. */ + cp[0] = '\0'; + territory = ++cp; + + while (cp[0] != '\0' && cp[0] != '.' && cp[0] != '@' + && cp[0] != '+' && cp[0] != ',' && cp[0] != '_') + ++cp; + + mask |= TERRITORY; + + if (cp[0] == '.') + { + /* Next is the codeset. */ + syntax = xpg; + cp[0] = '\0'; + codeset = ++cp; + + while (cp[0] != '\0' && cp[0] != '@') + ++cp; + + mask |= XPG_CODESET; + } + } + + if (cp[0] == '@' || (syntax != xpg && cp[0] == '+')) + { + /* Next is the modifier. */ + syntax = cp[0] == '@' ? xpg : cen; + cp[0] = '\0'; + modifier = ++cp; + + while (syntax == cen && cp[0] != '\0' && cp[0] != '+' + && cp[0] != ',' && cp[0] != '_') + ++cp; + + mask |= XPG_MODIFIER | CEN_AUDIENCE; + } + + if (syntax != xpg && (cp[0] == '+' || cp[0] == ',' || cp[0] == '_')) + { + syntax = cen; + + if (cp[0] == '+') + { + /* Next is special application (CEN syntax). */ + cp[0] = '\0'; + special = ++cp; + + while (cp[0] != '\0' && cp[0] != ',' && cp[0] != '_') + ++cp; + + mask |= CEN_SPECIAL; + } + + if (cp[0] == ',') + { + /* Next is sponsor (CEN syntax). */ + cp[0] = '\0'; + sponsor = ++cp; + + while (cp[0] != '\0' && cp[0] != '_') + ++cp; + + mask |= CEN_SPONSOR; + } + + if (cp[0] == '_') + { + /* Next is revision (CEN syntax). */ + cp[0] = '\0'; + revision = ++cp; + + mask |= CEN_REVISION; + } + } + + /* For CEN sytnax values it might be important to have the + separator character in the file name, not for XPG syntax. */ + if (syntax == xpg) + { + if (territory[0] == '\0') + mask &= ~TERRITORY; + + if (codeset[0] == '\0') + mask &= ~XPG_CODESET; + + if (modifier[0] == '\0') + mask &= ~XPG_MODIFIER; + } + + /* Create all possible locale entries which might be interested in + generalzation. */ + retval = make_entry_rec (dirname, mask, language, territory, codeset, + modifier, special, sponsor, revision, + domainname, 1); + if (retval == NULL) + /* This means we are out of core. */ + return NULL; + + if (retval->decided == 0) + _nl_load_domain (retval); + if (retval->data == NULL) + { + int cnt; + for (cnt = 0; retval->successor[cnt] != NULL; ++cnt) + { + if (retval->successor[cnt]->decided == 0) + _nl_load_domain (retval->successor[cnt]); + if (retval->successor[cnt]->data != NULL) + break; + + /* Signal that locale is not available. */ + retval->successor[cnt] = NULL; + } + if (retval->successor[cnt] == NULL) + retval = NULL; + } + + /* The room for an alias was dynamically allocated. Free it now. */ + if (alias_value != NULL) + free (locale); + + return retval; +} + + +static struct loaded_domain * +make_entry_rec (dirname, mask, language, territory, codeset, modifier, + special, sponsor, revision, domain, do_allocate) + const char *dirname; + int mask; + const char *language; + const char *territory; + const char *codeset; + const char *modifier; + const char *special; + const char *sponsor; + const char *revision; + const char *domain; + int do_allocate; +{ + struct loaded_domain *retval, *last; + char *filename, *cp; + size_t entries; + int cnt; + + /* Allocate room for the full file name. */ + filename = (char *) malloc (strlen (dirname) + 1 + + strlen (language) + + ((mask & TERRITORY) != 0 + ? strlen (territory) : 0) + + ((mask & XPG_CODESET) != 0 + ? strlen (codeset) : 0) + + ((mask & XPG_MODIFIER) != 0 ? + strlen (modifier) : 0) + + ((mask & CEN_SPECIAL) != 0 + ? strlen (special) : 0) + + ((mask & CEN_SPONSOR) != 0 + ? strlen (sponsor) : 0) + + ((mask & CEN_REVISION) != 0 + ? strlen (revision) : 0) + 1 + + strlen (domain) + 1); + + if (filename == NULL) + return NULL; + + retval = NULL; + last = NULL; + + /* We don't want libintl.a to depend on any other library. So we + avoid the non-standard function stpcpy. In GNU C Library this + function is available, though. Also allow the symbol HAVE_STPCPY + to be defined. */ +#if !defined _LIBC && !defined HAVE_STPCPY +# define stpcpy(p, s) \ + (strcpy (p, s), strchr (p, '\0')) +#endif + + /* Construct file name. */ + cp = stpcpy (filename, dirname); + *cp++ = '/'; + cp = stpcpy (cp, language); + + if ((mask & TERRITORY) != 0) + { + *cp++ = '_'; + cp = stpcpy (cp, territory); + } + if ((mask & XPG_CODESET) != 0) + { + *cp++ = '.'; + cp = stpcpy (cp, codeset); + } + if ((mask & (XPG_MODIFIER | CEN_AUDIENCE)) != 0) + { + /* This component can be part of both syntaces but has different + leading characters. For CEN we use `+', else `@'. */ + *cp++ = (mask & CEN_AUDIENCE) != 0 ? '+' : '@'; + cp = stpcpy (cp, modifier); + } + if ((mask & CEN_SPECIAL) != 0) + { + *cp++ = '+'; + cp = stpcpy (cp, special); + } + if ((mask & CEN_SPONSOR) != 0) + { + *cp++ = ','; + cp = stpcpy (cp, sponsor); + } + if ((mask & CEN_REVISION) != 0) + { + *cp++ = '_'; + cp = stpcpy (cp, revision); + } + + *cp++ = '/'; + stpcpy (cp, domain); + + /* Look in list of already loaded domains whether it is already + available. */ + last = NULL; + for (retval = _nl_loaded_domains; retval != NULL; retval = retval->next) + { + int compare = strcmp (retval->filename, filename); + if (compare == 0) + /* We found it! */ + break; + if (compare < 0) + { + /* It's not in the list. */ + retval = NULL; + break; + } + + last = retval; + } + + if (retval != NULL || do_allocate == 0) + { + free (filename); + return retval; + } + + retval = (struct loaded_domain *) malloc (sizeof (*retval)); + if (retval == NULL) + return NULL; + + retval->filename = filename; + retval->decided = 0; + + if (last == NULL) + { + retval->next = _nl_loaded_domains; + _nl_loaded_domains = retval; + } + else + { + retval->next = last->next; + last->next = retval; + } + + entries = 0; + for (cnt = 126; cnt >= 0; --cnt) + if (cnt < mask && (cnt & ~mask) == 0 + && ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)) + retval->successor[entries++] = make_entry_rec (dirname, cnt, + language, territory, + codeset, modifier, + special, sponsor, + revision, domain, 1); + retval->successor[entries] = NULL; + + return retval; +} |