diff options
author | Roland McGrath <roland@gnu.org> | 2002-08-10 06:22:37 +0000 |
---|---|---|
committer | Roland McGrath <roland@gnu.org> | 2002-08-10 06:22:37 +0000 |
commit | cb09a2cda2e83d6002a41a5d5e5f37a925f52d51 (patch) | |
tree | 8ed11334f3c54c99d6c4399839936d1d75af5301 /locale/programs/locarchive.c | |
parent | 946860b1cb6372260430fb0f446083d66f35f434 (diff) | |
download | glibc-cb09a2cda2e83d6002a41a5d5e5f37a925f52d51.zip glibc-cb09a2cda2e83d6002a41a5d5e5f37a925f52d51.tar.gz glibc-cb09a2cda2e83d6002a41a5d5e5f37a925f52d51.tar.bz2 |
* locale/loadarchive.c (_nl_load_locale_from_archive): Parse locale
name to find codeset name (if any) and normalize it. If the
normalized name differs, look up only that in the archive.
* locale/programs/locarchive.c (add_locale_to_archive): If the name
contains a codeset, normalize the codeset store only the normalized
name in the archive. If not, add an alias containing the locale's
normalized codeset name. Apply codeset name normalization when
matching entries in the alias file.
* locale/programs/locarchive.c (delete_locales_from_archive): Don't
decrement HEAD->namehash_used here.
(add_locale): Only need to insert name string when name_offset != 0.
* locale/programs/localedef.c (options): Add -A/--alias-file.
(alias_file): New variable.
(parse_opt): Grok -A, set that.
* locale/programs/localedef.h: Declare it.
* locale/programs/locarchive.c (insert_name): New function, broken out
of ...
(add_locale_to_archive): ... here. Call that.
(add_alias): New function.
(add_locale): New static function, add_locale_to_archive renamed.
(add_locale_to_archive): Call that and use add_alias to add an alias
for the name with codeset if the given name lacks it.
(enlarge_archive): Call add_locale instead of add_locale_to_archive.
* locale/Makefile (routines): Add loadarchive.
* locale/loadarchive.c: New file, started from code by Ulrich Drepper.
(_nl_load_locale_from_archive): New function.
* locale/localeinfo.h: Declare it.
* locale/findlocale.c (_nl_find_locale): If using default locale path,
try _nl_load_locale_from_archive first.
* locale/loadlocale.c (_nl_intern_locale_data): New function,
broken out of _nl_load_locale.
(_nl_load_locale): Call that.
* locale/localeinfo.h: Declare it.
(struct locale_data): Replace member `mmaped' with `alloc', an enum.
(struct locale_data): Remove unused member `options'.
* locale/findlocale.c (_nl_remove_locale): Update uses.
* locale/loadlocale.c (_nl_load_locale, _nl_unload_locale): Likewise.
* locale/C-collate.c: Update initializer.
* locale/C-identification.c: Likewise.
* locale/C-measurement.c: Likewise.
* locale/C-telephone.c: Likewise.
* locale/C-address.c: Likewise.
* locale/C-name.c: Likewise.
* locale/C-paper.c: Likewise.
* locale/C-time.c: Likewise.
* locale/C-numeric.c: Likewise.
* locale/C-monetary.c: Likewise.
* locale/C-messages.c : Likewise.
* locale/C-ctype.c: Likewise.
* locale/hashval.h [! LONGBITS]: Include <limits.h> here and
use CHAR_BIT instead of BITSPERBYTE.
* locale/localeinfo.h (_nl_find_locale, _nl_load_locale,
_nl_unload_locale): Add `internal_function attribute_hidden' to decls.
* locale/findlocale.c (_nl_find_locale): Add internal_function to defn.
(_nl_remove_locale): Likewise.
* locale/loadlocale.c (_nl_load_locale, _nl_unload_locale): Likewise.
* locale/findlocale.c (_nl_default_locale_path): New variable.
(_nl_find_locale): If LOCALE_PATH is null, default to that.
* locale/localeinfo.h: Declare it.
* locale/setlocale.c (setlocale): Use _nl_default_locale_path
in place of LOCALEDIR. If no LOCPATH, pass null to _nl_find_locale.
* locale/newlocale.c (__newlocale): Likewise.
* misc/err.c (vwarnx, vwarn): Fix typos in libc_hidden_def uses.
* inet/rexec.c (rexec_af): Add libc_hidden_def.
* sysdeps/generic/morecore.c: Likewise.
* signal/allocrtsig.c (__libc_current_sigrtmin): Likewise.
(__libc_current_sigrtmax): Likewise.
2002-08-08 Roland McGrath <roland@redhat.com>
* locale/loadlocale.c (_nl_load_locale): Don't use MAP_INHERIT.
* catgets/open_catalog.c (__open_catalog): Likewise.
* locale/programs/locarchive.c (INITIAL_NUM_NAMES): Renamed
from typo INITIAL_NUM_NANES.
(create_archive): Update use.
Diffstat (limited to 'locale/programs/locarchive.c')
-rw-r--r-- | locale/programs/locarchive.c | 416 |
1 files changed, 342 insertions, 74 deletions
diff --git a/locale/programs/locarchive.c b/locale/programs/locarchive.c index de026b2..267d7ba 100644 --- a/locale/programs/locarchive.c +++ b/locale/programs/locarchive.c @@ -31,6 +31,7 @@ #include <locale.h> #include <stdbool.h> #include <stdio.h> +#include <stdio_ext.h> #include <stdlib.h> #include <string.h> #include <time.h> @@ -59,7 +60,7 @@ static const char *locnames[] = /* Size of the initial archive header. */ -#define INITIAL_NUM_NANES 450 +#define INITIAL_NUM_NAMES 450 #define INITIAL_SIZE_STRINGS 3500 #define INITIAL_NUM_LOCREC 350 #define INITIAL_NUM_SUMS 2000 @@ -85,7 +86,7 @@ create_archive (const char *archivefname, struct locarhandle *ah) head.magic = AR_MAGIC; head.namehash_offset = sizeof (struct locarhead); head.namehash_used = 0; - head.namehash_size = next_prime (INITIAL_NUM_NANES); + head.namehash_size = next_prime (INITIAL_NUM_NAMES); head.string_offset = (head.namehash_offset + head.namehash_size * sizeof (struct namehashent)); @@ -166,6 +167,9 @@ create_archive (const char *archivefname, struct locarhandle *ah) ah->len = total; } +/* forward decl for below */ +static uint32_t add_locale (struct locarhandle *ah, const char *name, + locale_data_t data, bool replace); static void enlarge_archive (struct locarhandle *ah, const struct locarhead *head) @@ -300,10 +304,9 @@ enlarge_archive (struct locarhandle *ah, const struct locarhead *head) old_data[idx].sum); } - if (add_locale_to_archive (&new_ah, - ((char *) ah->addr - + oldnamehashtab[cnt].name_offset), - old_data, 0) != 0) + if (add_locale (&new_ah, + ((char *) ah->addr + oldnamehashtab[cnt].name_offset), + old_data, 0) == 0) error (EXIT_FAILURE, 0, _("cannot extend locale archive file")); } @@ -446,16 +449,123 @@ close_archive (struct locarhandle *ah) } } +#include "../../intl/explodename.c" +#include "../../intl/l10nflist.c" + +static struct namehashent * +insert_name (struct locarhandle *ah, + const char *name, size_t name_len, bool replace) +{ + const struct locarhead *const head = ah->addr; + struct namehashent *namehashtab + = (struct namehashent *) ((char *) ah->addr + head->namehash_offset); + unsigned int insert_idx, idx, incr; + + /* Hash value of the locale name. */ + uint32_t hval = compute_hashval (name, name_len); + + insert_idx = -1; + idx = hval % head->namehash_size; + incr = 1 + hval % (head->namehash_size - 2); + + /* If the name_offset field is zero this means this is a + deleted entry and therefore no entry can be found. */ + while (namehashtab[idx].name_offset != 0) + { + if (namehashtab[idx].hashval == hval + && strcmp (name, + (char *) ah->addr + namehashtab[idx].name_offset) == 0) + { + /* Found the entry. */ + if (namehashtab[idx].locrec_offset != 0 && ! replace) + { + if (! be_quiet) + error (0, 0, _("locale '%s' already exists"), name); + return NULL; + } + + break; + } + + /* Remember the first place we can insert the new entry. */ + if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1) + insert_idx = idx; + + idx += incr; + if (idx >= head->namehash_size) + idx -= head->namehash_size; + } + + /* Add as early as possible. */ + if (insert_idx != -1) + idx = insert_idx; + + namehashtab[idx].hashval = hval; /* no-op if replacing an old entry. */ + return &namehashtab[idx]; +} + +static void +add_alias (struct locarhandle *ah, const char *alias, bool replace, + const char *oldname, uint32_t locrec_offset) +{ + struct locarhead *head = ah->addr; + const size_t name_len = strlen (alias); + struct namehashent *namehashent = insert_name (ah, alias, strlen (alias), + replace); + if (namehashent == NULL && ! replace) + return; + + if (namehashent->name_offset == 0) + { + /* We are adding a new hash entry for this alias. + Determine whether we have to resize the file. */ + if (head->string_used + name_len + 1 > head->string_size + || 100 * head->namehash_used > 75 * head->namehash_size) + { + /* The current archive is not large enough. */ + enlarge_archive (ah, head); + + /* The locrecent might have moved, so we have to look up + the old name afresh. */ + namehashent = insert_name (ah, oldname, strlen (oldname), true); + assert (namehashent->name_offset != 0); + assert (namehashent->locrec_offset != 0); + locrec_offset = namehashent->locrec_offset; + + /* Tail call to try the whole thing again. */ + add_alias (ah, alias, replace, oldname, locrec_offset); + return; + } + + /* Add the name string. */ + memcpy (ah->addr + head->string_offset + head->string_used, + alias, name_len + 1); + namehashent->name_offset = head->string_offset + head->string_used; + head->string_used += name_len + 1; + + ++head->namehash_used; + } + + if (namehashent->locrec_offset != 0) + { + /* Replacing an existing entry. + Mark that we are no longer using the old locrecent. */ + struct locrecent *locrecent + = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + --locrecent->refs; + } + + /* Point this entry at the locrecent installed for the main name. */ + namehashent->locrec_offset = locrec_offset; +} + /* Check the content of the archive for duplicates. Add the content - of the files if necessary. Add all the names, possibly overwriting - old files. */ -int -add_locale_to_archive (ah, name, data, replace) - struct locarhandle *ah; - const char *name; - locale_data_t data; - bool replace; + of the files if necessary. Returns the locrec_offset. */ +static uint32_t +add_locale (struct locarhandle *ah, + const char *name, locale_data_t data, bool replace) { /* First look for the name. If it already exists and we are not supposed to replace it don't do anything. If it does not exist @@ -467,9 +577,7 @@ add_locale_to_archive (ah, name, data, replace) uint32_t hval; unsigned int cnt; unsigned int idx; - unsigned int insert_idx; struct locarhead *head; - struct namehashent *namehashtab; struct namehashent *namehashent; unsigned int incr; struct locrecent *locrecent; @@ -477,8 +585,6 @@ add_locale_to_archive (ah, name, data, replace) head = ah->addr; sumhashtab = (struct sumhashent *) ((char *) ah->addr + head->sumhash_offset); - namehashtab = (struct namehashent *) ((char *) ah->addr - + head->namehash_offset); /* For each locale category data set determine whether the same data @@ -514,47 +620,10 @@ add_locale_to_archive (ah, name, data, replace) } } - - /* Hash value of the locale name. */ - hval = compute_hashval (name, name_len); - - insert_idx = -1; - idx = hval % head->namehash_size; - incr = 1 + hval % (head->namehash_size - 2); - - /* If the name_offset field is zero this means this is no - deleted entry and therefore no entry can be found. */ - while (namehashtab[idx].name_offset != 0) - { - if (namehashtab[idx].hashval == hval - && strcmp (name, - (char *) ah->addr + namehashtab[idx].name_offset) == 0) - { - /* Found the entry. */ - if (namehashtab[idx].locrec_offset != 0 && ! replace) - { - if (! be_quiet) - error (0, 0, _("locale '%s' already exists"), name); - return 1; - } - - break; - } - - /* Remember the first place we can insert the new entry. */ - if (namehashtab[idx].locrec_offset == 0 && insert_idx == -1) - insert_idx = idx; - - idx += incr; - if (idx >= head->namehash_size) - idx -= head->namehash_size; - } - - /* Add as early as possible. */ - if (insert_idx != -1) - idx = insert_idx; - - namehashent = &namehashtab[idx]; + /* Find a slot for the locale name in the hash table. */ + namehashent = insert_name (ah, name, name_len, replace); + if (namehashent == NULL) /* Already exists and !REPLACE. */ + return 0; /* Determine whether we have to resize the file. */ if (100 * (head->sumhash_used + num_new_offsets) > 75 * head->sumhash_size @@ -565,7 +634,7 @@ add_locale_to_archive (ah, name, data, replace) { /* The current archive is not large enough. */ enlarge_archive (ah, head); - return add_locale_to_archive (ah, name, data, replace); + return add_locale (ah, name, data, replace); } /* Add the locale data which is not yet in the archive. */ @@ -620,29 +689,46 @@ add_locale_to_archive (ah, name, data, replace) ++head->sumhash_used; } - - if (namehashent->locrec_offset == 0) + if (namehashent->name_offset == 0) { /* Add the name string. */ memcpy ((char *) ah->addr + head->string_offset + head->string_used, name, name_len + 1); namehashent->name_offset = head->string_offset + head->string_used; head->string_used += name_len + 1; + ++head->namehash_used; + } + if (namehashent->locrec_offset == 0) + { /* Allocate a name location record. */ namehashent->locrec_offset = (head->locrectab_offset + (head->locrectab_used++ * sizeof (struct locrecent))); - - namehashent->hashval = hval; - - ++head->namehash_used; + locrecent = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + locrecent->refs = 1; } + else + { + /* If there are other aliases pointing to this locrecent, + we still need a new one. If not, reuse the old one. */ + locrecent = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + if (locrecent->refs > 1) + { + --locrecent->refs; + namehashent->locrec_offset = (head->locrectab_offset + + (head->locrectab_used++ + * sizeof (struct locrecent))); + locrecent = (struct locrecent *) ((char *) ah->addr + + namehashent->locrec_offset); + locrecent->refs = 1; + } + } /* Fill in the table with the locations of the locale data. */ - locrecent = (struct locrecent *) ((char *) ah->addr - + namehashent->locrec_offset); for (cnt = 0; cnt < __LC_LAST; ++cnt) if (cnt != LC_ALL) { @@ -650,13 +736,196 @@ add_locale_to_archive (ah, name, data, replace) locrecent->record[cnt].len = data[cnt].size; } + return namehashent->locrec_offset; +} - /* Read the locale.alias file to see whether any matching record is - found. If an entry is available check whether it is already in - the archive. If this is the case check whether the new locale's - name is more specific than the one currently referred to by the - alias. */ +/* Check the content of the archive for duplicates. Add the content + of the files if necessary. Add all the names, possibly overwriting + old files. */ +int +add_locale_to_archive (ah, name, data, replace) + struct locarhandle *ah; + const char *name; + locale_data_t data; + bool replace; +{ + char *normalized_name = NULL; + uint32_t locrec_offset; + + /* First analyze the name to decide how to archive it. */ + const char *language; + const char *modifier; + const char *territory; + const char *codeset; + const char *normalized_codeset; + int mask = _nl_explode_name (strdupa (name), + &language, &modifier, &territory, + &codeset, &normalized_codeset); + + if (mask & XPG_NORM_CODESET) + /* This name contains a codeset in unnormalized form. + We will store it in the archive with a normalized name. */ + asprintf (&normalized_name, "%s%s%s.%s%s%s", + language, territory == NULL ? "" : "_", territory ?: "", + (mask & XPG_NORM_CODESET) ? normalized_codeset : codeset, + modifier == NULL ? "" : "@", modifier ?: ""); + + /* This call does the main work. */ + locrec_offset = add_locale (ah, normalized_name ?: name, data, replace); + free (normalized_name); + if (locrec_offset == 0) + { + if (mask & XPG_NORM_CODESET) + free ((char *) normalized_codeset); + return -1; + } + + if ((mask & XPG_CODESET) == 0) + { + /* This name lacks a codeset, so determine the locale's codeset and + add an alias for its name with normalized codeset appended. */ + + const struct + { + unsigned int magic; + unsigned int nstrings; + unsigned int strindex[0]; + } *filedata = data[LC_CTYPE].addr; + codeset = (char *) filedata + + filedata->strindex[_NL_ITEM_INDEX (_NL_CTYPE_CODESET_NAME)]; + + normalized_codeset = _nl_normalize_codeset (codeset, strlen (codeset)); + mask |= XPG_NORM_CODESET; + + asprintf (&normalized_name, "%s%s%s.%s%s%s", + language, territory == NULL ? "" : "_", territory ?: "", + normalized_codeset, + modifier == NULL ? "" : "@", modifier ?: ""); + + add_alias (ah, normalized_name, replace, name, locrec_offset); + free (normalized_name); + } + + /* Now read the locale.alias files looking for lines whose + right hand side matches our name after normalization. */ + if (alias_file != NULL) + { + FILE *fp; + fp = fopen (alias_file, "r"); + if (fp == NULL) + error (1, errno, _("locale alias file `%s' not found"), + alias_file); + + /* No threads present. */ + __fsetlocking (fp, FSETLOCKING_BYCALLER); + + while (! feof_unlocked (fp)) + { + /* It is a reasonable approach to use a fix buffer here + because + a) we are only interested in the first two fields + b) these fields must be usable as file names and so must + not be that long */ + char buf[BUFSIZ]; + char *alias; + char *value; + char *cp; + + if (fgets_unlocked (buf, BUFSIZ, fp) == NULL) + /* EOF reached. */ + break; + + cp = buf; + /* Ignore leading white space. */ + while (isspace (cp[0]) && cp[0] != '\n') + ++cp; + + /* A leading '#' signals a comment line. */ + if (cp[0] != '\0' && cp[0] != '#' && cp[0] != '\n') + { + alias = cp++; + while (cp[0] != '\0' && !isspace (cp[0])) + ++cp; + /* Terminate alias name. */ + if (cp[0] != '\0') + *cp++ = '\0'; + + /* Now look for the beginning of the value. */ + while (isspace (cp[0])) + ++cp; + + if (cp[0] != '\0') + { + value = cp++; + while (cp[0] != '\0' && !isspace (cp[0])) + ++cp; + /* Terminate value. */ + if (cp[0] == '\n') + { + /* This has to be done to make the following + test for the end of line possible. We are + looking for the terminating '\n' which do not + overwrite here. */ + *cp++ = '\0'; + *cp = '\n'; + } + else if (cp[0] != '\0') + *cp++ = '\0'; + + /* Does this alias refer to our locale? We will + normalize the right hand side and compare the + elements of the normalized form. */ + { + const char *rhs_language; + const char *rhs_modifier; + const char *rhs_territory; + const char *rhs_codeset; + const char *rhs_normalized_codeset; + int rhs_mask = _nl_explode_name (value, + &rhs_language, + &rhs_modifier, + &rhs_territory, + &rhs_codeset, + &rhs_normalized_codeset); + if (!strcmp (language, rhs_language) + && ((rhs_mask & XPG_CODESET) + /* He has a codeset, it must match normalized. */ + ? !strcmp ((mask & XPG_NORM_CODESET) + ? normalized_codeset : codeset, + (rhs_mask & XPG_NORM_CODESET) + ? rhs_normalized_codeset : rhs_codeset) + /* He has no codeset, we must also have none. */ + : (mask & XPG_CODESET) == 0) + /* Codeset (or lack thereof) matches. */ + && !strcmp (territory ?: "", rhs_territory ?: "") + && !strcmp (modifier ?: "", rhs_modifier ?: "")) + /* We have a winner. */ + add_alias (ah, alias, replace, + normalized_name ?: name, locrec_offset); + if (rhs_mask & XPG_NORM_CODESET) + free ((char *) rhs_normalized_codeset); + } + } + } + + /* Possibly not the whole line fits into the buffer. + Ignore the rest of the line. */ + while (strchr (cp, '\n') == NULL) + { + cp = buf; + if (fgets_unlocked (buf, BUFSIZ, fp) == NULL) + /* Make sure the inner loop will be left. The outer + loop will exit at the `feof' test. */ + *cp = '\n'; + } + } + + fclose (fp); + } + + if (mask & XPG_NORM_CODESET) + free ((char *) normalized_codeset); return 0; } @@ -903,7 +1172,6 @@ delete_locales_from_archive (nlist, list) /* Found the entry. Now mark it as removed by zero-ing the reference to the locale record. */ namehashtab[idx].locrec_offset = 0; - --head->namehash_used; break; } |