mirror of
https://sourceware.org/git/glibc.git
synced 2024-12-25 12:11:10 +00:00
0393dfd6c2
* locale/programs/ld-collate.c (collate_finish): Use error_at_line instead of error_with_loc. * locale/weight.h: Use u_int32_t instead of u32_t. * string/strxfrm.c: Likewise. * string/strxfrm.c: Find weight.h in ../locale; don't #include "localeinfo.h". * string/strcoll.c: Likewise. * locale/programs/simple-hash.c, locale/programs/simple-hash.h, locale/programs/xmalloc.c, locale/programs/xstrdup.c: Helper functions for locale related programs. locale/programs/charmap.c, locale/programs/charset.c, locale/programs/charset.h, locale/programs/config.h, locale/programs/ctypedump.c, locale/programs/ld-collate.c, locale/programs/ld-ctype.c, locale/programs/ld-messages.c, locale/programs/ld-monetary.c, locale/programs/ld-numeric.c, locale/programs/ld-time.c, locale/programs/linereader.c, locale/programs/linereader.h, locale/programs/locale.c, locale/programs/localedef.c, locale/programs/locales.h, locale/programs/locfile-kw.gperf, locale/programs/locfile-kw.h, locale/programs/locfile-token.h, locale/programs/locfile.c, locale/programs/locfile.h, locale/programs/stringtrans.c, locale/programs/stringtrans.h: Implementation of locale related programs.
580 lines
14 KiB
C
580 lines
14 KiB
C
/* finddomain.c -- handle list of needed message catalogs
|
|
Copyright (C) 1995, 1996 Free Software Foundation, Inc.
|
|
Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
|
|
|
|
This file is part of the GNU C Library. Its master source is NOT part of
|
|
the C library, however. The master source lives in /gd/gnu/lib.
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Library General Public License as
|
|
published by the Free Software Foundation; either version 2 of the
|
|
License, or (at your option) any later version.
|
|
|
|
The GNU C Library 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
|
|
Library General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
License along with the GNU C Library; see the file COPYING.LIB. If
|
|
not, write to the Free Software Foundation, Inc., 675 Mass Ave,
|
|
Cambridge, MA 02139, USA. */
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <config.h>
|
|
#endif
|
|
|
|
#include <ctype.h>
|
|
#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(dest, src) __stpcpy(dest, src)
|
|
#endif
|
|
|
|
/* Encoding of locale name parts. */
|
|
#define CEN_REVISION 1
|
|
#define CEN_SPONSOR 2
|
|
#define CEN_SPECIAL 4
|
|
#define XPG_NORM_CODESET 8
|
|
#define XPG_CODESET 16
|
|
#define TERRITORY 32
|
|
#define CEN_AUDIENCE 64
|
|
#define XPG_MODIFIER 128
|
|
|
|
#define CEN_SPECIFIC (CEN_REVISION|CEN_SPONSOR|CEN_SPECIAL|CEN_AUDIENCE)
|
|
#define XPG_SPECIFIC (XPG_CODESET|XPG_NORM_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 PARAMS ((const char *dirname,
|
|
int mask,
|
|
const char *language,
|
|
const char *territory,
|
|
const char *codeset,
|
|
const char *normalized_codeset,
|
|
const char *modifier,
|
|
const char *special,
|
|
const char *sponsor,
|
|
const char *revision,
|
|
const char *domainname,
|
|
int do_allocate));
|
|
|
|
/* Normalize name of selected codeset. */
|
|
static const char *normalize_codeset PARAMS ((const char *codeset));
|
|
|
|
/* Substitution for systems lacking this function in their C library. */
|
|
#if !_LIBC && !HAVE_STPCPY
|
|
static char *stpcpy__ PARAMS ((char *dest, const char *src));
|
|
# define stpcpy(dest, src) stpcpy__ (dest, src)
|
|
#endif
|
|
|
|
|
|
/* 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 *normalized_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) normalized codeset
|
|
(6) territory
|
|
(7) 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, 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 = 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;
|
|
}
|
|
|
|
/* 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 (codeset != cp && codeset[0] != '\0')
|
|
{
|
|
normalized_codeset = normalize_codeset (codeset);
|
|
if (strcmp (codeset, normalized_codeset) == 0)
|
|
free ((char *) normalized_codeset);
|
|
else
|
|
mask |= XPG_NORM_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 != NULL && territory[0] == '\0')
|
|
mask &= ~TERRITORY;
|
|
|
|
if (codeset != NULL && codeset[0] == '\0')
|
|
mask &= ~XPG_CODESET;
|
|
|
|
if (modifier != NULL && 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,
|
|
normalized_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,
|
|
normalized_codeset, modifier, special, sponsor, revision,
|
|
domain, do_allocate)
|
|
const char *dirname;
|
|
int mask;
|
|
const char *language;
|
|
const char *territory;
|
|
const char *codeset;
|
|
const char *normalized_codeset;
|
|
const char *modifier;
|
|
const char *special;
|
|
const char *sponsor;
|
|
const char *revision;
|
|
const char *domain;
|
|
int do_allocate;
|
|
{
|
|
char *filename = NULL;
|
|
struct loaded_domain *last = NULL;
|
|
struct loaded_domain *retval;
|
|
char *cp;
|
|
size_t entries;
|
|
int cnt;
|
|
|
|
|
|
/* Process the current entry described by the MASK only when it is
|
|
valid. Because the mask can have in the first call bits from
|
|
both syntaces set this is necessary to prevent constructing
|
|
illegal local names. */
|
|
/* FIXME: Rewrite because test is necessary only in first round. */
|
|
if ((mask & CEN_SPECIFIC) == 0 || (mask & XPG_SPECIFIC) == 0
|
|
|| ((mask & XPG_CODESET) != 0 && (mask & XPG_NORM_CODESET) != 0))
|
|
{
|
|
/* Allocate room for the full file name. */
|
|
filename = (char *) malloc (strlen (dirname) + 1
|
|
+ strlen (language)
|
|
+ ((mask & TERRITORY) != 0
|
|
? strlen (territory) + 1 : 0)
|
|
+ ((mask & XPG_CODESET) != 0
|
|
? strlen (codeset) + 1 : 0)
|
|
+ ((mask & XPG_NORM_CODESET) != 0
|
|
? strlen (normalized_codeset) + 1 : 0)
|
|
+ ((mask & XPG_MODIFIER) != 0 ?
|
|
strlen (modifier) + 1 : 0)
|
|
+ ((mask & CEN_SPECIAL) != 0
|
|
? strlen (special) + 1 : 0)
|
|
+ ((mask & CEN_SPONSOR) != 0
|
|
? strlen (sponsor) + 1 : 0)
|
|
+ ((mask & CEN_REVISION) != 0
|
|
? strlen (revision) + 1 : 0) + 1
|
|
+ strlen (domain) + 1);
|
|
|
|
if (filename == NULL)
|
|
return NULL;
|
|
|
|
retval = NULL;
|
|
last = NULL;
|
|
|
|
/* 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_NORM_CODESET) != 0)
|
|
{
|
|
*cp++ = '.';
|
|
cp = stpcpy (cp, normalized_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)
|
|
if (retval->filename != NULL)
|
|
{
|
|
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 = 254; cnt >= 0; --cnt)
|
|
if (cnt < mask && (cnt & ~mask) == 0
|
|
&& ((cnt & CEN_SPECIFIC) == 0 || (cnt & XPG_SPECIFIC) == 0)
|
|
&& ((cnt & XPG_CODESET) == 0 || (cnt & XPG_NORM_CODESET) == 0))
|
|
retval->successor[entries++] = make_entry_rec (dirname, cnt,
|
|
language, territory,
|
|
codeset,
|
|
normalized_codeset,
|
|
modifier, special,
|
|
sponsor, revision,
|
|
domain, 1);
|
|
retval->successor[entries] = NULL;
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
static const char *
|
|
normalize_codeset (codeset)
|
|
const char *codeset;
|
|
{
|
|
int len = 0;
|
|
int only_digit = 1;
|
|
const char *cp;
|
|
char *retval;
|
|
char *wp;
|
|
|
|
for (cp = codeset; cp[0] != '\0'; ++cp)
|
|
if (isalnum (cp[0]))
|
|
{
|
|
++len;
|
|
|
|
if (isalpha (cp[0]))
|
|
only_digit = 0;
|
|
}
|
|
|
|
retval = (char *) malloc ((only_digit ? 3 : 0) + len + 1);
|
|
|
|
if (retval != NULL)
|
|
{
|
|
if (only_digit)
|
|
wp = stpcpy (retval, "ISO");
|
|
else
|
|
wp = retval;
|
|
|
|
for (cp = codeset; cp[0] != '\0'; ++cp)
|
|
if (isalpha (cp[0]))
|
|
*wp++ = toupper (cp[0]);
|
|
else if (isdigit (cp[0]))
|
|
*wp++ = cp[0];
|
|
|
|
*wp = '\0';
|
|
}
|
|
|
|
return (const char *) retval;
|
|
}
|
|
|
|
|
|
/* @@ begin of epilog @@ */
|
|
|
|
/* 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 !_LIBC && !HAVE_STPCPY
|
|
static char *
|
|
stpcpy__ (dest, src)
|
|
char *dest;
|
|
const char *src;
|
|
{
|
|
while ((*dest++ = *src++) != '\0')
|
|
/* Do nothing. */ ;
|
|
return dest - 1;
|
|
}
|
|
#endif
|