1 /* $OpenBSD: setlocale.c,v 1.30 2019/07/03 03:24:04 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <locale.h> 19 #include <stdlib.h> 20 #include <string.h> 21 22 #include "rune.h" 23 24 static void 25 freegl(char **oldgl) 26 { 27 int ic; 28 29 if (oldgl == NULL) 30 return; 31 for (ic = LC_ALL; ic < _LC_LAST; ic++) 32 free(oldgl[ic]); 33 free(oldgl); 34 } 35 36 static char ** 37 dupgl(char **oldgl) 38 { 39 char **newgl; 40 int ic; 41 42 if ((newgl = calloc(_LC_LAST, sizeof(*newgl))) == NULL) 43 return NULL; 44 for (ic = LC_ALL; ic < _LC_LAST; ic++) { 45 if ((newgl[ic] = strdup(ic == LC_ALL ? "" : 46 oldgl == NULL ? "C" : oldgl[ic])) == NULL) { 47 freegl(newgl); 48 return NULL; 49 } 50 } 51 return newgl; 52 } 53 54 static int 55 changegl(int category, const char *locname, char **gl) 56 { 57 char *cp; 58 59 if ((locname = _get_locname(category, locname)) == NULL || 60 (cp = strdup(locname)) == NULL) 61 return -1; 62 63 free(gl[category]); 64 gl[category] = cp; 65 return 0; 66 } 67 68 char * 69 setlocale(int category, const char *locname) 70 { 71 /* 72 * Even though only LC_CTYPE has any effect in the OpenBSD 73 * base system, store complete information about the global 74 * locale, such that third-party software can access it, 75 * both via setlocale(3) and via locale(1). 76 */ 77 static char global_locname[256]; 78 static char **global_locale; 79 80 char **newgl, *firstname, *nextname; 81 int ic; 82 83 if (category < LC_ALL || category >= _LC_LAST) 84 return NULL; 85 86 /* 87 * Change the global locale. 88 */ 89 if (locname != NULL) { 90 if ((newgl = dupgl(global_locale)) == NULL) 91 return NULL; 92 if (category == LC_ALL && strchr(locname, '/') != NULL) { 93 94 /* One value for each category. */ 95 if ((firstname = strdup(locname)) == NULL) { 96 freegl(newgl); 97 return NULL; 98 } 99 nextname = firstname; 100 for (ic = 1; ic < _LC_LAST; ic++) 101 if (nextname == NULL || changegl(ic, 102 strsep(&nextname, "/"), newgl) == -1) 103 break; 104 free(firstname); 105 if (ic < _LC_LAST || nextname != NULL) { 106 freegl(newgl); 107 return NULL; 108 } 109 } else { 110 111 /* One value only. */ 112 if (changegl(category, locname, newgl) == -1) { 113 freegl(newgl); 114 return NULL; 115 } 116 117 /* One common value for all categories. */ 118 if (category == LC_ALL) { 119 for (ic = 1; ic < _LC_LAST; ic++) { 120 if (changegl(ic, locname, 121 newgl) == -1) { 122 freegl(newgl); 123 return NULL; 124 } 125 } 126 } 127 } 128 } else 129 newgl = global_locale; 130 131 /* 132 * Assemble a string representation of the globale locale. 133 */ 134 135 /* setlocale(3) was never called with a non-NULL argument. */ 136 if (newgl == NULL) { 137 (void)strlcpy(global_locname, "C", sizeof(global_locname)); 138 goto done; 139 } 140 141 /* Individual category, or LC_ALL uniformly set. */ 142 if (category > LC_ALL || newgl[LC_ALL][0] != '\0') { 143 if (strlcpy(global_locname, newgl[category], 144 sizeof(global_locname)) >= sizeof(global_locname)) 145 global_locname[0] = '\0'; 146 goto done; 147 } 148 149 /* 150 * Check whether all categories agree and return either 151 * the single common name for all categories or a string 152 * listing the names for all categories. 153 */ 154 for (ic = 2; ic < _LC_LAST; ic++) 155 if (strcmp(newgl[ic], newgl[1]) != 0) 156 break; 157 if (ic == _LC_LAST) { 158 if (strlcpy(global_locname, newgl[1], 159 sizeof(global_locname)) >= sizeof(global_locname)) 160 global_locname[0] = '\0'; 161 } else { 162 ic = snprintf(global_locname, sizeof(global_locname), 163 "%s/%s/%s/%s/%s/%s", newgl[1], newgl[2], newgl[3], 164 newgl[4], newgl[5], newgl[6]); 165 if (ic < 0 || ic >= sizeof(global_locname)) 166 global_locname[0] = '\0'; 167 } 168 169 done: 170 if (locname != NULL) { 171 /* 172 * We can't replace the global locale earlier 173 * because we first have to make sure that we 174 * also have the memory required to report success. 175 */ 176 if (global_locname[0] != '\0') { 177 freegl(global_locale); 178 global_locale = newgl; 179 if (category == LC_ALL || category == LC_CTYPE) 180 _GlobalRuneLocale = 181 strchr(newgl[LC_CTYPE], '.') == NULL ? 182 &_DefaultRuneLocale : _Utf8RuneLocale; 183 } else { 184 freegl(newgl); 185 return NULL; 186 } 187 } 188 return global_locname; 189 } 190 DEF_STRONG(setlocale); 191