1 1.9 ginsbach /* $NetBSD: locale.c,v 1.9 2020/04/28 22:29:32 ginsbach Exp $ */ 2 1.1 tshiozak 3 1.1 tshiozak /*- 4 1.1 tshiozak * Copyright (c) 2002, 2003 Alexey Zelkin <phantom (at) FreeBSD.org> 5 1.1 tshiozak * All rights reserved. 6 1.1 tshiozak * 7 1.1 tshiozak * Redistribution and use in source and binary forms, with or without 8 1.1 tshiozak * modification, are permitted provided that the following conditions 9 1.1 tshiozak * are met: 10 1.1 tshiozak * 1. Redistributions of source code must retain the above copyright 11 1.1 tshiozak * notice, this list of conditions and the following disclaimer. 12 1.1 tshiozak * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 tshiozak * notice, this list of conditions and the following disclaimer in the 14 1.1 tshiozak * documentation and/or other materials provided with the distribution. 15 1.1 tshiozak * 16 1.1 tshiozak * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 1.1 tshiozak * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 tshiozak * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 tshiozak * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 1.1 tshiozak * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 tshiozak * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 tshiozak * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 tshiozak * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 tshiozak * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 tshiozak * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 tshiozak * SUCH DAMAGE. 27 1.1 tshiozak * 28 1.1 tshiozak * FreeBSD: src/usr.bin/locale/locale.c,v 1.10 2003/06/26 11:05:56 phantom Exp 29 1.1 tshiozak */ 30 1.1 tshiozak 31 1.1 tshiozak #include <sys/cdefs.h> 32 1.1 tshiozak #if defined(LIBC_SCCS) && !defined(lint) 33 1.9 ginsbach __RCSID("$NetBSD: locale.c,v 1.9 2020/04/28 22:29:32 ginsbach Exp $"); 34 1.1 tshiozak #endif /* LIBC_SCCS and not lint */ 35 1.1 tshiozak 36 1.1 tshiozak /* 37 1.1 tshiozak * XXX: implement missing era_* (LC_TIME) keywords (require libc & 38 1.1 tshiozak * nl_langinfo(3) extensions) 39 1.1 tshiozak * 40 1.1 tshiozak * XXX: correctly handle reserved 'charmap' keyword and '-m' option (require 41 1.1 tshiozak * localedef(1) implementation). Currently it's handled via 42 1.1 tshiozak * nl_langinfo(CODESET). 43 1.1 tshiozak */ 44 1.1 tshiozak 45 1.1 tshiozak #include <sys/types.h> 46 1.5 tnozaki #include <assert.h> 47 1.1 tshiozak #include <dirent.h> 48 1.1 tshiozak #include <err.h> 49 1.1 tshiozak #include <locale.h> 50 1.1 tshiozak #include <langinfo.h> 51 1.5 tnozaki #include <limits.h> 52 1.1 tshiozak #include <paths.h> 53 1.1 tshiozak #include <stdio.h> 54 1.1 tshiozak #include <stdlib.h> 55 1.1 tshiozak #include <string.h> 56 1.1 tshiozak #include <stringlist.h> 57 1.1 tshiozak #include <unistd.h> 58 1.1 tshiozak 59 1.5 tnozaki #include "citrus_namespace.h" 60 1.5 tnozaki #include "citrus_region.h" 61 1.5 tnozaki #include "citrus_lookup.h" 62 1.6 tnozaki #include "setlocale_local.h" 63 1.5 tnozaki 64 1.1 tshiozak /* Local prototypes */ 65 1.1 tshiozak void init_locales_list(void); 66 1.5 tnozaki void init_locales_list_alias(void); 67 1.1 tshiozak void list_charmaps(void); 68 1.1 tshiozak void list_locales(void); 69 1.1 tshiozak const char *lookup_localecat(int); 70 1.1 tshiozak char *kwval_lconv(int); 71 1.1 tshiozak int kwval_lookup(char *, char **, int *, int *); 72 1.1 tshiozak void showdetails(char *); 73 1.1 tshiozak void showkeywordslist(void); 74 1.1 tshiozak void showlocale(void); 75 1.1 tshiozak void usage(void); 76 1.1 tshiozak 77 1.1 tshiozak /* Global variables */ 78 1.1 tshiozak static StringList *locales = NULL; 79 1.1 tshiozak 80 1.1 tshiozak int all_locales = 0; 81 1.1 tshiozak int all_charmaps = 0; 82 1.1 tshiozak int prt_categories = 0; 83 1.1 tshiozak int prt_keywords = 0; 84 1.1 tshiozak int more_params = 0; 85 1.1 tshiozak 86 1.1 tshiozak struct _lcinfo { 87 1.1 tshiozak const char *name; 88 1.1 tshiozak int id; 89 1.1 tshiozak } lcinfo [] = { 90 1.1 tshiozak { "LC_CTYPE", LC_CTYPE }, 91 1.1 tshiozak { "LC_COLLATE", LC_COLLATE }, 92 1.1 tshiozak { "LC_TIME", LC_TIME }, 93 1.1 tshiozak { "LC_NUMERIC", LC_NUMERIC }, 94 1.1 tshiozak { "LC_MONETARY", LC_MONETARY }, 95 1.1 tshiozak { "LC_MESSAGES", LC_MESSAGES } 96 1.1 tshiozak }; 97 1.1 tshiozak #define NLCINFO (sizeof(lcinfo)/sizeof(lcinfo[0])) 98 1.1 tshiozak 99 1.1 tshiozak /* ids for values not referenced by nl_langinfo() */ 100 1.1 tshiozak #define KW_ZERO 10000 101 1.1 tshiozak #define KW_GROUPING (KW_ZERO+1) 102 1.1 tshiozak #define KW_INT_CURR_SYMBOL (KW_ZERO+2) 103 1.1 tshiozak #define KW_CURRENCY_SYMBOL (KW_ZERO+3) 104 1.1 tshiozak #define KW_MON_DECIMAL_POINT (KW_ZERO+4) 105 1.1 tshiozak #define KW_MON_THOUSANDS_SEP (KW_ZERO+5) 106 1.1 tshiozak #define KW_MON_GROUPING (KW_ZERO+6) 107 1.1 tshiozak #define KW_POSITIVE_SIGN (KW_ZERO+7) 108 1.1 tshiozak #define KW_NEGATIVE_SIGN (KW_ZERO+8) 109 1.1 tshiozak #define KW_INT_FRAC_DIGITS (KW_ZERO+9) 110 1.1 tshiozak #define KW_FRAC_DIGITS (KW_ZERO+10) 111 1.1 tshiozak #define KW_P_CS_PRECEDES (KW_ZERO+11) 112 1.1 tshiozak #define KW_P_SEP_BY_SPACE (KW_ZERO+12) 113 1.1 tshiozak #define KW_N_CS_PRECEDES (KW_ZERO+13) 114 1.1 tshiozak #define KW_N_SEP_BY_SPACE (KW_ZERO+14) 115 1.1 tshiozak #define KW_P_SIGN_POSN (KW_ZERO+15) 116 1.1 tshiozak #define KW_N_SIGN_POSN (KW_ZERO+16) 117 1.1 tshiozak #define KW_INT_P_CS_PRECEDES (KW_ZERO+17) 118 1.1 tshiozak #define KW_INT_P_SEP_BY_SPACE (KW_ZERO+18) 119 1.1 tshiozak #define KW_INT_N_CS_PRECEDES (KW_ZERO+19) 120 1.1 tshiozak #define KW_INT_N_SEP_BY_SPACE (KW_ZERO+20) 121 1.1 tshiozak #define KW_INT_P_SIGN_POSN (KW_ZERO+21) 122 1.1 tshiozak #define KW_INT_N_SIGN_POSN (KW_ZERO+22) 123 1.1 tshiozak 124 1.1 tshiozak struct _kwinfo { 125 1.1 tshiozak const char *name; 126 1.1 tshiozak int isstr; /* true - string, false - number */ 127 1.1 tshiozak int catid; /* LC_* */ 128 1.1 tshiozak int value_ref; 129 1.1 tshiozak const char *comment; 130 1.1 tshiozak } kwinfo [] = { 131 1.1 tshiozak { "charmap", 1, LC_CTYPE, CODESET, "" }, /* hack */ 132 1.1 tshiozak 133 1.1 tshiozak { "decimal_point", 1, LC_NUMERIC, RADIXCHAR, "" }, 134 1.1 tshiozak { "thousands_sep", 1, LC_NUMERIC, THOUSEP, "" }, 135 1.1 tshiozak { "grouping", 1, LC_NUMERIC, KW_GROUPING, "" }, 136 1.1 tshiozak { "radixchar", 1, LC_NUMERIC, RADIXCHAR, 137 1.1 tshiozak "Same as decimal_point (BSD only)" }, /* compat */ 138 1.1 tshiozak { "thousep", 1, LC_NUMERIC, THOUSEP, 139 1.1 tshiozak "Same as thousands_sep (BSD only)" }, /* compat */ 140 1.1 tshiozak 141 1.1 tshiozak { "int_curr_symbol", 1, LC_MONETARY, KW_INT_CURR_SYMBOL, "" }, 142 1.1 tshiozak { "currency_symbol", 1, LC_MONETARY, KW_CURRENCY_SYMBOL, "" }, 143 1.1 tshiozak { "mon_decimal_point", 1, LC_MONETARY, KW_MON_DECIMAL_POINT, "" }, 144 1.1 tshiozak { "mon_thousands_sep", 1, LC_MONETARY, KW_MON_THOUSANDS_SEP, "" }, 145 1.1 tshiozak { "mon_grouping", 1, LC_MONETARY, KW_MON_GROUPING, "" }, 146 1.1 tshiozak { "positive_sign", 1, LC_MONETARY, KW_POSITIVE_SIGN, "" }, 147 1.1 tshiozak { "negative_sign", 1, LC_MONETARY, KW_NEGATIVE_SIGN, "" }, 148 1.1 tshiozak 149 1.1 tshiozak { "int_frac_digits", 0, LC_MONETARY, KW_INT_FRAC_DIGITS, "" }, 150 1.1 tshiozak { "frac_digits", 0, LC_MONETARY, KW_FRAC_DIGITS, "" }, 151 1.1 tshiozak { "p_cs_precedes", 0, LC_MONETARY, KW_P_CS_PRECEDES, "" }, 152 1.1 tshiozak { "p_sep_by_space", 0, LC_MONETARY, KW_P_SEP_BY_SPACE, "" }, 153 1.1 tshiozak { "n_cs_precedes", 0, LC_MONETARY, KW_N_CS_PRECEDES, "" }, 154 1.1 tshiozak { "n_sep_by_space", 0, LC_MONETARY, KW_N_SEP_BY_SPACE, "" }, 155 1.1 tshiozak { "p_sign_posn", 0, LC_MONETARY, KW_P_SIGN_POSN, "" }, 156 1.1 tshiozak { "n_sign_posn", 0, LC_MONETARY, KW_N_SIGN_POSN, "" }, 157 1.1 tshiozak { "int_p_cs_precedes", 0, LC_MONETARY, KW_INT_P_CS_PRECEDES, "" }, 158 1.1 tshiozak { "int_p_sep_by_space", 0, LC_MONETARY, KW_INT_P_SEP_BY_SPACE, "" }, 159 1.1 tshiozak { "int_n_cs_precedes", 0, LC_MONETARY, KW_INT_N_CS_PRECEDES, "" }, 160 1.1 tshiozak { "int_n_sep_by_space", 0, LC_MONETARY, KW_INT_N_SEP_BY_SPACE, "" }, 161 1.1 tshiozak { "int_p_sign_posn", 0, LC_MONETARY, KW_INT_P_SIGN_POSN, "" }, 162 1.1 tshiozak { "int_n_sign_posn", 0, LC_MONETARY, KW_INT_N_SIGN_POSN, "" }, 163 1.1 tshiozak 164 1.1 tshiozak { "d_t_fmt", 1, LC_TIME, D_T_FMT, "" }, 165 1.1 tshiozak { "d_fmt", 1, LC_TIME, D_FMT, "" }, 166 1.1 tshiozak { "t_fmt", 1, LC_TIME, T_FMT, "" }, 167 1.1 tshiozak { "am_str", 1, LC_TIME, AM_STR, "" }, 168 1.1 tshiozak { "pm_str", 1, LC_TIME, PM_STR, "" }, 169 1.1 tshiozak { "t_fmt_ampm", 1, LC_TIME, T_FMT_AMPM, "" }, 170 1.1 tshiozak { "day_1", 1, LC_TIME, DAY_1, "" }, 171 1.1 tshiozak { "day_2", 1, LC_TIME, DAY_2, "" }, 172 1.1 tshiozak { "day_3", 1, LC_TIME, DAY_3, "" }, 173 1.1 tshiozak { "day_4", 1, LC_TIME, DAY_4, "" }, 174 1.1 tshiozak { "day_5", 1, LC_TIME, DAY_5, "" }, 175 1.1 tshiozak { "day_6", 1, LC_TIME, DAY_6, "" }, 176 1.1 tshiozak { "day_7", 1, LC_TIME, DAY_7, "" }, 177 1.1 tshiozak { "abday_1", 1, LC_TIME, ABDAY_1, "" }, 178 1.1 tshiozak { "abday_2", 1, LC_TIME, ABDAY_2, "" }, 179 1.1 tshiozak { "abday_3", 1, LC_TIME, ABDAY_3, "" }, 180 1.1 tshiozak { "abday_4", 1, LC_TIME, ABDAY_4, "" }, 181 1.1 tshiozak { "abday_5", 1, LC_TIME, ABDAY_5, "" }, 182 1.1 tshiozak { "abday_6", 1, LC_TIME, ABDAY_6, "" }, 183 1.1 tshiozak { "abday_7", 1, LC_TIME, ABDAY_7, "" }, 184 1.1 tshiozak { "mon_1", 1, LC_TIME, MON_1, "" }, 185 1.1 tshiozak { "mon_2", 1, LC_TIME, MON_2, "" }, 186 1.1 tshiozak { "mon_3", 1, LC_TIME, MON_3, "" }, 187 1.1 tshiozak { "mon_4", 1, LC_TIME, MON_4, "" }, 188 1.1 tshiozak { "mon_5", 1, LC_TIME, MON_5, "" }, 189 1.1 tshiozak { "mon_6", 1, LC_TIME, MON_6, "" }, 190 1.1 tshiozak { "mon_7", 1, LC_TIME, MON_7, "" }, 191 1.1 tshiozak { "mon_8", 1, LC_TIME, MON_8, "" }, 192 1.1 tshiozak { "mon_9", 1, LC_TIME, MON_9, "" }, 193 1.1 tshiozak { "mon_10", 1, LC_TIME, MON_10, "" }, 194 1.1 tshiozak { "mon_11", 1, LC_TIME, MON_11, "" }, 195 1.1 tshiozak { "mon_12", 1, LC_TIME, MON_12, "" }, 196 1.1 tshiozak { "abmon_1", 1, LC_TIME, ABMON_1, "" }, 197 1.1 tshiozak { "abmon_2", 1, LC_TIME, ABMON_2, "" }, 198 1.1 tshiozak { "abmon_3", 1, LC_TIME, ABMON_3, "" }, 199 1.1 tshiozak { "abmon_4", 1, LC_TIME, ABMON_4, "" }, 200 1.1 tshiozak { "abmon_5", 1, LC_TIME, ABMON_5, "" }, 201 1.1 tshiozak { "abmon_6", 1, LC_TIME, ABMON_6, "" }, 202 1.1 tshiozak { "abmon_7", 1, LC_TIME, ABMON_7, "" }, 203 1.1 tshiozak { "abmon_8", 1, LC_TIME, ABMON_8, "" }, 204 1.1 tshiozak { "abmon_9", 1, LC_TIME, ABMON_9, "" }, 205 1.1 tshiozak { "abmon_10", 1, LC_TIME, ABMON_10, "" }, 206 1.1 tshiozak { "abmon_11", 1, LC_TIME, ABMON_11, "" }, 207 1.1 tshiozak { "abmon_12", 1, LC_TIME, ABMON_12, "" }, 208 1.1 tshiozak { "era", 1, LC_TIME, ERA, "(unavailable)" }, 209 1.1 tshiozak { "era_d_fmt", 1, LC_TIME, ERA_D_FMT, "(unavailable)" }, 210 1.1 tshiozak { "era_d_t_fmt", 1, LC_TIME, ERA_D_T_FMT, "(unavailable)" }, 211 1.1 tshiozak { "era_t_fmt", 1, LC_TIME, ERA_T_FMT, "(unavailable)" }, 212 1.1 tshiozak { "alt_digits", 1, LC_TIME, ALT_DIGITS, "" }, 213 1.1 tshiozak 214 1.1 tshiozak { "yesexpr", 1, LC_MESSAGES, YESEXPR, "" }, 215 1.1 tshiozak { "noexpr", 1, LC_MESSAGES, NOEXPR, "" }, 216 1.1 tshiozak { "yesstr", 1, LC_MESSAGES, YESSTR, 217 1.1 tshiozak "(POSIX legacy)" }, /* compat */ 218 1.1 tshiozak { "nostr", 1, LC_MESSAGES, NOSTR, 219 1.1 tshiozak "(POSIX legacy)" } /* compat */ 220 1.1 tshiozak 221 1.1 tshiozak }; 222 1.1 tshiozak #define NKWINFO (sizeof(kwinfo)/sizeof(kwinfo[0])) 223 1.1 tshiozak 224 1.1 tshiozak int 225 1.1 tshiozak main(int argc, char *argv[]) 226 1.1 tshiozak { 227 1.2 agc int ch; 228 1.1 tshiozak int tmp; 229 1.1 tshiozak 230 1.1 tshiozak while ((ch = getopt(argc, argv, "ackm")) != -1) { 231 1.1 tshiozak switch (ch) { 232 1.1 tshiozak case 'a': 233 1.1 tshiozak all_locales = 1; 234 1.1 tshiozak break; 235 1.1 tshiozak case 'c': 236 1.1 tshiozak prt_categories = 1; 237 1.1 tshiozak break; 238 1.1 tshiozak case 'k': 239 1.1 tshiozak prt_keywords = 1; 240 1.1 tshiozak break; 241 1.1 tshiozak case 'm': 242 1.1 tshiozak all_charmaps = 1; 243 1.1 tshiozak break; 244 1.1 tshiozak default: 245 1.1 tshiozak usage(); 246 1.1 tshiozak } 247 1.1 tshiozak } 248 1.1 tshiozak argc -= optind; 249 1.1 tshiozak argv += optind; 250 1.1 tshiozak 251 1.1 tshiozak /* validate arguments */ 252 1.1 tshiozak if (all_locales && all_charmaps) 253 1.1 tshiozak usage(); 254 1.9 ginsbach if ((all_locales || all_charmaps) && argc > 0) 255 1.1 tshiozak usage(); 256 1.1 tshiozak if ((all_locales || all_charmaps) && (prt_categories || prt_keywords)) 257 1.1 tshiozak usage(); 258 1.1 tshiozak if ((prt_categories || prt_keywords) && argc <= 0) 259 1.1 tshiozak usage(); 260 1.1 tshiozak 261 1.1 tshiozak /* process '-a' */ 262 1.1 tshiozak if (all_locales) { 263 1.1 tshiozak list_locales(); 264 1.1 tshiozak exit(0); 265 1.1 tshiozak } 266 1.1 tshiozak 267 1.1 tshiozak /* process '-m' */ 268 1.1 tshiozak if (all_charmaps) { 269 1.1 tshiozak list_charmaps(); 270 1.1 tshiozak exit(0); 271 1.1 tshiozak } 272 1.1 tshiozak 273 1.1 tshiozak /* check for special case '-k list' */ 274 1.1 tshiozak tmp = 0; 275 1.1 tshiozak if (prt_keywords && argc > 0) 276 1.1 tshiozak while (tmp < argc) 277 1.1 tshiozak if (strcasecmp(argv[tmp++], "list") == 0) { 278 1.1 tshiozak showkeywordslist(); 279 1.1 tshiozak exit(0); 280 1.1 tshiozak } 281 1.1 tshiozak 282 1.1 tshiozak /* process '-c' and/or '-k' */ 283 1.1 tshiozak if (prt_categories || prt_keywords || argc > 0) { 284 1.1 tshiozak setlocale(LC_ALL, ""); 285 1.1 tshiozak while (argc > 0) { 286 1.1 tshiozak showdetails(*argv); 287 1.1 tshiozak argv++; 288 1.1 tshiozak argc--; 289 1.1 tshiozak } 290 1.1 tshiozak exit(0); 291 1.1 tshiozak } 292 1.1 tshiozak 293 1.1 tshiozak /* no arguments, show current locale state */ 294 1.1 tshiozak showlocale(); 295 1.1 tshiozak 296 1.1 tshiozak return (0); 297 1.1 tshiozak } 298 1.1 tshiozak 299 1.1 tshiozak void 300 1.1 tshiozak usage(void) 301 1.1 tshiozak { 302 1.3 jmmv printf("usage: locale [ -a | -m ]\n" 303 1.1 tshiozak " locale [ -ck ] name ...\n"); 304 1.1 tshiozak exit(1); 305 1.1 tshiozak } 306 1.1 tshiozak 307 1.1 tshiozak /* 308 1.1 tshiozak * Output information about all available locales 309 1.1 tshiozak * 310 1.1 tshiozak * XXX actually output of this function does not guarantee that locale 311 1.1 tshiozak * is really available to application, since it can be broken or 312 1.1 tshiozak * inconsistent thus setlocale() will fail. Maybe add '-V' function to 313 1.1 tshiozak * also validate these locales? 314 1.1 tshiozak */ 315 1.1 tshiozak void 316 1.1 tshiozak list_locales(void) 317 1.1 tshiozak { 318 1.1 tshiozak size_t i; 319 1.1 tshiozak 320 1.1 tshiozak init_locales_list(); 321 1.1 tshiozak for (i = 0; i < locales->sl_cur; i++) { 322 1.1 tshiozak printf("%s\n", locales->sl_str[i]); 323 1.1 tshiozak } 324 1.1 tshiozak } 325 1.1 tshiozak 326 1.1 tshiozak /* 327 1.1 tshiozak * qsort() helper function 328 1.1 tshiozak */ 329 1.1 tshiozak static int 330 1.1 tshiozak scmp(const void *s1, const void *s2) 331 1.1 tshiozak { 332 1.1 tshiozak return strcmp(*(const char **)s1, *(const char **)s2); 333 1.1 tshiozak } 334 1.1 tshiozak 335 1.1 tshiozak /* 336 1.1 tshiozak * Output information about all available charmaps 337 1.1 tshiozak * 338 1.9 ginsbach * XXX this function is doing a task in hackish way, i.e. by scanning 339 1.9 ginsbach * list of locales, splitting their codeset part and building list of 340 1.1 tshiozak * them. 341 1.1 tshiozak */ 342 1.1 tshiozak void 343 1.1 tshiozak list_charmaps(void) 344 1.1 tshiozak { 345 1.1 tshiozak size_t i; 346 1.1 tshiozak char *s, *cs; 347 1.1 tshiozak StringList *charmaps; 348 1.1 tshiozak 349 1.1 tshiozak /* initialize StringList */ 350 1.1 tshiozak charmaps = sl_init(); 351 1.1 tshiozak if (charmaps == NULL) 352 1.1 tshiozak err(1, "could not allocate memory"); 353 1.1 tshiozak 354 1.1 tshiozak /* fetch locales list */ 355 1.1 tshiozak init_locales_list(); 356 1.1 tshiozak 357 1.1 tshiozak /* split codesets and build their list */ 358 1.1 tshiozak for (i = 0; i < locales->sl_cur; i++) { 359 1.1 tshiozak s = locales->sl_str[i]; 360 1.1 tshiozak if ((cs = strchr(s, '.')) != NULL) { 361 1.1 tshiozak cs++; 362 1.1 tshiozak if (sl_find(charmaps, cs) == NULL) 363 1.1 tshiozak sl_add(charmaps, cs); 364 1.1 tshiozak } 365 1.1 tshiozak } 366 1.1 tshiozak 367 1.1 tshiozak /* add US-ASCII, if not yet added */ 368 1.1 tshiozak if (sl_find(charmaps, "US-ASCII") == NULL) 369 1.1 tshiozak sl_add(charmaps, "US-ASCII"); 370 1.1 tshiozak 371 1.1 tshiozak /* sort the list */ 372 1.1 tshiozak qsort(charmaps->sl_str, charmaps->sl_cur, sizeof(char *), scmp); 373 1.1 tshiozak 374 1.1 tshiozak /* print results */ 375 1.1 tshiozak for (i = 0; i < charmaps->sl_cur; i++) { 376 1.1 tshiozak printf("%s\n", charmaps->sl_str[i]); 377 1.1 tshiozak } 378 1.1 tshiozak } 379 1.1 tshiozak 380 1.1 tshiozak /* 381 1.1 tshiozak * Retrieve sorted list of system locales (or user locales, if PATH_LOCALE 382 1.1 tshiozak * environment variable is set) 383 1.1 tshiozak */ 384 1.1 tshiozak void 385 1.1 tshiozak init_locales_list(void) 386 1.1 tshiozak { 387 1.1 tshiozak DIR *dirp; 388 1.1 tshiozak struct dirent *dp; 389 1.5 tnozaki char *s; 390 1.1 tshiozak 391 1.1 tshiozak /* why call this function twice ? */ 392 1.1 tshiozak if (locales != NULL) 393 1.1 tshiozak return; 394 1.1 tshiozak 395 1.1 tshiozak /* initialize StringList */ 396 1.1 tshiozak locales = sl_init(); 397 1.1 tshiozak if (locales == NULL) 398 1.1 tshiozak err(1, "could not allocate memory"); 399 1.1 tshiozak 400 1.1 tshiozak /* get actual locales directory name */ 401 1.1 tshiozak setlocale(LC_CTYPE, "C"); 402 1.1 tshiozak if (_PathLocale == NULL) 403 1.1 tshiozak errx(1, "unable to find locales storage"); 404 1.1 tshiozak 405 1.1 tshiozak /* open locales directory */ 406 1.1 tshiozak dirp = opendir(_PathLocale); 407 1.1 tshiozak if (dirp == NULL) 408 1.1 tshiozak err(1, "could not open directory '%s'", _PathLocale); 409 1.1 tshiozak 410 1.1 tshiozak /* scan directory and store its contents except "." and ".." */ 411 1.1 tshiozak while ((dp = readdir(dirp)) != NULL) { 412 1.5 tnozaki /* exclude "." and "..", _LOCALE_ALIAS_NAME */ 413 1.5 tnozaki if ((dp->d_name[0] != '.' || (dp->d_name[1] != '\0' && 414 1.5 tnozaki (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) && 415 1.5 tnozaki strcmp(_LOCALE_ALIAS_NAME, dp->d_name) != 0) { 416 1.5 tnozaki s = strdup(dp->d_name); 417 1.5 tnozaki if (s == NULL) 418 1.5 tnozaki err(1, "could not allocate memory"); 419 1.5 tnozaki sl_add(locales, s); 420 1.5 tnozaki } 421 1.1 tshiozak } 422 1.1 tshiozak closedir(dirp); 423 1.1 tshiozak 424 1.1 tshiozak /* make sure that 'POSIX' and 'C' locales are present in the list. 425 1.1 tshiozak * POSIX 1003.1-2001 requires presence of 'POSIX' name only here, but 426 1.9 ginsbach * we also list 'C' for consistency 427 1.1 tshiozak */ 428 1.1 tshiozak if (sl_find(locales, "POSIX") == NULL) 429 1.1 tshiozak sl_add(locales, "POSIX"); 430 1.1 tshiozak 431 1.1 tshiozak if (sl_find(locales, "C") == NULL) 432 1.1 tshiozak sl_add(locales, "C"); 433 1.1 tshiozak 434 1.5 tnozaki init_locales_list_alias(); 435 1.5 tnozaki 436 1.1 tshiozak /* make output nicer, sort the list */ 437 1.1 tshiozak qsort(locales->sl_str, locales->sl_cur, sizeof(char *), scmp); 438 1.1 tshiozak } 439 1.1 tshiozak 440 1.5 tnozaki void 441 1.5 tnozaki init_locales_list_alias(void) 442 1.5 tnozaki { 443 1.5 tnozaki char aliaspath[PATH_MAX]; 444 1.5 tnozaki struct _lookup *hlookup; 445 1.5 tnozaki struct _region key, dat; 446 1.5 tnozaki size_t n; 447 1.5 tnozaki char *s, *t; 448 1.5 tnozaki 449 1.5 tnozaki _DIAGASSERT(locales != NULL); 450 1.5 tnozaki _DIAGASSERT(_PathLocale != NULL); 451 1.5 tnozaki 452 1.5 tnozaki (void)snprintf(aliaspath, sizeof(aliaspath), 453 1.5 tnozaki "%s/" _LOCALE_ALIAS_NAME, _PathLocale); 454 1.5 tnozaki 455 1.5 tnozaki if (_lookup_seq_open(&hlookup, aliaspath, 456 1.5 tnozaki _LOOKUP_CASE_SENSITIVE) == 0) { 457 1.5 tnozaki while (_lookup_seq_next(hlookup, &key, &dat) == 0) { 458 1.5 tnozaki n = _region_size((const struct _region *)&key); 459 1.5 tnozaki s = _region_head((const struct _region *)&key); 460 1.5 tnozaki for (t = s; n > 0 && *s!= '/'; --n, ++s); 461 1.5 tnozaki n = (size_t)(s - t); 462 1.7 hira s = malloc(n + 1); 463 1.5 tnozaki if (s == NULL) 464 1.5 tnozaki err(1, "could not allocate memory"); 465 1.5 tnozaki memcpy(s, t, n); 466 1.5 tnozaki s[n] = '\0'; 467 1.5 tnozaki if (sl_find(locales, s) == NULL) 468 1.5 tnozaki sl_add(locales, s); 469 1.5 tnozaki else 470 1.5 tnozaki free(s); 471 1.5 tnozaki } 472 1.5 tnozaki _lookup_seq_close(hlookup); 473 1.5 tnozaki } 474 1.5 tnozaki } 475 1.5 tnozaki 476 1.1 tshiozak /* 477 1.1 tshiozak * Show current locale status, depending on environment variables 478 1.1 tshiozak */ 479 1.1 tshiozak void 480 1.1 tshiozak showlocale(void) 481 1.1 tshiozak { 482 1.1 tshiozak size_t i; 483 1.1 tshiozak const char *lang, *vval, *eval; 484 1.1 tshiozak 485 1.1 tshiozak setlocale(LC_ALL, ""); 486 1.1 tshiozak 487 1.1 tshiozak lang = getenv("LANG"); 488 1.1 tshiozak if (lang == NULL) { 489 1.1 tshiozak lang = ""; 490 1.1 tshiozak } 491 1.4 jdolecek printf("LANG=\"%s\"\n", lang); 492 1.1 tshiozak /* XXX: if LANG is null, then set it to "C" to get implied values? */ 493 1.1 tshiozak 494 1.1 tshiozak for (i = 0; i < NLCINFO; i++) { 495 1.1 tshiozak vval = setlocale(lcinfo[i].id, NULL); 496 1.1 tshiozak eval = getenv(lcinfo[i].name); 497 1.1 tshiozak if (eval != NULL && !strcmp(eval, vval) 498 1.1 tshiozak && strcmp(lang, vval)) { 499 1.1 tshiozak /* 500 1.1 tshiozak * Appropriate environment variable set, its value 501 1.9 ginsbach * is valid and not overridden by LC_ALL 502 1.1 tshiozak * 503 1.1 tshiozak * XXX: possible side effect: if both LANG and 504 1.9 ginsbach * overridden environment variable are set into same 505 1.1 tshiozak * value, then it'll be assumed as 'implied' 506 1.1 tshiozak */ 507 1.4 jdolecek printf("%s=\"%s\"\n", lcinfo[i].name, vval); 508 1.1 tshiozak } else { 509 1.1 tshiozak printf("%s=\"%s\"\n", lcinfo[i].name, vval); 510 1.1 tshiozak } 511 1.1 tshiozak } 512 1.1 tshiozak 513 1.1 tshiozak vval = getenv("LC_ALL"); 514 1.1 tshiozak if (vval == NULL) { 515 1.1 tshiozak vval = ""; 516 1.1 tshiozak } 517 1.4 jdolecek printf("LC_ALL=\"%s\"\n", vval); 518 1.1 tshiozak } 519 1.1 tshiozak 520 1.1 tshiozak /* 521 1.1 tshiozak * keyword value lookup helper (via localeconv()) 522 1.1 tshiozak */ 523 1.1 tshiozak char * 524 1.1 tshiozak kwval_lconv(int id) 525 1.1 tshiozak { 526 1.1 tshiozak struct lconv *lc; 527 1.1 tshiozak char *rval; 528 1.1 tshiozak 529 1.1 tshiozak rval = NULL; 530 1.1 tshiozak lc = localeconv(); 531 1.1 tshiozak switch (id) { 532 1.1 tshiozak case KW_GROUPING: 533 1.1 tshiozak rval = lc->grouping; 534 1.1 tshiozak break; 535 1.1 tshiozak case KW_INT_CURR_SYMBOL: 536 1.1 tshiozak rval = lc->int_curr_symbol; 537 1.1 tshiozak break; 538 1.1 tshiozak case KW_CURRENCY_SYMBOL: 539 1.1 tshiozak rval = lc->currency_symbol; 540 1.1 tshiozak break; 541 1.1 tshiozak case KW_MON_DECIMAL_POINT: 542 1.1 tshiozak rval = lc->mon_decimal_point; 543 1.1 tshiozak break; 544 1.1 tshiozak case KW_MON_THOUSANDS_SEP: 545 1.1 tshiozak rval = lc->mon_thousands_sep; 546 1.1 tshiozak break; 547 1.1 tshiozak case KW_MON_GROUPING: 548 1.1 tshiozak rval = lc->mon_grouping; 549 1.1 tshiozak break; 550 1.1 tshiozak case KW_POSITIVE_SIGN: 551 1.1 tshiozak rval = lc->positive_sign; 552 1.1 tshiozak break; 553 1.1 tshiozak case KW_NEGATIVE_SIGN: 554 1.1 tshiozak rval = lc->negative_sign; 555 1.1 tshiozak break; 556 1.1 tshiozak case KW_INT_FRAC_DIGITS: 557 1.1 tshiozak rval = &(lc->int_frac_digits); 558 1.1 tshiozak break; 559 1.1 tshiozak case KW_FRAC_DIGITS: 560 1.1 tshiozak rval = &(lc->frac_digits); 561 1.1 tshiozak break; 562 1.1 tshiozak case KW_P_CS_PRECEDES: 563 1.1 tshiozak rval = &(lc->p_cs_precedes); 564 1.1 tshiozak break; 565 1.1 tshiozak case KW_P_SEP_BY_SPACE: 566 1.1 tshiozak rval = &(lc->p_sep_by_space); 567 1.1 tshiozak break; 568 1.1 tshiozak case KW_N_CS_PRECEDES: 569 1.1 tshiozak rval = &(lc->n_cs_precedes); 570 1.1 tshiozak break; 571 1.1 tshiozak case KW_N_SEP_BY_SPACE: 572 1.1 tshiozak rval = &(lc->n_sep_by_space); 573 1.1 tshiozak break; 574 1.1 tshiozak case KW_P_SIGN_POSN: 575 1.1 tshiozak rval = &(lc->p_sign_posn); 576 1.1 tshiozak break; 577 1.1 tshiozak case KW_N_SIGN_POSN: 578 1.1 tshiozak rval = &(lc->n_sign_posn); 579 1.1 tshiozak break; 580 1.1 tshiozak case KW_INT_P_CS_PRECEDES: 581 1.1 tshiozak rval = &(lc->int_p_cs_precedes); 582 1.1 tshiozak break; 583 1.1 tshiozak case KW_INT_P_SEP_BY_SPACE: 584 1.1 tshiozak rval = &(lc->int_p_sep_by_space); 585 1.1 tshiozak break; 586 1.1 tshiozak case KW_INT_N_CS_PRECEDES: 587 1.1 tshiozak rval = &(lc->int_n_cs_precedes); 588 1.1 tshiozak break; 589 1.1 tshiozak case KW_INT_N_SEP_BY_SPACE: 590 1.1 tshiozak rval = &(lc->int_n_sep_by_space); 591 1.1 tshiozak break; 592 1.1 tshiozak case KW_INT_P_SIGN_POSN: 593 1.1 tshiozak rval = &(lc->int_p_sign_posn); 594 1.1 tshiozak break; 595 1.1 tshiozak case KW_INT_N_SIGN_POSN: 596 1.1 tshiozak rval = &(lc->int_n_sign_posn); 597 1.1 tshiozak break; 598 1.1 tshiozak default: 599 1.1 tshiozak break; 600 1.1 tshiozak } 601 1.1 tshiozak return (rval); 602 1.1 tshiozak } 603 1.1 tshiozak 604 1.1 tshiozak /* 605 1.1 tshiozak * keyword value and properties lookup 606 1.1 tshiozak */ 607 1.1 tshiozak int 608 1.1 tshiozak kwval_lookup(char *kwname, char **kwval, int *cat, int *isstr) 609 1.1 tshiozak { 610 1.1 tshiozak int rval; 611 1.1 tshiozak size_t i; 612 1.1 tshiozak 613 1.1 tshiozak rval = 0; 614 1.1 tshiozak for (i = 0; i < NKWINFO; i++) { 615 1.1 tshiozak if (strcasecmp(kwname, kwinfo[i].name) == 0) { 616 1.1 tshiozak rval = 1; 617 1.1 tshiozak *cat = kwinfo[i].catid; 618 1.1 tshiozak *isstr = kwinfo[i].isstr; 619 1.1 tshiozak if (kwinfo[i].value_ref < KW_ZERO) { 620 1.1 tshiozak *kwval = nl_langinfo(kwinfo[i].value_ref); 621 1.1 tshiozak } else { 622 1.1 tshiozak *kwval = kwval_lconv(kwinfo[i].value_ref); 623 1.1 tshiozak } 624 1.1 tshiozak break; 625 1.1 tshiozak } 626 1.1 tshiozak } 627 1.1 tshiozak 628 1.1 tshiozak return (rval); 629 1.1 tshiozak } 630 1.1 tshiozak 631 1.1 tshiozak /* 632 1.1 tshiozak * Show details about requested keyword according to '-k' and/or '-c' 633 1.1 tshiozak * command line options specified. 634 1.1 tshiozak */ 635 1.1 tshiozak void 636 1.1 tshiozak showdetails(char *kw) 637 1.1 tshiozak { 638 1.1 tshiozak int isstr, cat, tmpval; 639 1.1 tshiozak char *kwval; 640 1.1 tshiozak 641 1.1 tshiozak if (kwval_lookup(kw, &kwval, &cat, &isstr) == 0) { 642 1.1 tshiozak /* 643 1.1 tshiozak * invalid keyword specified. 644 1.1 tshiozak * XXX: any actions? 645 1.1 tshiozak */ 646 1.1 tshiozak return; 647 1.1 tshiozak } 648 1.1 tshiozak 649 1.1 tshiozak if (prt_categories) { 650 1.1 tshiozak printf("%s\n", lookup_localecat(cat)); 651 1.1 tshiozak } 652 1.1 tshiozak 653 1.1 tshiozak if (prt_keywords) { 654 1.1 tshiozak if (isstr) { 655 1.1 tshiozak printf("%s=\"%s\"\n", kw, kwval); 656 1.1 tshiozak } else { 657 1.1 tshiozak tmpval = (char) *kwval; 658 1.1 tshiozak printf("%s=%d\n", kw, tmpval); 659 1.1 tshiozak } 660 1.1 tshiozak } 661 1.1 tshiozak 662 1.1 tshiozak if (!prt_categories && !prt_keywords) { 663 1.1 tshiozak if (isstr) { 664 1.1 tshiozak printf("%s\n", kwval); 665 1.1 tshiozak } else { 666 1.1 tshiozak tmpval = (char) *kwval; 667 1.1 tshiozak printf("%d\n", tmpval); 668 1.1 tshiozak } 669 1.1 tshiozak } 670 1.1 tshiozak } 671 1.1 tshiozak 672 1.1 tshiozak /* 673 1.1 tshiozak * Convert locale category id into string 674 1.1 tshiozak */ 675 1.1 tshiozak const char * 676 1.1 tshiozak lookup_localecat(int cat) 677 1.1 tshiozak { 678 1.1 tshiozak size_t i; 679 1.1 tshiozak 680 1.1 tshiozak for (i = 0; i < NLCINFO; i++) 681 1.1 tshiozak if (lcinfo[i].id == cat) { 682 1.1 tshiozak return (lcinfo[i].name); 683 1.1 tshiozak } 684 1.1 tshiozak return ("UNKNOWN"); 685 1.1 tshiozak } 686 1.1 tshiozak 687 1.1 tshiozak /* 688 1.1 tshiozak * Show list of keywords 689 1.1 tshiozak */ 690 1.1 tshiozak void 691 1.1 tshiozak showkeywordslist(void) 692 1.1 tshiozak { 693 1.1 tshiozak size_t i; 694 1.1 tshiozak 695 1.1 tshiozak #define FMT "%-20s %-12s %-7s %-20s\n" 696 1.1 tshiozak 697 1.1 tshiozak printf("List of available keywords\n\n"); 698 1.1 tshiozak printf(FMT, "Keyword", "Category", "Type", "Comment"); 699 1.1 tshiozak printf("-------------------- ------------ ------- --------------------\n"); 700 1.1 tshiozak for (i = 0; i < NKWINFO; i++) { 701 1.1 tshiozak printf(FMT, 702 1.1 tshiozak kwinfo[i].name, 703 1.1 tshiozak lookup_localecat(kwinfo[i].catid), 704 1.1 tshiozak (kwinfo[i].isstr == 0) ? "number" : "string", 705 1.1 tshiozak kwinfo[i].comment); 706 1.1 tshiozak } 707 1.1 tshiozak } 708