mirror of
git://sourceware.org/git/glibc.git
synced 2024-11-21 01:12:26 +08:00
24906b43b9
Merge new message handling code from GNU gettext, by Drepper. * intl: New directory. * Makefile (subdirs): Add intl. * sysdeps/generic/dl-sysdep.c (_dl_sysdep_start): Return USER_ENTRY instead of storing it on our stack. * elf/rtld.c (rtld_command): Variable removed. (_dl_skip_args): New variable. (dl_main): Increment _dl_skip_args instead of setting rtld_command. If the link_map for the executable itself is not first in the chain, make it so. * sysdeps/i386/dl-machine.h (RTLD_START): Use _dl_skip_args as count of args to skip. Thu Sep 28 09:20:04 1995 Ulrich Drepper <drepper@gnu.ai.mit.edu> * stdlib/strtod.c (STRTOF): Fix handling of numbers with lots of leading zeroes.
477 lines
12 KiB
C
477 lines
12 KiB
C
/* 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;
|
|
}
|