/* Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc. This file is part of the GNU C Library. 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. */ #include #include #include #include #include #include #if defined(__linux__) #include #include #include #include extern CONST unsigned short int __ctype_b_C[]; extern CONST unsigned char __ctype_tolower_C[]; extern CONST unsigned char __ctype_toupper_C[]; extern CONST struct ctype_info __ctype_C; extern CONST struct collate_info __collate_C; extern CONST struct monetary_info __monetary_C; extern CONST struct numeric_info __numeric_C; extern CONST struct response_info __response_C; extern CONST struct time_info __time_C; extern CONST unsigned short int __ctype_b_ISO_8859_1[]; extern CONST unsigned char __ctype_tolower_ISO_8859_1[]; extern CONST unsigned char __ctype_toupper_ISO_8859_1[]; extern CONST struct ctype_info __ctype_ISO_8859_1; extern CONST unsigned short int __ctype_b_KOI_8[]; extern CONST unsigned char __ctype_tolower_KOI_8[]; extern CONST unsigned char __ctype_toupper_KOI_8[]; extern CONST struct ctype_info __ctype_KOI_8; static int loc_open __P ((CONST unsigned char *, CONST unsigned char *)); static int guard_check __P ((CONST int, CONST unsigned char *)); static int loc_rdline __P ((CONST int, unsigned char *, CONST int, unsigned char **[])); static int loc_collate __P ((CONST int, CONST unsigned char *)); static int loc_ctype __P ((CONST int, CONST unsigned char *)); static int loc_monetary __P ((CONST int, CONST unsigned char *)); static int loc_numeric __P ((CONST int, CONST unsigned char *)); static int loc_time __P ((CONST int, CONST unsigned char *)); static int loc_response __P ((CONST int, CONST unsigned char *)); #endif /* __linux__ */ /* Switch to the locale called NAME in CATEGORY. Return a string describing the locale. This string can be used as the NAME argument in a later call. If NAME is NULL, don't switch locales, but return the current one. If NAME is "", switch to a locale based on the environment variables, as per POSIX. Return NULL on error. */ char * DEFUN (setlocale, (category, name), int category AND CONST char *name) { #if defined(__linux__) /* Don't ask me why I did this. H.J. */ /* Fixed up setlocale() by Mitch (m.dsouza@mrc-apu.cam.ac.uk) */ /* Nickolay Saukh was here */ /* as was Steve Robbins */ static struct message_struct { int category; CONST char *name; char *locale; char *saved_locale; } ms[] = { { LC_COLLATE, "LC_COLLATE", "C" }, { LC_CTYPE, "LC_CTYPE", "C" }, { LC_MONETARY, "LC_MONETARY", "C" }, { LC_NUMERIC, "LC_NUMERIC", "C" }, { LC_TIME, "LC_TIME", "C" }, { LC_RESPONSE, "LC_RESPONSE", "C" }, { LC_MESSAGES, "LC_MESSAGES", "C" }, /* * New categories go in between here. */ { LC_ALL, "LC_ALL", "C" }, }; #define SIZE_MS (sizeof (ms) / sizeof (struct message_struct)) int i, j, len; char *ptr; if (name == NULL) { /* We just asking for current settings */ for (i = 0; i < SIZE_MS; i++) { if (category == ms[i].category) return (char *) ms[i].locale; } errno = EINVAL; return NULL; /* Not a valid category */ } /* * What the category asked (excluding LC_ALL)? */ if (category != LC_ALL) { for (i = 0; i < SIZE_MS - 1; i++) { if (category != ms[i].category) continue; /* If "" is given as the locale then we check environment vars */ if (*name == '\0') { if ((ptr = getenv ("LC_ALL")) == NULL && (ptr = getenv (ms[i].name)) == NULL && (ptr = getenv ("LANG")) == NULL) { /* * Can't find a relevant variable; POSIX says the result * is implementation-dependent. Let's just return the * previous setting, so that setlocale (LC_ALL, "") doesn't * always fail. */ return (char *) ms[i].locale; } } else ptr = (char *)name; if (!strcmp (ptr, ms[i].locale)) return (char *) ms[i].locale; #define C_LOCALE (!strcmp (ptr, "C") || !strcmp (ptr, "POSIX")) switch (category) { int fd; case LC_COLLATE: if (C_LOCALE) { _collate_info = &__collate_C; } else if ((fd = loc_open (ptr, ms[i].name)) < 0 || !loc_collate (fd, ptr)) { errno = ENOENT; return NULL; /* Unknown locale */ } break; case LC_CTYPE: if (C_LOCALE) { __ctype_b = __ctype_b_C + 1; __ctype_tolower = __ctype_tolower_C + 1; __ctype_toupper = __ctype_toupper_C + 1; _ctype_info = &__ctype_C; } else if (!strcmp (ptr, "ISO-8859-1")) { __ctype_b = __ctype_b_ISO_8859_1 + 1; __ctype_tolower = __ctype_tolower_ISO_8859_1 + 1; __ctype_toupper = __ctype_toupper_ISO_8859_1 + 1; _ctype_info = &__ctype_ISO_8859_1; } else if (!strcmp (ptr, "koi8-r")) { __ctype_b = __ctype_b_KOI_8 + 1; __ctype_tolower = __ctype_tolower_KOI_8 + 1; __ctype_toupper = __ctype_toupper_KOI_8 + 1; _ctype_info = &__ctype_KOI_8; } else if ((fd = loc_open (ptr, ms[i].name)) < 0 || !loc_ctype (fd, ptr)) { errno = ENOENT; return NULL; /* Unknown locale */ } break; case LC_MONETARY: if (C_LOCALE) { _monetary_info = &__monetary_C; } else if ((fd = loc_open (ptr, ms[i].name)) < 0 || !loc_monetary (fd, ptr)) { errno = ENOENT; return NULL; /* Unknown locale */ } break; case LC_NUMERIC: if (C_LOCALE) { _numeric_info = &__numeric_C; } else if ((fd = loc_open (ptr, ms[i].name)) < 0 || !loc_numeric (fd, ptr)) { errno = ENOENT; return NULL; /* Unknown locale */ } break; case LC_TIME: if (C_LOCALE) { _time_info = &__time_C; } else if ((fd = loc_open (ptr, ms[i].name)) < 0 || !loc_time (fd, ptr)) { errno = ENOENT; return NULL; /* Unknown locale */ } break; case LC_RESPONSE: if (C_LOCALE) { _response_info = &__response_C; } else if ((fd = loc_open (ptr, ms[i].name)) < 0 || !loc_response (fd, ptr)) { errno = ENOENT; return NULL; /* Unknown locale */ } break; case LC_MESSAGES: if (ptr == NULL) { errno = ENOENT; return NULL; /* Unknown locale */ } /* Some interaction with nls required to * ensure correct settings */ break; default: /* * Invalid category requested */ errno = EINVAL; return NULL; } if ((ms[i].locale = malloc (strlen (ptr) + 1)) == NULL) { errno = ENOMEM; return NULL; } strcpy (ms[i].locale, ptr); return (ms[i].locale); } } /* * If we here, then LC_ALL was requested * Save total length of all locale strings in 'len' */ len = 0; for (i = 0; i < SIZE_MS - 1; i++) { /* Save current setting */ ms[i].saved_locale = setlocale (ms[i].category, NULL); if ((ptr = setlocale (ms[i].category, name)) == NULL) { /* Oops! Recover original state */ int se = errno; for (j = 0; j < i; j++) (void) setlocale (ms[j].category, ms[j].saved_locale); errno = se; return NULL; } len += strlen (ptr) + 1; /* include one for the slash */ } /* if we here, then all individual locales were installed */ if ((ms[i].locale = malloc (len + 1)) == NULL) { errno = ENOMEM; return NULL; } *(ms[i].locale) = 0; for (j = 0; j < SIZE_MS - 1; ++j) { strcat (ms[i].locale, ms[j].locale); strcat (ms[i].locale, "/"); } return (char *) ms[i].locale; #else /* Braindead implementation until I finish the fancy one. */ if (name == NULL || name[0] == '\0') return (char *) "C"; if (!strcmp (name, "C") || !strcmp (name, "POSIX")) return (char *) name; errno = EINVAL; return NULL; #endif } #if defined(__linux__) && defined(USE_ISO_8859_1) static int DEFUN (loc_open, (loc_name, cat_name), CONST unsigned char *loc_name AND CONST unsigned char *cat_name) { unsigned char buffer[128]; (void) strcpy (buffer, _PATH_LOCALE); (void) strcat (buffer, "/"); (void) strcat (buffer, loc_name); (void) strcat (buffer, "/"); (void) strcat (buffer, cat_name); return open (buffer, O_RDONLY); } static int DEFUN (guard_check, (fd, cname), CONST int fd AND CONST unsigned char *cname) { #ifndef NOGUARD unsigned char fcname[256]; /* * Guard check (overkill?) */ fcname[sizeof (fcname) - 1] = '\0'; if (read (fd, fcname, sizeof (fcname) - 1) < strlen (cname) + 1) { (void) close (fd); return 0; } else if (strcmp (cname, fcname)) { (void) close (fd); return 0; } #endif return 1; } static int DEFUN (loc_collate, (fd, cname), CONST int fd AND CONST unsigned char *cname) { static unsigned char values[UCHAR_MAX + 1]; static unsigned char offsets[UCHAR_MAX + 1]; static struct collate_info cinfo = { 0, NULL, values, offsets }; if (read (fd, values, sizeof (values)) != sizeof (values)) { (void) close (fd); return 0; } if (read (fd, offsets, sizeof (offsets)) != sizeof (offsets)) { (void) close (fd); return 0; } if (read (fd, &cinfo.nsubsts, sizeof (cinfo.nsubsts)) != sizeof (cinfo.nsubsts)) { (void) close (fd); return 0; } /* * XXX: for a while a did not like substitutions */ if (cinfo.nsubsts) { (void) close (fd); return 0; } if (!guard_check (fd, cname)) return 0; (void) close (fd); _collate_info = &cinfo; return 1; } static int DEFUN (loc_ctype, (fd, cname), CONST int fd AND CONST unsigned char *cname) { /* * There is no urgent need for multibytes */ extern CONST struct ctype_mbchar_info __ctype_mbchar_C; static struct ctables { unsigned short int xtype[UCHAR_MAX + 2]; unsigned char xlower[UCHAR_MAX + 2]; unsigned char xupper[UCHAR_MAX + 2]; } ctables; static struct ctype_ctype_info c2info = { ctables.xtype, ctables.xlower, ctables.xupper }; static CONST struct ctype_info cinfo = { &c2info, (struct ctype_mbchar_info *) &__ctype_mbchar_C /* No multibytes!!! */ }; if (read (fd, &ctables, sizeof (ctables)) != sizeof (ctables)) { (void) close (fd); return 0; } if (!guard_check (fd, cname)) return 0; (void) close (fd); __ctype_b = ctables.xtype + 1; __ctype_tolower = ctables.xlower + 1; __ctype_toupper = ctables.xupper + 1; _ctype_info = &cinfo; return 1; } static int DEFUN (loc_rdline, (fd, buf, buflen, target), CONST int fd AND unsigned char *buf AND CONST int buflen AND unsigned char **target[]) { int i; short len; unsigned char *cp = buf; for (i = 0; target[i] != NULL; i++) { if (read (fd, &len, sizeof (len)) != sizeof (len) || len > buflen - (cp - buf) || read (fd, cp, len) != len || *(cp + len - 1) != '\0') { (void) close (fd); return 0; } *target[i] = cp; cp += len; } return 1; } static int DEFUN (loc_monetary, (fd, cname), CONST int fd AND CONST unsigned char *cname) { static struct monetary_info minfo; static unsigned char **lines[] = { (unsigned char **) &minfo.int_curr_symbol, (unsigned char **) &minfo.currency_symbol, (unsigned char **) &minfo.mon_decimal_point, (unsigned char **) &minfo.mon_thousands_sep, (unsigned char **) &minfo.mon_grouping, (unsigned char **) &minfo.positive_sign, (unsigned char **) &minfo.negative_sign, NULL }; static unsigned char mbuffer[128]; /* Hope it long enough */ if (!loc_rdline (fd, mbuffer, sizeof (mbuffer), lines)) return 0; #define fclen (((&minfo.n_sign_posn) - (&minfo.int_frac_digits)) + sizeof (minfo.n_sign_posn)) if (read (fd, &minfo.int_frac_digits, fclen) != fclen) { (void) close (fd); return 0; } #undef fclen if (!guard_check (fd, cname)) return 0; (void) close (fd); _monetary_info = &minfo; return 1; } static int DEFUN (loc_numeric, (fd, cname), CONST int fd AND CONST unsigned char *cname) { static struct numeric_info ninfo; static unsigned char **lines[] = { (unsigned char **) &ninfo.decimal_point, (unsigned char **) &ninfo.thousands_sep, (unsigned char **) &ninfo.grouping, NULL }; static unsigned char nbuffer[64]; /* Hope it long enough */ if (!loc_rdline (fd, nbuffer, sizeof (nbuffer), lines)) return 0; if (!guard_check (fd, cname)) return 0; (void) close (fd); _numeric_info = &ninfo; return 1; } static int DEFUN (loc_time, (fd, cname), CONST int fd AND CONST unsigned char *cname) { static struct time_info tinfo; static unsigned char **lines[] = { (unsigned char **) &tinfo.abbrev_wkday[0], (unsigned char **) &tinfo.abbrev_wkday[1], (unsigned char **) &tinfo.abbrev_wkday[2], (unsigned char **) &tinfo.abbrev_wkday[3], (unsigned char **) &tinfo.abbrev_wkday[4], (unsigned char **) &tinfo.abbrev_wkday[5], (unsigned char **) &tinfo.abbrev_wkday[6], (unsigned char **) &tinfo.full_wkday[0], (unsigned char **) &tinfo.full_wkday[1], (unsigned char **) &tinfo.full_wkday[2], (unsigned char **) &tinfo.full_wkday[3], (unsigned char **) &tinfo.full_wkday[4], (unsigned char **) &tinfo.full_wkday[5], (unsigned char **) &tinfo.full_wkday[6], (unsigned char **) &tinfo.abbrev_month[0], (unsigned char **) &tinfo.abbrev_month[1], (unsigned char **) &tinfo.abbrev_month[2], (unsigned char **) &tinfo.abbrev_month[3], (unsigned char **) &tinfo.abbrev_month[4], (unsigned char **) &tinfo.abbrev_month[5], (unsigned char **) &tinfo.abbrev_month[6], (unsigned char **) &tinfo.abbrev_month[7], (unsigned char **) &tinfo.abbrev_month[8], (unsigned char **) &tinfo.abbrev_month[9], (unsigned char **) &tinfo.abbrev_month[10], (unsigned char **) &tinfo.abbrev_month[11], (unsigned char **) &tinfo.full_month[0], (unsigned char **) &tinfo.full_month[1], (unsigned char **) &tinfo.full_month[2], (unsigned char **) &tinfo.full_month[3], (unsigned char **) &tinfo.full_month[4], (unsigned char **) &tinfo.full_month[5], (unsigned char **) &tinfo.full_month[6], (unsigned char **) &tinfo.full_month[7], (unsigned char **) &tinfo.full_month[8], (unsigned char **) &tinfo.full_month[9], (unsigned char **) &tinfo.full_month[10], (unsigned char **) &tinfo.full_month[11], (unsigned char **) &tinfo.ampm[0], (unsigned char **) &tinfo.ampm[1], (unsigned char **) &tinfo.date_time, (unsigned char **) &tinfo.date, (unsigned char **) &tinfo.time, (unsigned char **) &tinfo.ut0, (unsigned char **) &tinfo.tz, NULL }; static unsigned char tbuffer[1024]; /* Hope it long enough */ if (!loc_rdline (fd, tbuffer, sizeof (tbuffer), lines)) return 0; if (!guard_check (fd, cname)) return 0; (void) close (fd); _time_info = &tinfo; return 1; } static int DEFUN (loc_response, (fd, cname), CONST int fd AND CONST unsigned char *cname) { static struct response_info rinfo; static unsigned char **lines[] = { (unsigned char **) &rinfo.yesexpr, (unsigned char **) &rinfo.noexpr, NULL }; static unsigned char rbuffer[64]; /* Hope it long enough */ if (!loc_rdline (fd, rbuffer, sizeof (rbuffer), lines)) return 0; if (!guard_check (fd, cname)) return 0; (void) close (fd); _response_info = &rinfo; return 1; } #endif