/* $OpenBSD: setlocale.c,v 1.30 2019/07/03 03:24:04 deraadt Exp $ */ /* * Copyright (c) 2017 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include "rune.h" static void freegl(char **oldgl) { int ic; if (oldgl == NULL) return; for (ic = LC_ALL; ic < _LC_LAST; ic++) free(oldgl[ic]); free(oldgl); } static char ** dupgl(char **oldgl) { char **newgl; int ic; if ((newgl = calloc(_LC_LAST, sizeof(*newgl))) == NULL) return NULL; for (ic = LC_ALL; ic < _LC_LAST; ic++) { if ((newgl[ic] = strdup(ic == LC_ALL ? "" : oldgl == NULL ? "C" : oldgl[ic])) == NULL) { freegl(newgl); return NULL; } } return newgl; } static int changegl(int category, const char *locname, char **gl) { char *cp; if ((locname = _get_locname(category, locname)) == NULL || (cp = strdup(locname)) == NULL) return -1; free(gl[category]); gl[category] = cp; return 0; } char * setlocale(int category, const char *locname) { /* * Even though only LC_CTYPE has any effect in the OpenBSD * base system, store complete information about the global * locale, such that third-party software can access it, * both via setlocale(3) and via locale(1). */ static char global_locname[256]; static char **global_locale; char **newgl, *firstname, *nextname; int ic; if (category < LC_ALL || category >= _LC_LAST) return NULL; /* * Change the global locale. */ if (locname != NULL) { if ((newgl = dupgl(global_locale)) == NULL) return NULL; if (category == LC_ALL && strchr(locname, '/') != NULL) { /* One value for each category. */ if ((firstname = strdup(locname)) == NULL) { freegl(newgl); return NULL; } nextname = firstname; for (ic = 1; ic < _LC_LAST; ic++) if (nextname == NULL || changegl(ic, strsep(&nextname, "/"), newgl) == -1) break; free(firstname); if (ic < _LC_LAST || nextname != NULL) { freegl(newgl); return NULL; } } else { /* One value only. */ if (changegl(category, locname, newgl) == -1) { freegl(newgl); return NULL; } /* One common value for all categories. */ if (category == LC_ALL) { for (ic = 1; ic < _LC_LAST; ic++) { if (changegl(ic, locname, newgl) == -1) { freegl(newgl); return NULL; } } } } } else newgl = global_locale; /* * Assemble a string representation of the globale locale. */ /* setlocale(3) was never called with a non-NULL argument. */ if (newgl == NULL) { (void)strlcpy(global_locname, "C", sizeof(global_locname)); goto done; } /* Individual category, or LC_ALL uniformly set. */ if (category > LC_ALL || newgl[LC_ALL][0] != '\0') { if (strlcpy(global_locname, newgl[category], sizeof(global_locname)) >= sizeof(global_locname)) global_locname[0] = '\0'; goto done; } /* * Check whether all categories agree and return either * the single common name for all categories or a string * listing the names for all categories. */ for (ic = 2; ic < _LC_LAST; ic++) if (strcmp(newgl[ic], newgl[1]) != 0) break; if (ic == _LC_LAST) { if (strlcpy(global_locname, newgl[1], sizeof(global_locname)) >= sizeof(global_locname)) global_locname[0] = '\0'; } else { ic = snprintf(global_locname, sizeof(global_locname), "%s/%s/%s/%s/%s/%s", newgl[1], newgl[2], newgl[3], newgl[4], newgl[5], newgl[6]); if (ic < 0 || ic >= sizeof(global_locname)) global_locname[0] = '\0'; } done: if (locname != NULL) { /* * We can't replace the global locale earlier * because we first have to make sure that we * also have the memory required to report success. */ if (global_locname[0] != '\0') { freegl(global_locale); global_locale = newgl; if (category == LC_ALL || category == LC_CTYPE) _GlobalRuneLocale = strchr(newgl[LC_CTYPE], '.') == NULL ? &_DefaultRuneLocale : _Utf8RuneLocale; } else { freegl(newgl); return NULL; } } return global_locname; } DEF_STRONG(setlocale);