summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorache <ache@FreeBSD.org>2002-08-08 05:51:54 +0000
committerache <ache@FreeBSD.org>2002-08-08 05:51:54 +0000
commit3b0ddae36e14f345ddbfb571d2838b236c78d073 (patch)
tree92e69228cb60dc54efe523b4a60905762797b926 /lib
parenta72d8585b7d6c0e0b3cd0824b229a07724c33288 (diff)
downloadFreeBSD-src-3b0ddae36e14f345ddbfb571d2838b236c78d073.zip
FreeBSD-src-3b0ddae36e14f345ddbfb571d2838b236c78d073.tar.gz
Rewrite locale loading procedures, so any load failure will not affect
currently cached data. It allows a number of nice things, like: removing fallback code from single locale loading, remove memory leak when LC_CTYPE data loaded again and again, efficient cache use, not only for setlocale(locale1); setlocale(locale1), but for setlocale(locale1); setlocale("C"); setlocale(locale1) too (i.e. data file loaded only once).
Diffstat (limited to 'lib')
-rw-r--r--lib/libc/locale/collate.c126
-rw-r--r--lib/libc/locale/collate.h3
-rw-r--r--lib/libc/locale/euc.c35
-rw-r--r--lib/libc/locale/ldpart.c88
-rw-r--r--lib/libc/locale/ldpart.h4
-rw-r--r--lib/libc/locale/lmessages.c23
-rw-r--r--lib/libc/locale/lmonetary.c9
-rw-r--r--lib/libc/locale/lnumeric.c7
-rw-r--r--lib/libc/locale/rune.c48
-rw-r--r--lib/libc/locale/setlocale.c37
-rw-r--r--lib/libc/locale/setrunelocale.c71
11 files changed, 259 insertions, 192 deletions
diff --git a/lib/libc/locale/collate.c b/lib/libc/locale/collate.c
index 83684d1..c7e6c12 100644
--- a/lib/libc/locale/collate.c
+++ b/lib/libc/locale/collate.c
@@ -40,68 +40,115 @@ __FBSDID("$FreeBSD$");
#include "collate.h"
#include "setlocale.h"
+#include "ldpart.h"
#include "libc_private.h"
int __collate_load_error = 1;
int __collate_substitute_nontrivial;
-char __collate_version[STR_LEN];
+
u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN];
struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
-#define FREAD(a, b, c, d) \
- do { \
- if (fread(a, b, c, d) != c) { \
- (void)fclose(d); \
- return -1; \
- } \
- } while(0)
-
void __collate_err(int ex, const char *f) __dead2;
int
-__collate_load_tables(encoding)
- char *encoding;
+__collate_load_tables(const char *encoding)
{
- char buf[PATH_MAX];
FILE *fp;
- int i, save_load_error;
+ int i, saverr;
+ char collate_version[STR_LEN];
+ char buf[PATH_MAX];
+ char *TMP_substitute_table, *TMP_char_pri_table, *TMP_chain_pri_table;
+ static char collate_encoding[ENCODING_LEN + 1];
- save_load_error = __collate_load_error;
- __collate_load_error = 1;
- if (!encoding) {
- __collate_load_error = save_load_error;
- return -1;
+ /* 'encoding' must be already checked. */
+ if (strcmp(encoding, "C") == 0 || strcmp(encoding, "POSIX") == 0) {
+ __collate_load_error = 1;
+ return (_LDP_CACHE);
}
- if (!strcmp(encoding, "C") || !strcmp(encoding, "POSIX"))
- return 0;
- if (!_PathLocale) {
- __collate_load_error = save_load_error;
- return -1;
+
+ /*
+ * If the locale name is the same as our cache, use the cache.
+ */
+ if (strcmp(encoding, collate_encoding) == 0) {
+ __collate_load_error = 0;
+ return (_LDP_CACHE);
}
+
+ /*
+ * Slurp the locale file into the cache.
+ */
+
+ /* 'PathLocale' must be already set & checked. */
/* Range checking not needed, encoding has fixed size */
(void)strcpy(buf, _PathLocale);
(void)strcat(buf, "/");
(void)strcat(buf, encoding);
(void)strcat(buf, "/LC_COLLATE");
- if ((fp = fopen(buf, "r")) == NULL) {
- __collate_load_error = save_load_error;
- return -1;
+ if ((fp = fopen(buf, "r")) == NULL)
+ return (_LDP_ERROR);
+
+ if ((TMP_substitute_table =
+ malloc(sizeof(__collate_substitute_table))) == NULL) {
+ (void)fclose(fp);
+ errno = ENOMEM;
+ return (_LDP_ERROR);
}
- FREAD(__collate_version, sizeof(__collate_version), 1, fp);
- if (strcmp(__collate_version, COLLATE_VERSION) != 0) {
+ if ((TMP_char_pri_table =
+ malloc(sizeof(__collate_char_pri_table))) == NULL) {
+ free(TMP_substitute_table);
(void)fclose(fp);
- return -1;
+ errno = ENOMEM;
+ return (_LDP_ERROR);
}
- FREAD(__collate_substitute_table, sizeof(__collate_substitute_table),
- 1, fp);
- FREAD(__collate_char_pri_table, sizeof(__collate_char_pri_table), 1,
- fp);
- FREAD(__collate_chain_pri_table, sizeof(__collate_chain_pri_table), 1,
- fp);
+ if ((TMP_chain_pri_table =
+ malloc(sizeof(__collate_chain_pri_table))) == NULL) {
+ free(TMP_substitute_table);
+ free(TMP_char_pri_table);
+ (void)fclose(fp);
+ errno = ENOMEM;
+ return (_LDP_ERROR);
+ }
+
+#define FREAD(a, b, c, d) \
+{ \
+ if (fread(a, b, c, d) != c) { \
+ saverr = errno; \
+ free(TMP_substitute_table); \
+ free(TMP_char_pri_table); \
+ free(TMP_chain_pri_table); \
+ (void)fclose(d); \
+ errno = saverr; \
+ return (_LDP_ERROR); \
+ } \
+}
+
+ FREAD(collate_version, sizeof(collate_version), 1, fp);
+ if (strcmp(collate_version, COLLATE_VERSION) != 0) {
+ free(TMP_substitute_table);
+ free(TMP_char_pri_table);
+ free(TMP_chain_pri_table);
+ (void)fclose(fp);
+ errno = EFTYPE;
+ return (_LDP_ERROR);
+ }
+ FREAD(TMP_substitute_table, sizeof(__collate_substitute_table), 1, fp);
+ FREAD(TMP_char_pri_table, sizeof(__collate_char_pri_table), 1, fp);
+ FREAD(TMP_chain_pri_table, sizeof(__collate_chain_pri_table), 1, fp);
(void)fclose(fp);
- __collate_load_error = 0;
+
+ (void)strcpy(collate_encoding, encoding);
+ (void)memcpy(__collate_substitute_table, TMP_substitute_table,
+ sizeof(__collate_substitute_table));
+ (void)memcpy(__collate_char_pri_table, TMP_char_pri_table,
+ sizeof(__collate_char_pri_table));
+ (void)memcpy(__collate_chain_pri_table, TMP_chain_pri_table,
+ sizeof(__collate_chain_pri_table));
+ free(TMP_substitute_table);
+ free(TMP_char_pri_table);
+ free(TMP_chain_pri_table);
__collate_substitute_nontrivial = 0;
for (i = 0; i < UCHAR_MAX + 1; i++) {
@@ -111,8 +158,9 @@ __collate_load_tables(encoding)
break;
}
}
+ __collate_load_error = 0;
- return 0;
+ return (_LDP_LOADED);
}
u_char *
@@ -152,8 +200,8 @@ __collate_lookup(t, len, prim, sec)
*len = 1;
*prim = *sec = 0;
- for(p2 = __collate_chain_pri_table; p2->str[0]; p2++) {
- if(strncmp(t, p2->str, strlen(p2->str)) == 0) {
+ for (p2 = __collate_chain_pri_table; p2->str[0]; p2++) {
+ if (strncmp(t, p2->str, strlen(p2->str)) == 0) {
*len = strlen(p2->str);
*prim = p2->prim;
*sec = p2->sec;
diff --git a/lib/libc/locale/collate.h b/lib/libc/locale/collate.h
index d8dd045..4a0eefb 100644
--- a/lib/libc/locale/collate.h
+++ b/lib/libc/locale/collate.h
@@ -48,7 +48,6 @@ struct __collate_st_chain_pri {
extern int __collate_load_error;
extern int __collate_substitute_nontrivial;
-extern char __collate_version[STR_LEN];
extern u_char __collate_substitute_table[UCHAR_MAX + 1][STR_LEN];
extern struct __collate_st_char_pri __collate_char_pri_table[UCHAR_MAX + 1];
extern struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
@@ -56,7 +55,7 @@ extern struct __collate_st_chain_pri __collate_chain_pri_table[TABLE_SIZE];
__BEGIN_DECLS
u_char *__collate_strdup(u_char *);
u_char *__collate_substitute(const u_char *);
-int __collate_load_tables(char *);
+int __collate_load_tables(const char *);
void __collate_lookup(const u_char *, int *, int *, int *);
int __collate_range_cmp(int, int);
#ifdef COLLATE_DEBUG
diff --git a/lib/libc/locale/euc.c b/lib/libc/locale/euc.c
index 9008a91..43194fa 100644
--- a/lib/libc/locale/euc.c
+++ b/lib/libc/locale/euc.c
@@ -63,40 +63,36 @@ _EUC_init(rl)
_RuneLocale *rl;
{
_EucInfo *ei;
- int x;
+ int x, new__mb_cur_max;
char *v, *e;
rl->sgetrune = _EUC_sgetrune;
rl->sputrune = _EUC_sputrune;
- if (!rl->variable) {
- free(rl);
+ if (rl->variable == NULL)
return (EFTYPE);
- }
- v = (char *) rl->variable;
+
+ v = (char *)rl->variable;
while (*v == ' ' || *v == '\t')
++v;
- if ((ei = malloc(sizeof(_EucInfo))) == NULL) {
- free(rl);
+ if ((ei = malloc(sizeof(_EucInfo))) == NULL)
return (ENOMEM);
- }
- __mb_cur_max = 0;
+
+ new__mb_cur_max = 0;
for (x = 0; x < 4; ++x) {
- ei->count[x] = (int) strtol(v, &e, 0);
+ ei->count[x] = (int)strtol(v, &e, 0);
if (v == e || !(v = e)) {
- free(rl);
free(ei);
return (EFTYPE);
}
- if (__mb_cur_max < ei->count[x])
- __mb_cur_max = ei->count[x];
+ if (new__mb_cur_max < ei->count[x])
+ new__mb_cur_max = ei->count[x];
while (*v == ' ' || *v == '\t')
++v;
- ei->bits[x] = (int) strtol(v, &e, 0);
+ ei->bits[x] = (int)strtol(v, &e, 0);
if (v == e || !(v = e)) {
- free(rl);
free(ei);
return (EFTYPE);
}
@@ -105,18 +101,13 @@ _EUC_init(rl)
}
ei->mask = (int)strtol(v, &e, 0);
if (v == e || !(v = e)) {
- free(rl);
free(ei);
return (EFTYPE);
}
- if (sizeof(_EucInfo) <= rl->variable_len) {
- memcpy(rl->variable, ei, sizeof(_EucInfo));
- free(ei);
- } else {
- rl->variable = ei;
- }
+ rl->variable = ei;
rl->variable_len = sizeof(_EucInfo);
_CurrentRuneLocale = rl;
+ __mb_cur_max = new__mb_cur_max;
return (0);
}
diff --git a/lib/libc/locale/ldpart.c b/lib/libc/locale/ldpart.c
index 0e276cf..1933312 100644
--- a/lib/libc/locale/ldpart.c
+++ b/lib/libc/locale/ldpart.c
@@ -42,7 +42,6 @@ __FBSDID("$FreeBSD$");
#include "ldpart.h"
static int split_lines(char *, const char *);
-static void set_from_buf(const char *, int, const char **);
int
__part_load_locale(const char *name,
@@ -53,35 +52,25 @@ __part_load_locale(const char *name,
int locale_buf_size_min,
const char **dst_localebuf)
{
- static char locale_buf_C[] = "C";
- static int num_lines;
- int saverr;
- int fd;
- char *lbuf;
- char *p;
- const char *plim;
- char filename[PATH_MAX];
+ int saverr, fd, i, num_lines;
+ char *lbuf, *p;
+ const char *plim;
+ char filename[PATH_MAX];
struct stat st;
- size_t namesize;
- size_t bufsize;
- int save_using_locale;
-
- save_using_locale = *using_locale;
- *using_locale = 0;
+ size_t namesize, bufsize;
/* 'name' must be already checked. */
-
- if (!strcmp(name, "C") || !strcmp(name, "POSIX"))
- return 0;
+ if (strcmp(name, "C") == 0 || strcmp(name, "POSIX") == 0) {
+ *using_locale = 0;
+ return (_LDP_CACHE);
+ }
/*
* If the locale name is the same as our cache, use the cache.
*/
- lbuf = locale_buf;
- if (lbuf != NULL && strcmp(name, lbuf) == 0) {
- set_from_buf(lbuf, num_lines, dst_localebuf);
+ if (locale_buf != NULL && strcmp(name, locale_buf) == 0) {
*using_locale = 1;
- return 0;
+ return (_LDP_CACHE);
}
/*
@@ -90,16 +79,14 @@ __part_load_locale(const char *name,
namesize = strlen(name) + 1;
/* 'PathLocale' must be already set & checked. */
-
/* Range checking not needed, 'name' size is limited */
strcpy(filename, _PathLocale);
strcat(filename, "/");
strcat(filename, name);
strcat(filename, "/");
strcat(filename, category_filename);
- fd = _open(filename, O_RDONLY);
- if (fd < 0)
- goto no_locale;
+ if ((fd = _open(filename, O_RDONLY)) < 0)
+ return (_LDP_ERROR);
if (_fstat(fd, &st) != 0)
goto bad_locale;
if (st.st_size <= 0) {
@@ -107,18 +94,15 @@ __part_load_locale(const char *name,
goto bad_locale;
}
bufsize = namesize + st.st_size;
- locale_buf = NULL;
- lbuf = (lbuf == NULL || lbuf == locale_buf_C) ?
- malloc(bufsize) : reallocf(lbuf, bufsize);
- if (lbuf == NULL)
+ if ((lbuf = malloc(bufsize)) == NULL) {
+ errno = ENOMEM;
goto bad_locale;
+ }
(void)strcpy(lbuf, name);
p = lbuf + namesize;
plim = p + st.st_size;
if (_read(fd, p, (size_t) st.st_size) != st.st_size)
goto bad_lbuf;
- if (_close(fd) != 0)
- goto bad_lbuf;
/*
* Parse the locale file into localebuf.
*/
@@ -133,37 +117,37 @@ __part_load_locale(const char *name,
num_lines = locale_buf_size_min;
else {
errno = EFTYPE;
- goto reset_locale;
+ goto bad_lbuf;
}
- set_from_buf(lbuf, num_lines, dst_localebuf);
+ (void)_close(fd);
/*
* Record the successful parse in the cache.
*/
+ if (locale_buf != NULL)
+ free(locale_buf);
locale_buf = lbuf;
-
+ for (p = locale_buf, i = 0; i < num_lines; i++)
+ dst_localebuf[i] = (p += strlen(p) + 1);
+ for (i = num_lines; i < locale_buf_size_max; i++)
+ dst_localebuf[i] = NULL;
*using_locale = 1;
- return 0;
+ return (_LDP_LOADED);
-reset_locale:
- locale_buf = locale_buf_C;
- save_using_locale = 0;
bad_lbuf:
- saverr = errno;
- free(lbuf);
+ saverr = errno;
+ free(lbuf);
errno = saverr;
bad_locale:
- saverr = errno;
- (void)_close(fd);
+ saverr = errno;
+ (void)_close(fd);
errno = saverr;
-no_locale:
- *using_locale = save_using_locale;
- return -1;
+ return (_LDP_ERROR);
}
static int
-split_lines(char *p, const char *plim)
+split_lines(char *p, const char *plim)
{
int i;
@@ -174,13 +158,3 @@ split_lines(char *p, const char *plim)
return (i);
}
-static void
-set_from_buf(const char *p, int num_lines, const char **dst_localebuf) {
-
- const char **ap;
- int i;
-
- for (ap = dst_localebuf, i = 0; i < num_lines; ++ap, ++i)
- *ap = p += strlen(p) + 1;
-}
-
diff --git a/lib/libc/locale/ldpart.h b/lib/libc/locale/ldpart.h
index 049ab56..becca06 100644
--- a/lib/libc/locale/ldpart.h
+++ b/lib/libc/locale/ldpart.h
@@ -29,6 +29,10 @@
#ifndef _LDPART_H_
#define _LDPART_H_
+#define _LDP_LOADED 0
+#define _LDP_ERROR (-1)
+#define _LDP_CACHE 1
+
int __part_load_locale(const char *, int*, char *, const char *,
int, int, const char **);
diff --git a/lib/libc/locale/lmessages.c b/lib/libc/locale/lmessages.c
index dd1239e..ed58c99 100644
--- a/lib/libc/locale/lmessages.c
+++ b/lib/libc/locale/lmessages.c
@@ -52,18 +52,19 @@ static char *_messages_locale_buf;
int
__messages_load_locale(const char *name)
{
- /*
- * Propose that we can have incomplete locale file (w/o "{yes,no}str").
- * Initialize them before loading. In case of complete locale, they'll
- * be initialized to loaded value, otherwise they'll not be touched.
- */
- _messages_locale.yesstr = empty;
- _messages_locale.nostr = empty;
+ int ret;
- return __part_load_locale(name, &_messages_using_locale,
- _messages_locale_buf, "LC_MESSAGES",
- LCMESSAGES_SIZE_FULL, LCMESSAGES_SIZE_MIN,
- (const char **)&_messages_locale);
+ ret = __part_load_locale(name, &_messages_using_locale,
+ _messages_locale_buf, "LC_MESSAGES",
+ LCMESSAGES_SIZE_FULL, LCMESSAGES_SIZE_MIN,
+ (const char **)&_messages_locale);
+ if (ret == _LDP_LOADED) {
+ if (_messages_locale.yesstr == NULL)
+ _messages_locale.yesstr = empty;
+ if (_messages_locale.nostr == NULL)
+ _messages_locale.nostr = empty;
+ }
+ return (ret);
}
struct lc_messages_T *
diff --git a/lib/libc/locale/lmonetary.c b/lib/libc/locale/lmonetary.c
index eddd1ce..3dc6df0 100644
--- a/lib/libc/locale/lmonetary.c
+++ b/lib/libc/locale/lmonetary.c
@@ -63,7 +63,7 @@ static int _monetary_using_locale;
static char *_monetary_locale_buf;
static char
-cnv(const char *str)
+cnv(const char *str)
{
int i = strtol(str, NULL, 10);
@@ -77,12 +77,13 @@ __monetary_load_locale(const char *name)
{
int ret;
- __mlocale_changed = 1;
ret = __part_load_locale(name, &_monetary_using_locale,
_monetary_locale_buf, "LC_MONETARY",
LCMONETARY_SIZE, LCMONETARY_SIZE,
(const char **)&_monetary_locale);
- if (ret == 0 && _monetary_using_locale) {
+ if (ret != _LDP_ERROR)
+ __mlocale_changed = 1;
+ if (ret == _LDP_LOADED) {
_monetary_locale.mon_grouping =
__fix_locale_grouping_str(_monetary_locale.mon_grouping);
@@ -102,7 +103,7 @@ __monetary_load_locale(const char *name)
}
struct lc_monetary_T *
-__get_current_monetary_locale(void)
+__get_current_monetary_locale(void)
{
return (_monetary_using_locale
? &_monetary_locale
diff --git a/lib/libc/locale/lnumeric.c b/lib/libc/locale/lnumeric.c
index 0fb0aab..b1b975e 100644
--- a/lib/libc/locale/lnumeric.c
+++ b/lib/libc/locale/lnumeric.c
@@ -53,14 +53,15 @@ __numeric_load_locale(const char *name)
{
int ret;
- __nlocale_changed = 1;
ret = __part_load_locale(name, &_numeric_using_locale,
_numeric_locale_buf, "LC_NUMERIC",
LCNUMERIC_SIZE, LCNUMERIC_SIZE,
(const char **)&_numeric_locale);
- if (ret == 0 && _numeric_using_locale)
+ if (ret != _LDP_ERROR)
+ __nlocale_changed = 1;
+ if (ret == _LDP_LOADED)
_numeric_locale.grouping =
- __fix_locale_grouping_str(_numeric_locale.grouping);
+ __fix_locale_grouping_str(_numeric_locale.grouping);
return (ret);
}
diff --git a/lib/libc/locale/rune.c b/lib/libc/locale/rune.c
index c67773c..63a50de 100644
--- a/lib/libc/locale/rune.c
+++ b/lib/libc/locale/rune.c
@@ -42,6 +42,7 @@ __FBSDID("$FreeBSD$");
#include "namespace.h"
#include <arpa/inet.h>
+#include <errno.h>
#include <rune.h>
#include <stdio.h>
#include <string.h>
@@ -59,22 +60,35 @@ _Read_RuneMagi(fp)
_RuneLocale *rl;
_RuneEntry *rr;
struct stat sb;
- int x;
+ int x, saverr;
if (_fstat(fileno(fp), &sb) < 0)
- return(0);
+ return (NULL);
- if (sb.st_size < sizeof(_RuneLocale))
- return(0);
+ if (sb.st_size < sizeof(_RuneLocale)) {
+ errno = EFTYPE;
+ return (NULL);
+ }
- if ((data = malloc(sb.st_size)) == NULL)
- return(0);
+ if ((data = malloc(sb.st_size)) == NULL) {
+ errno = ENOMEM;
+ return (NULL);
+ }
+ errno = 0;
rewind(fp); /* Someone might have read the magic number once already */
+ if (errno) {
+ saverr = errno;
+ free(data);
+ errno = saverr;
+ return (NULL);
+ }
if (fread(data, sb.st_size, 1, fp) != 1) {
+ saverr = errno;
free(data);
- return(0);
+ errno = saverr;
+ return (NULL);
}
rl = (_RuneLocale *)data;
@@ -84,7 +98,8 @@ _Read_RuneMagi(fp)
if (memcmp(rl->magic, _RUNE_MAGIC_1, sizeof(rl->magic))) {
free(data);
- return(0);
+ errno = EFTYPE;
+ return (NULL);
}
rl->invalid_rune = ntohl(rl->invalid_rune);
@@ -103,21 +118,24 @@ _Read_RuneMagi(fp)
rl->variable = rl->runetype_ext.ranges + rl->runetype_ext.nranges;
if (rl->variable > lastp) {
free(data);
- return(0);
+ errno = EFTYPE;
+ return (NULL);
}
rl->maplower_ext.ranges = (_RuneEntry *)rl->variable;
rl->variable = rl->maplower_ext.ranges + rl->maplower_ext.nranges;
if (rl->variable > lastp) {
free(data);
- return(0);
+ errno = EFTYPE;
+ return (NULL);
}
rl->mapupper_ext.ranges = (_RuneEntry *)rl->variable;
rl->variable = rl->mapupper_ext.ranges + rl->mapupper_ext.nranges;
if (rl->variable > lastp) {
free(data);
- return(0);
+ errno = EFTYPE;
+ return (NULL);
}
for (x = 0; x < rl->runetype_ext.nranges; ++x) {
@@ -131,7 +149,8 @@ _Read_RuneMagi(fp)
rl->variable = rr[x].types + len;
if (rl->variable > lastp) {
free(data);
- return(0);
+ errno = EFTYPE;
+ return (NULL);
}
while (len-- > 0)
rr[x].types[len] = ntohl(rr[x].types[len]);
@@ -156,7 +175,8 @@ _Read_RuneMagi(fp)
}
if (((char *)rl->variable) + rl->variable_len > (char *)lastp) {
free(data);
- return(0);
+ errno = EFTYPE;
+ return (NULL);
}
/*
@@ -174,5 +194,5 @@ _Read_RuneMagi(fp)
if (!rl->mapupper_ext.nranges)
rl->mapupper_ext.ranges = 0;
- return(rl);
+ return (rl);
}
diff --git a/lib/libc/locale/setlocale.c b/lib/libc/locale/setlocale.c
index 6b249a9..525ca8f 100644
--- a/lib/libc/locale/setlocale.c
+++ b/lib/libc/locale/setlocale.c
@@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$");
#include "lnumeric.h" /* for __numeric_load_locale() */
#include "lmessages.h" /* for __messages_load_locale() */
#include "setlocale.h"
+#include "ldpart.h"
#include "../stdtime/timelocal.h" /* for __time_load_locale() */
/*
@@ -92,7 +93,7 @@ static char saved_categories[_LC_LAST][ENCODING_LEN + 1];
static char current_locale_string[_LC_LAST * (ENCODING_LEN + 1/*"/"*/ + 1)];
static char *currentlocale(void);
-static int wrap_setrunelocale(char *);
+static int wrap_setrunelocale(const char *);
static char *loadlocale(int);
char *
@@ -206,7 +207,10 @@ setlocale(category, locale)
for (j = 1; j < i; j++) {
(void)strcpy(new_categories[j],
saved_categories[j]);
- (void)loadlocale(j);
+ if (loadlocale(j) == NULL) {
+ (void)strcpy(new_categories[j], "C");
+ (void)loadlocale(j);
+ }
}
errno = saverr;
return (NULL);
@@ -235,27 +239,24 @@ currentlocale()
}
static int
-wrap_setrunelocale(locale)
- char *locale;
+wrap_setrunelocale(const char *locale)
{
- int ret = setrunelocale(locale);
+ int ret = setrunelocale((char *)locale);
if (ret != 0) {
errno = ret;
- return (-1);
+ return (_LDP_ERROR);
}
- return (0);
+ return (_LDP_LOADED);
}
static char *
loadlocale(category)
int category;
{
- char *ret;
char *new = new_categories[category];
char *old = current_categories[category];
- int (*func)();
- int saverr;
+ int (*func)(const char *);
if ((new[0] == '.' &&
(new[1] == '\0' || (new[1] == '.' && new[2] == '\0'))) ||
@@ -278,8 +279,10 @@ loadlocale(category)
return (NULL);
}
_PathLocale = strdup(p);
- if (_PathLocale == NULL)
+ if (_PathLocale == NULL) {
+ errno = ENOMEM;
return (NULL);
+ }
} else
_PathLocale = _PATH_LOCALE;
}
@@ -311,15 +314,11 @@ loadlocale(category)
if (strcmp(new, old) == 0)
return (old);
- ret = func(new) != 0 ? NULL : new;
- if (ret == NULL) {
- saverr = errno;
- if (func(old) != 0 && func("C") == 0)
- (void)strcpy(old, "C");
- errno = saverr;
- } else
+ if (func(new) != _LDP_ERROR) {
(void)strcpy(old, new);
+ return (old);
+ }
- return (ret);
+ return (NULL);
}
diff --git a/lib/libc/locale/setrunelocale.c b/lib/libc/locale/setrunelocale.c
index 11d97bd..f1a01fc 100644
--- a/lib/libc/locale/setrunelocale.c
+++ b/lib/libc/locale/setrunelocale.c
@@ -54,12 +54,15 @@ extern int _MSKanji_init(_RuneLocale *);
extern _RuneLocale *_Read_RuneMagi(FILE *);
int
-setrunelocale(encoding)
- char *encoding;
+setrunelocale(char *encoding)
{
FILE *fp;
char name[PATH_MAX];
_RuneLocale *rl;
+ int saverr, ret;
+ static char ctype_encoding[ENCODING_LEN + 1];
+ static _RuneLocale *CachedRuneLocale;
+ static int Cached__mb_cur_max;
if (!encoding || !*encoding || strlen(encoding) > ENCODING_LEN ||
(encoding[0] == '.' &&
@@ -71,12 +74,25 @@ setrunelocale(encoding)
/*
* The "C" and "POSIX" locale are always here.
*/
- if (!strcmp(encoding, "C") || !strcmp(encoding, "POSIX")) {
+ if (strcmp(encoding, "C") == 0 || strcmp(encoding, "POSIX") == 0) {
_CurrentRuneLocale = &_DefaultRuneLocale;
__mb_cur_max = 1;
return (0);
}
+ /*
+ * If the locale name is the same as our cache, use the cache.
+ */
+ if (CachedRuneLocale != NULL &&
+ strcmp(encoding, ctype_encoding) == 0) {
+ _CurrentRuneLocale = CachedRuneLocale;
+ __mb_cur_max = Cached__mb_cur_max;
+ return (0);
+ }
+
+ /*
+ * Slurp the locale file into the cache.
+ */
if (_PathLocale == NULL) {
char *p = getenv("PATH_LOCALE");
@@ -90,7 +106,7 @@ setrunelocale(encoding)
return (ENAMETOOLONG);
_PathLocale = strdup(p);
if (_PathLocale == NULL)
- return (errno);
+ return (ENOMEM);
} else
_PathLocale = _PATH_LOCALE;
}
@@ -103,25 +119,38 @@ setrunelocale(encoding)
if ((fp = fopen(name, "r")) == NULL)
return (errno);
- if ((rl = _Read_RuneMagi(fp)) == 0) {
- fclose(fp);
- return (EFTYPE);
+ if ((rl = _Read_RuneMagi(fp)) == NULL) {
+ saverr = errno;
+ (void)fclose(fp);
+ return (saverr);
}
- fclose(fp);
+ (void)fclose(fp);
- if (!rl->encoding[0])
- return (EFTYPE);
- else if (!strcmp(rl->encoding, "NONE"))
- return (_none_init(rl));
- else if (!strcmp(rl->encoding, "UTF2"))
- return (_UTF2_init(rl));
- else if (!strcmp(rl->encoding, "EUC"))
- return (_EUC_init(rl));
- else if (!strcmp(rl->encoding, "BIG5"))
- return (_BIG5_init(rl));
- else if (!strcmp(rl->encoding, "MSKanji"))
- return (_MSKanji_init(rl));
+ if (strcmp(rl->encoding, "NONE") == 0)
+ ret = _none_init(rl);
+ else if (strcmp(rl->encoding, "UTF2") == 0)
+ ret = _UTF2_init(rl);
+ else if (strcmp(rl->encoding, "EUC") == 0)
+ ret = _EUC_init(rl);
+ else if (strcmp(rl->encoding, "BIG5") == 0)
+ ret = _BIG5_init(rl);
+ else if (strcmp(rl->encoding, "MSKanji") == 0)
+ ret = _MSKanji_init(rl);
else
- return (EFTYPE);
+ ret = EFTYPE;
+ if (ret == 0) {
+ if (CachedRuneLocale != NULL) {
+ /* See euc.c */
+ if (strcmp(CachedRuneLocale->encoding, "EUC") == 0)
+ free(CachedRuneLocale->variable);
+ free(CachedRuneLocale);
+ }
+ CachedRuneLocale = _CurrentRuneLocale;
+ Cached__mb_cur_max = __mb_cur_max;
+ (void)strcpy(ctype_encoding, encoding);
+ } else
+ free(rl);
+
+ return (ret);
}
OpenPOWER on IntegriCloud