1 /* $OpenBSD: setlocale.c,v 1.31 2024/08/18 02:20:29 guenther 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 <stdio.h> 20 #include <stdlib.h> 21 #include <string.h> 22 23 #include "rune.h" 24 25 static void 26 freegl(char **oldgl) 27 { 28 int ic; 29 30 if (oldgl == NULL) 31 return; 32 for (ic = LC_ALL; ic < _LC_LAST; ic++) 33 free(oldgl[ic]); 34 free(oldgl); 35 } 36 37 static char ** 38 dupgl(char **oldgl) 39 { 40 char **newgl; 41 int ic; 42 43 if ((newgl = calloc(_LC_LAST, sizeof(*newgl))) == NULL) 44 return NULL; 45 for (ic = LC_ALL; ic < _LC_LAST; ic++) { 46 if ((newgl[ic] = strdup(ic == LC_ALL ? "" : 47 oldgl == NULL ? "C" : oldgl[ic])) == NULL) { 48 freegl(newgl); 49 return NULL; 50 } 51 } 52 return newgl; 53 } 54 55 static int 56 changegl(int category, const char *locname, char **gl) 57 { 58 char *cp; 59 60 if ((locname = _get_locname(category, locname)) == NULL || 61 (cp = strdup(locname)) == NULL) 62 return -1; 63 64 free(gl[category]); 65 gl[category] = cp; 66 return 0; 67 } 68 69 char * 70 setlocale(int category, const char *locname) 71 { 72 /* 73 * Even though only LC_CTYPE has any effect in the OpenBSD 74 * base system, store complete information about the global 75 * locale, such that third-party software can access it, 76 * both via setlocale(3) and via locale(1). 77 */ 78 static char global_locname[256]; 79 static char **global_locale; 80 81 char **newgl, *firstname, *nextname; 82 int ic; 83 84 if (category < LC_ALL || category >= _LC_LAST) 85 return NULL; 86 87 /* 88 * Change the global locale. 89 */ 90 if (locname != NULL) { 91 if ((newgl = dupgl(global_locale)) == NULL) 92 return NULL; 93 if (category == LC_ALL && strchr(locname, '/') != NULL) { 94 95 /* One value for each category. */ 96 if ((firstname = strdup(locname)) == NULL) { 97 freegl(newgl); 98 return NULL; 99 } 100 nextname = firstname; 101 for (ic = 1; ic < _LC_LAST; ic++) 102 if (nextname == NULL || changegl(ic, 103 strsep(&nextname, "/"), newgl) == -1) 104 break; 105 free(firstname); 106 if (ic < _LC_LAST || nextname != NULL) { 107 freegl(newgl); 108 return NULL; 109 } 110 } else { 111 112 /* One value only. */ 113 if (changegl(category, locname, newgl) == -1) { 114 freegl(newgl); 115 return NULL; 116 } 117 118 /* One common value for all categories. */ 119 if (category == LC_ALL) { 120 for (ic = 1; ic < _LC_LAST; ic++) { 121 if (changegl(ic, locname, 122 newgl) == -1) { 123 freegl(newgl); 124 return NULL; 125 } 126 } 127 } 128 } 129 } else 130 newgl = global_locale; 131 132 /* 133 * Assemble a string representation of the globale locale. 134 */ 135 136 /* setlocale(3) was never called with a non-NULL argument. */ 137 if (newgl == NULL) { 138 (void)strlcpy(global_locname, "C", sizeof(global_locname)); 139 goto done; 140 } 141 142 /* Individual category, or LC_ALL uniformly set. */ 143 if (category > LC_ALL || newgl[LC_ALL][0] != '\0') { 144 if (strlcpy(global_locname, newgl[category], 145 sizeof(global_locname)) >= sizeof(global_locname)) 146 global_locname[0] = '\0'; 147 goto done; 148 } 149 150 /* 151 * Check whether all categories agree and return either 152 * the single common name for all categories or a string 153 * listing the names for all categories. 154 */ 155 for (ic = 2; ic < _LC_LAST; ic++) 156 if (strcmp(newgl[ic], newgl[1]) != 0) 157 break; 158 if (ic == _LC_LAST) { 159 if (strlcpy(global_locname, newgl[1], 160 sizeof(global_locname)) >= sizeof(global_locname)) 161 global_locname[0] = '\0'; 162 } else { 163 ic = snprintf(global_locname, sizeof(global_locname), 164 "%s/%s/%s/%s/%s/%s", newgl[1], newgl[2], newgl[3], 165 newgl[4], newgl[5], newgl[6]); 166 if (ic < 0 || ic >= sizeof(global_locname)) 167 global_locname[0] = '\0'; 168 } 169 170 done: 171 if (locname != NULL) { 172 /* 173 * We can't replace the global locale earlier 174 * because we first have to make sure that we 175 * also have the memory required to report success. 176 */ 177 if (global_locname[0] != '\0') { 178 freegl(global_locale); 179 global_locale = newgl; 180 if (category == LC_ALL || category == LC_CTYPE) 181 _GlobalRuneLocale = 182 strchr(newgl[LC_CTYPE], '.') == NULL ? 183 &_DefaultRuneLocale : _Utf8RuneLocale; 184 } else { 185 freegl(newgl); 186 return NULL; 187 } 188 } 189 return global_locname; 190 } 191 DEF_STRONG(setlocale); 192