1105239Sphantom/*- 2330449Seadler * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3330449Seadler * 4116612Sphantom * Copyright (c) 2002, 2003 Alexey Zelkin <phantom@FreeBSD.org> 5105239Sphantom * All rights reserved. 6105239Sphantom * 7105239Sphantom * Redistribution and use in source and binary forms, with or without 8105239Sphantom * modification, are permitted provided that the following conditions 9105239Sphantom * are met: 10105239Sphantom * 1. Redistributions of source code must retain the above copyright 11105239Sphantom * notice, this list of conditions and the following disclaimer. 12105239Sphantom * 2. Redistributions in binary form must reproduce the above copyright 13105239Sphantom * notice, this list of conditions and the following disclaimer in the 14105239Sphantom * documentation and/or other materials provided with the distribution. 15105239Sphantom * 16105239Sphantom * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17105239Sphantom * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18105239Sphantom * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19105239Sphantom * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20105239Sphantom * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21105239Sphantom * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22105239Sphantom * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23105239Sphantom * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24105239Sphantom * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25105239Sphantom * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26105239Sphantom * SUCH DAMAGE. 27105239Sphantom * 28105239Sphantom * $FreeBSD: stable/11/usr.bin/locale/locale.c 330449 2018-03-05 07:26:05Z eadler $ 29105239Sphantom */ 30105239Sphantom 31116612Sphantom/* 32116873Sphantom * XXX: implement missing era_* (LC_TIME) keywords (require libc & 33116873Sphantom * nl_langinfo(3) extensions) 34116612Sphantom * 35116612Sphantom * XXX: correctly handle reserved 'charmap' keyword and '-m' option (require 36242808Sgrog * localedef(1) implementation). Currently it's handled via 37116612Sphantom * nl_langinfo(CODESET). 38116612Sphantom */ 39116612Sphantom 40298169Sbapt#include <sys/param.h> 41105239Sphantom#include <sys/types.h> 42298169Sbapt 43105239Sphantom#include <dirent.h> 44116612Sphantom#include <err.h> 45309330Svangyzen#include <limits.h> 46105239Sphantom#include <locale.h> 47116612Sphantom#include <langinfo.h> 48105239Sphantom#include <stdio.h> 49105239Sphantom#include <stdlib.h> 50105239Sphantom#include <string.h> 51105239Sphantom#include <stringlist.h> 52105239Sphantom#include <unistd.h> 53116851Sphantom#include "setlocale.h" 54105239Sphantom 55105239Sphantom/* Local prototypes */ 56309330Svangyzenchar *format_grouping(const char *); 57116612Sphantomvoid init_locales_list(void); 58116876Sphantomvoid list_charmaps(void); 59105239Sphantomvoid list_locales(void); 60116675Sphantomconst char *lookup_localecat(int); 61116616Sphantomchar *kwval_lconv(int); 62310040Svangyzenint kwval_lookup(const char *, char **, int *, int *); 63310040Svangyzenvoid showdetails(const char *); 64197764Sedwinvoid showkeywordslist(char *substring); 65116612Sphantomvoid showlocale(void); 66116616Sphantomvoid usage(void); 67105239Sphantom 68105239Sphantom/* Global variables */ 69105239Sphantomstatic StringList *locales = NULL; 70105239Sphantom 71310040Svangyzenstatic int all_locales = 0; 72310040Svangyzenstatic int all_charmaps = 0; 73310040Svangyzenstatic int prt_categories = 0; 74310040Svangyzenstatic int prt_keywords = 0; 75105239Sphantom 76310040Svangyzenstatic const struct _lcinfo { 77116616Sphantom const char *name; 78116616Sphantom int id; 79105239Sphantom} lcinfo [] = { 80116616Sphantom { "LC_CTYPE", LC_CTYPE }, 81116616Sphantom { "LC_COLLATE", LC_COLLATE }, 82116612Sphantom { "LC_TIME", LC_TIME }, 83116612Sphantom { "LC_NUMERIC", LC_NUMERIC }, 84116612Sphantom { "LC_MONETARY", LC_MONETARY }, 85116612Sphantom { "LC_MESSAGES", LC_MESSAGES } 86105239Sphantom}; 87298169Sbapt#define NLCINFO nitems(lcinfo) 88105239Sphantom 89116612Sphantom/* ids for values not referenced by nl_langinfo() */ 90116612Sphantom#define KW_ZERO 10000 91116612Sphantom#define KW_GROUPING (KW_ZERO+1) 92242851Sgrog#define KW_INT_CURR_SYMBOL (KW_ZERO+2) 93242851Sgrog#define KW_CURRENCY_SYMBOL (KW_ZERO+3) 94242851Sgrog#define KW_MON_DECIMAL_POINT (KW_ZERO+4) 95242851Sgrog#define KW_MON_THOUSANDS_SEP (KW_ZERO+5) 96242851Sgrog#define KW_MON_GROUPING (KW_ZERO+6) 97242851Sgrog#define KW_POSITIVE_SIGN (KW_ZERO+7) 98242851Sgrog#define KW_NEGATIVE_SIGN (KW_ZERO+8) 99242851Sgrog#define KW_INT_FRAC_DIGITS (KW_ZERO+9) 100242851Sgrog#define KW_FRAC_DIGITS (KW_ZERO+10) 101242851Sgrog#define KW_P_CS_PRECEDES (KW_ZERO+11) 102242851Sgrog#define KW_P_SEP_BY_SPACE (KW_ZERO+12) 103242851Sgrog#define KW_N_CS_PRECEDES (KW_ZERO+13) 104242851Sgrog#define KW_N_SEP_BY_SPACE (KW_ZERO+14) 105242851Sgrog#define KW_P_SIGN_POSN (KW_ZERO+15) 106242851Sgrog#define KW_N_SIGN_POSN (KW_ZERO+16) 107242851Sgrog#define KW_INT_P_CS_PRECEDES (KW_ZERO+17) 108242851Sgrog#define KW_INT_P_SEP_BY_SPACE (KW_ZERO+18) 109242851Sgrog#define KW_INT_N_CS_PRECEDES (KW_ZERO+19) 110242851Sgrog#define KW_INT_N_SEP_BY_SPACE (KW_ZERO+20) 111242851Sgrog#define KW_INT_P_SIGN_POSN (KW_ZERO+21) 112242851Sgrog#define KW_INT_N_SIGN_POSN (KW_ZERO+22) 113116612Sphantom 114310040Svangyzenstatic const struct _kwinfo { 115116616Sphantom const char *name; 116116616Sphantom int isstr; /* true - string, false - number */ 117116616Sphantom int catid; /* LC_* */ 118116616Sphantom int value_ref; 119116675Sphantom const char *comment; 120116612Sphantom} kwinfo [] = { 121116675Sphantom { "charmap", 1, LC_CTYPE, CODESET, "" }, /* hack */ 122116612Sphantom 123116675Sphantom { "decimal_point", 1, LC_NUMERIC, RADIXCHAR, "" }, 124116675Sphantom { "thousands_sep", 1, LC_NUMERIC, THOUSEP, "" }, 125116675Sphantom { "grouping", 1, LC_NUMERIC, KW_GROUPING, "" }, 126116675Sphantom { "radixchar", 1, LC_NUMERIC, RADIXCHAR, 127116675Sphantom "Same as decimal_point (FreeBSD only)" }, /* compat */ 128116675Sphantom { "thousep", 1, LC_NUMERIC, THOUSEP, 129116675Sphantom "Same as thousands_sep (FreeBSD only)" }, /* compat */ 130116612Sphantom 131116675Sphantom { "int_curr_symbol", 1, LC_MONETARY, KW_INT_CURR_SYMBOL, "" }, 132116675Sphantom { "currency_symbol", 1, LC_MONETARY, KW_CURRENCY_SYMBOL, "" }, 133116675Sphantom { "mon_decimal_point", 1, LC_MONETARY, KW_MON_DECIMAL_POINT, "" }, 134116675Sphantom { "mon_thousands_sep", 1, LC_MONETARY, KW_MON_THOUSANDS_SEP, "" }, 135116675Sphantom { "mon_grouping", 1, LC_MONETARY, KW_MON_GROUPING, "" }, 136116675Sphantom { "positive_sign", 1, LC_MONETARY, KW_POSITIVE_SIGN, "" }, 137116675Sphantom { "negative_sign", 1, LC_MONETARY, KW_NEGATIVE_SIGN, "" }, 138116612Sphantom 139116675Sphantom { "int_frac_digits", 0, LC_MONETARY, KW_INT_FRAC_DIGITS, "" }, 140116675Sphantom { "frac_digits", 0, LC_MONETARY, KW_FRAC_DIGITS, "" }, 141116675Sphantom { "p_cs_precedes", 0, LC_MONETARY, KW_P_CS_PRECEDES, "" }, 142116675Sphantom { "p_sep_by_space", 0, LC_MONETARY, KW_P_SEP_BY_SPACE, "" }, 143116675Sphantom { "n_cs_precedes", 0, LC_MONETARY, KW_N_CS_PRECEDES, "" }, 144116675Sphantom { "n_sep_by_space", 0, LC_MONETARY, KW_N_SEP_BY_SPACE, "" }, 145116675Sphantom { "p_sign_posn", 0, LC_MONETARY, KW_P_SIGN_POSN, "" }, 146116675Sphantom { "n_sign_posn", 0, LC_MONETARY, KW_N_SIGN_POSN, "" }, 147116873Sphantom { "int_p_cs_precedes", 0, LC_MONETARY, KW_INT_P_CS_PRECEDES, "" }, 148116873Sphantom { "int_p_sep_by_space", 0, LC_MONETARY, KW_INT_P_SEP_BY_SPACE, "" }, 149116873Sphantom { "int_n_cs_precedes", 0, LC_MONETARY, KW_INT_N_CS_PRECEDES, "" }, 150116873Sphantom { "int_n_sep_by_space", 0, LC_MONETARY, KW_INT_N_SEP_BY_SPACE, "" }, 151116873Sphantom { "int_p_sign_posn", 0, LC_MONETARY, KW_INT_P_SIGN_POSN, "" }, 152116873Sphantom { "int_n_sign_posn", 0, LC_MONETARY, KW_INT_N_SIGN_POSN, "" }, 153116612Sphantom 154116675Sphantom { "d_t_fmt", 1, LC_TIME, D_T_FMT, "" }, 155116675Sphantom { "d_fmt", 1, LC_TIME, D_FMT, "" }, 156116675Sphantom { "t_fmt", 1, LC_TIME, T_FMT, "" }, 157116675Sphantom { "am_str", 1, LC_TIME, AM_STR, "" }, 158116675Sphantom { "pm_str", 1, LC_TIME, PM_STR, "" }, 159116675Sphantom { "t_fmt_ampm", 1, LC_TIME, T_FMT_AMPM, "" }, 160116675Sphantom { "day_1", 1, LC_TIME, DAY_1, "" }, 161116675Sphantom { "day_2", 1, LC_TIME, DAY_2, "" }, 162116675Sphantom { "day_3", 1, LC_TIME, DAY_3, "" }, 163116675Sphantom { "day_4", 1, LC_TIME, DAY_4, "" }, 164116675Sphantom { "day_5", 1, LC_TIME, DAY_5, "" }, 165116675Sphantom { "day_6", 1, LC_TIME, DAY_6, "" }, 166116675Sphantom { "day_7", 1, LC_TIME, DAY_7, "" }, 167116675Sphantom { "abday_1", 1, LC_TIME, ABDAY_1, "" }, 168116675Sphantom { "abday_2", 1, LC_TIME, ABDAY_2, "" }, 169116675Sphantom { "abday_3", 1, LC_TIME, ABDAY_3, "" }, 170116675Sphantom { "abday_4", 1, LC_TIME, ABDAY_4, "" }, 171116675Sphantom { "abday_5", 1, LC_TIME, ABDAY_5, "" }, 172116675Sphantom { "abday_6", 1, LC_TIME, ABDAY_6, "" }, 173116675Sphantom { "abday_7", 1, LC_TIME, ABDAY_7, "" }, 174116675Sphantom { "mon_1", 1, LC_TIME, MON_1, "" }, 175116675Sphantom { "mon_2", 1, LC_TIME, MON_2, "" }, 176116675Sphantom { "mon_3", 1, LC_TIME, MON_3, "" }, 177116675Sphantom { "mon_4", 1, LC_TIME, MON_4, "" }, 178116675Sphantom { "mon_5", 1, LC_TIME, MON_5, "" }, 179116675Sphantom { "mon_6", 1, LC_TIME, MON_6, "" }, 180116675Sphantom { "mon_7", 1, LC_TIME, MON_7, "" }, 181116675Sphantom { "mon_8", 1, LC_TIME, MON_8, "" }, 182116675Sphantom { "mon_9", 1, LC_TIME, MON_9, "" }, 183116675Sphantom { "mon_10", 1, LC_TIME, MON_10, "" }, 184116675Sphantom { "mon_11", 1, LC_TIME, MON_11, "" }, 185116675Sphantom { "mon_12", 1, LC_TIME, MON_12, "" }, 186116675Sphantom { "abmon_1", 1, LC_TIME, ABMON_1, "" }, 187116675Sphantom { "abmon_2", 1, LC_TIME, ABMON_2, "" }, 188116675Sphantom { "abmon_3", 1, LC_TIME, ABMON_3, "" }, 189116675Sphantom { "abmon_4", 1, LC_TIME, ABMON_4, "" }, 190116675Sphantom { "abmon_5", 1, LC_TIME, ABMON_5, "" }, 191116675Sphantom { "abmon_6", 1, LC_TIME, ABMON_6, "" }, 192116675Sphantom { "abmon_7", 1, LC_TIME, ABMON_7, "" }, 193116675Sphantom { "abmon_8", 1, LC_TIME, ABMON_8, "" }, 194116675Sphantom { "abmon_9", 1, LC_TIME, ABMON_9, "" }, 195116675Sphantom { "abmon_10", 1, LC_TIME, ABMON_10, "" }, 196116675Sphantom { "abmon_11", 1, LC_TIME, ABMON_11, "" }, 197116675Sphantom { "abmon_12", 1, LC_TIME, ABMON_12, "" }, 198197847Sedwin { "altmon_1", 1, LC_TIME, ALTMON_1, "(FreeBSD only)" }, 199197847Sedwin { "altmon_2", 1, LC_TIME, ALTMON_2, "(FreeBSD only)" }, 200197847Sedwin { "altmon_3", 1, LC_TIME, ALTMON_3, "(FreeBSD only)" }, 201197847Sedwin { "altmon_4", 1, LC_TIME, ALTMON_4, "(FreeBSD only)" }, 202197847Sedwin { "altmon_5", 1, LC_TIME, ALTMON_5, "(FreeBSD only)" }, 203197847Sedwin { "altmon_6", 1, LC_TIME, ALTMON_6, "(FreeBSD only)" }, 204197847Sedwin { "altmon_7", 1, LC_TIME, ALTMON_7, "(FreeBSD only)" }, 205197847Sedwin { "altmon_8", 1, LC_TIME, ALTMON_8, "(FreeBSD only)" }, 206197847Sedwin { "altmon_9", 1, LC_TIME, ALTMON_9, "(FreeBSD only)" }, 207197847Sedwin { "altmon_10", 1, LC_TIME, ALTMON_10, "(FreeBSD only)" }, 208197847Sedwin { "altmon_11", 1, LC_TIME, ALTMON_11, "(FreeBSD only)" }, 209197847Sedwin { "altmon_12", 1, LC_TIME, ALTMON_12, "(FreeBSD only)" }, 210116675Sphantom { "era", 1, LC_TIME, ERA, "(unavailable)" }, 211116675Sphantom { "era_d_fmt", 1, LC_TIME, ERA_D_FMT, "(unavailable)" }, 212116675Sphantom { "era_d_t_fmt", 1, LC_TIME, ERA_D_T_FMT, "(unavailable)" }, 213116675Sphantom { "era_t_fmt", 1, LC_TIME, ERA_T_FMT, "(unavailable)" }, 214116675Sphantom { "alt_digits", 1, LC_TIME, ALT_DIGITS, "" }, 215116675Sphantom { "d_md_order", 1, LC_TIME, D_MD_ORDER, 216116675Sphantom "(FreeBSD only)" }, /* local */ 217116612Sphantom 218116675Sphantom { "yesexpr", 1, LC_MESSAGES, YESEXPR, "" }, 219116675Sphantom { "noexpr", 1, LC_MESSAGES, NOEXPR, "" }, 220116675Sphantom { "yesstr", 1, LC_MESSAGES, YESSTR, 221116675Sphantom "(POSIX legacy)" }, /* compat */ 222116675Sphantom { "nostr", 1, LC_MESSAGES, NOSTR, 223116675Sphantom "(POSIX legacy)" } /* compat */ 224116612Sphantom 225116612Sphantom}; 226307697Saraujo#define NKWINFO (nitems(kwinfo)) 227116612Sphantom 228310040Svangyzenstatic const char *boguslocales[] = { "UTF-8" }; 229307697Saraujo#define NBOGUS (nitems(boguslocales)) 230133013Stjr 231105239Sphantomint 232105239Sphantommain(int argc, char *argv[]) 233105239Sphantom{ 234124830Sgrehan int ch; 235116675Sphantom int tmp; 236105239Sphantom 237197764Sedwin while ((ch = getopt(argc, argv, "ackms:")) != -1) { 238105239Sphantom switch (ch) { 239105239Sphantom case 'a': 240105239Sphantom all_locales = 1; 241105239Sphantom break; 242116612Sphantom case 'c': 243116612Sphantom prt_categories = 1; 244105239Sphantom break; 245116612Sphantom case 'k': 246116612Sphantom prt_keywords = 1; 247116612Sphantom break; 248116612Sphantom case 'm': 249116612Sphantom all_charmaps = 1; 250116612Sphantom break; 251105239Sphantom default: 252105239Sphantom usage(); 253105239Sphantom } 254116612Sphantom } 255105239Sphantom argc -= optind; 256105239Sphantom argv += optind; 257105239Sphantom 258116612Sphantom /* validate arguments */ 259116612Sphantom if (all_locales && all_charmaps) 260116612Sphantom usage(); 261242743Sgrog if ((all_locales || all_charmaps) && argc > 0) 262116612Sphantom usage(); 263116612Sphantom if ((all_locales || all_charmaps) && (prt_categories || prt_keywords)) 264116612Sphantom usage(); 265116612Sphantom 266116612Sphantom /* process '-a' */ 267105239Sphantom if (all_locales) { 268105239Sphantom list_locales(); 269105239Sphantom exit(0); 270105239Sphantom } 271105239Sphantom 272116612Sphantom /* process '-m' */ 273116612Sphantom if (all_charmaps) { 274116876Sphantom list_charmaps(); 275116876Sphantom exit(0); 276116612Sphantom } 277116612Sphantom 278116675Sphantom /* check for special case '-k list' */ 279116675Sphantom tmp = 0; 280116675Sphantom if (prt_keywords && argc > 0) 281116675Sphantom while (tmp < argc) 282116675Sphantom if (strcasecmp(argv[tmp++], "list") == 0) { 283197764Sedwin showkeywordslist(argv[tmp]); 284116675Sphantom exit(0); 285116675Sphantom } 286116675Sphantom 287243201Sgrog /* process '-c', '-k', or command line arguments. */ 288243201Sgrog if (prt_categories || prt_keywords || argc > 0) { 289309180Sume if (prt_keywords || argc > 0) 290309180Sume setlocale(LC_ALL, ""); 291242743Sgrog if (argc > 0) { 292242808Sgrog while (argc > 0) { 293242743Sgrog showdetails(*argv); 294242808Sgrog argv++; 295242808Sgrog argc--; 296242808Sgrog } 297242808Sgrog } else { 298242743Sgrog uint i; 299298169Sbapt for (i = 0; i < nitems(kwinfo); i++) 300310040Svangyzen showdetails(kwinfo[i].name); 301242808Sgrog } 302116612Sphantom exit(0); 303116612Sphantom } 304116612Sphantom 305116612Sphantom /* no arguments, show current locale state */ 306116612Sphantom showlocale(); 307116612Sphantom 308105239Sphantom return (0); 309105239Sphantom} 310105239Sphantom 311105239Sphantomvoid 312105239Sphantomusage(void) 313105239Sphantom{ 314116612Sphantom printf("Usage: locale [ -a | -m ]\n" 315242808Sgrog " locale -k list [prefix]\n" 316242808Sgrog " locale [ -ck ] [keyword ...]\n"); 317105239Sphantom exit(1); 318105239Sphantom} 319105239Sphantom 320116612Sphantom/* 321116612Sphantom * Output information about all available locales 322116612Sphantom * 323116612Sphantom * XXX actually output of this function does not guarantee that locale 324116612Sphantom * is really available to application, since it can be broken or 325242851Sgrog * inconsistent thus setlocale() will fail. Maybe add '-V' function to 326116612Sphantom * also validate these locales? 327116612Sphantom */ 328105239Sphantomvoid 329105239Sphantomlist_locales(void) 330105239Sphantom{ 331116616Sphantom size_t i; 332105239Sphantom 333116612Sphantom init_locales_list(); 334105239Sphantom for (i = 0; i < locales->sl_cur; i++) { 335105239Sphantom printf("%s\n", locales->sl_str[i]); 336105239Sphantom } 337105239Sphantom} 338105239Sphantom 339116876Sphantom/* 340116877Sphantom * qsort() helper function 341116877Sphantom */ 342116877Sphantomstatic int 343116877Sphantomscmp(const void *s1, const void *s2) 344116877Sphantom{ 345310040Svangyzen return strcmp(*(const char * const *)s1, *(const char * const *)s2); 346116877Sphantom} 347116877Sphantom 348116877Sphantom/* 349116876Sphantom * Output information about all available charmaps 350116876Sphantom * 351116876Sphantom * XXX this function is doing a task in hackish way, i.e. by scaning 352116876Sphantom * list of locales, spliting their codeset part and building list of 353116876Sphantom * them. 354116876Sphantom */ 355116876Sphantomvoid 356116876Sphantomlist_charmaps(void) 357116876Sphantom{ 358116876Sphantom size_t i; 359116876Sphantom char *s, *cs; 360116876Sphantom StringList *charmaps; 361116612Sphantom 362116876Sphantom /* initialize StringList */ 363116876Sphantom charmaps = sl_init(); 364116876Sphantom if (charmaps == NULL) 365116876Sphantom err(1, "could not allocate memory"); 366116876Sphantom 367116876Sphantom /* fetch locales list */ 368116876Sphantom init_locales_list(); 369116876Sphantom 370116876Sphantom /* split codesets and build their list */ 371116876Sphantom for (i = 0; i < locales->sl_cur; i++) { 372116876Sphantom s = locales->sl_str[i]; 373116876Sphantom if ((cs = strchr(s, '.')) != NULL) { 374116876Sphantom cs++; 375116876Sphantom if (sl_find(charmaps, cs) == NULL) 376116876Sphantom sl_add(charmaps, cs); 377116876Sphantom } 378116876Sphantom } 379116876Sphantom 380116876Sphantom /* add US-ASCII, if not yet added */ 381116876Sphantom if (sl_find(charmaps, "US-ASCII") == NULL) 382310040Svangyzen sl_add(charmaps, strdup("US-ASCII")); 383116876Sphantom 384116876Sphantom /* sort the list */ 385116876Sphantom qsort(charmaps->sl_str, charmaps->sl_cur, sizeof(char *), scmp); 386116876Sphantom 387116876Sphantom /* print results */ 388116876Sphantom for (i = 0; i < charmaps->sl_cur; i++) { 389116876Sphantom printf("%s\n", charmaps->sl_str[i]); 390116876Sphantom } 391116876Sphantom} 392116876Sphantom 393116612Sphantom/* 394116612Sphantom * Retrieve sorted list of system locales (or user locales, if PATH_LOCALE 395116612Sphantom * environment variable is set) 396116612Sphantom */ 397105239Sphantomvoid 398116612Sphantominit_locales_list(void) 399105239Sphantom{ 400116612Sphantom DIR *dirp; 401116612Sphantom struct dirent *dp; 402133013Stjr size_t i; 403133013Stjr int bogus; 404105239Sphantom 405116612Sphantom /* why call this function twice ? */ 406116612Sphantom if (locales != NULL) 407105239Sphantom return; 408116612Sphantom 409116612Sphantom /* initialize StringList */ 410116612Sphantom locales = sl_init(); 411116612Sphantom if (locales == NULL) 412116612Sphantom err(1, "could not allocate memory"); 413116612Sphantom 414116612Sphantom /* get actual locales directory name */ 415116851Sphantom if (__detect_path_locale() != 0) 416116851Sphantom err(1, "unable to find locales storage"); 417116612Sphantom 418116612Sphantom /* open locales directory */ 419116851Sphantom dirp = opendir(_PathLocale); 420116612Sphantom if (dirp == NULL) 421116851Sphantom err(1, "could not open directory '%s'", _PathLocale); 422116612Sphantom 423116612Sphantom /* scan directory and store its contents except "." and ".." */ 424116612Sphantom while ((dp = readdir(dirp)) != NULL) { 425116612Sphantom if (*(dp->d_name) == '.') 426116612Sphantom continue; /* exclude "." and ".." */ 427133013Stjr for (bogus = i = 0; i < NBOGUS; i++) 428133013Stjr if (strncmp(dp->d_name, boguslocales[i], 429133013Stjr strlen(boguslocales[i])) == 0) 430133013Stjr bogus = 1; 431133013Stjr if (!bogus) 432133013Stjr sl_add(locales, strdup(dp->d_name)); 433105239Sphantom } 434116612Sphantom closedir(dirp); 435105239Sphantom 436242808Sgrog /* make sure that 'POSIX' and 'C' locales are present in the list. 437116612Sphantom * POSIX 1003.1-2001 requires presence of 'POSIX' name only here, but 438242808Sgrog * we also list 'C' for constistency 439242808Sgrog */ 440116612Sphantom if (sl_find(locales, "POSIX") == NULL) 441310040Svangyzen sl_add(locales, strdup("POSIX")); 442105239Sphantom 443116612Sphantom if (sl_find(locales, "C") == NULL) 444310040Svangyzen sl_add(locales, strdup("C")); 445105239Sphantom 446116612Sphantom /* make output nicer, sort the list */ 447116612Sphantom qsort(locales->sl_str, locales->sl_cur, sizeof(char *), scmp); 448105239Sphantom} 449105239Sphantom 450116612Sphantom/* 451116612Sphantom * Show current locale status, depending on environment variables 452116612Sphantom */ 453105239Sphantomvoid 454116612Sphantomshowlocale(void) 455105239Sphantom{ 456116616Sphantom size_t i; 457125329Sache const char *lang, *vval, *eval; 458105239Sphantom 459125329Sache setlocale(LC_ALL, ""); 460105239Sphantom 461125329Sache lang = getenv("LANG"); 462125329Sache if (lang == NULL) { 463116612Sphantom lang = ""; 464125329Sache } 465125329Sache printf("LANG=%s\n", lang); 466116616Sphantom /* XXX: if LANG is null, then set it to "C" to get implied values? */ 467105239Sphantom 468116612Sphantom for (i = 0; i < NLCINFO; i++) { 469116612Sphantom vval = setlocale(lcinfo[i].id, NULL); 470116612Sphantom eval = getenv(lcinfo[i].name); 471116612Sphantom if (eval != NULL && !strcmp(eval, vval) 472116612Sphantom && strcmp(lang, vval)) { 473116612Sphantom /* 474116612Sphantom * Appropriate environment variable set, its value 475289677Seadler * is valid and not overridden by LC_ALL 476116612Sphantom * 477116612Sphantom * XXX: possible side effect: if both LANG and 478289677Seadler * overridden environment variable are set into same 479116612Sphantom * value, then it'll be assumed as 'implied' 480116612Sphantom */ 481116612Sphantom printf("%s=%s\n", lcinfo[i].name, vval); 482116612Sphantom } else { 483116612Sphantom printf("%s=\"%s\"\n", lcinfo[i].name, vval); 484116612Sphantom } 485105239Sphantom } 486105239Sphantom 487125329Sache vval = getenv("LC_ALL"); 488125329Sache if (vval == NULL) { 489125329Sache vval = ""; 490125329Sache } 491125329Sache printf("LC_ALL=%s\n", vval); 492105239Sphantom} 493105239Sphantom 494309330Svangyzenchar * 495309330Svangyzenformat_grouping(const char *binary) 496309330Svangyzen{ 497309330Svangyzen static char rval[64]; 498309330Svangyzen const char *cp; 499310040Svangyzen size_t roff; 500310040Svangyzen int len; 501309330Svangyzen 502309330Svangyzen rval[0] = '\0'; 503310040Svangyzen roff = 0; 504309330Svangyzen for (cp = binary; *cp != '\0'; ++cp) { 505310040Svangyzen#if CHAR_MIN != 0 506310040Svangyzen if (*cp < 0) 507310040Svangyzen break; /* garbage input */ 508310040Svangyzen#endif 509310040Svangyzen len = snprintf(&rval[roff], sizeof(rval) - roff, "%u;", *cp); 510310040Svangyzen if (len < 0 || (unsigned)len >= sizeof(rval) - roff) 511310040Svangyzen break; /* insufficient space for output */ 512310040Svangyzen roff += len; 513310040Svangyzen if (*cp == CHAR_MAX) 514310040Svangyzen break; /* special termination */ 515309330Svangyzen } 516309330Svangyzen 517310040Svangyzen /* Truncate at the last successfully snprintf()ed semicolon. */ 518310040Svangyzen if (roff != 0) 519310040Svangyzen rval[roff - 1] = '\0'; 520309330Svangyzen 521310040Svangyzen return (&rval[0]); 522309330Svangyzen} 523309330Svangyzen 524116612Sphantom/* 525116612Sphantom * keyword value lookup helper (via localeconv()) 526116612Sphantom */ 527116612Sphantomchar * 528116612Sphantomkwval_lconv(int id) 529105239Sphantom{ 530116616Sphantom struct lconv *lc; 531116616Sphantom char *rval; 532105239Sphantom 533116616Sphantom rval = NULL; 534116616Sphantom lc = localeconv(); 535116612Sphantom switch (id) { 536116612Sphantom case KW_GROUPING: 537309330Svangyzen rval = format_grouping(lc->grouping); 538116612Sphantom break; 539116612Sphantom case KW_INT_CURR_SYMBOL: 540116612Sphantom rval = lc->int_curr_symbol; 541116612Sphantom break; 542116612Sphantom case KW_CURRENCY_SYMBOL: 543116612Sphantom rval = lc->currency_symbol; 544116612Sphantom break; 545116612Sphantom case KW_MON_DECIMAL_POINT: 546116612Sphantom rval = lc->mon_decimal_point; 547116612Sphantom break; 548116612Sphantom case KW_MON_THOUSANDS_SEP: 549116612Sphantom rval = lc->mon_thousands_sep; 550116612Sphantom break; 551116612Sphantom case KW_MON_GROUPING: 552309330Svangyzen rval = format_grouping(lc->mon_grouping); 553116612Sphantom break; 554116612Sphantom case KW_POSITIVE_SIGN: 555116612Sphantom rval = lc->positive_sign; 556116612Sphantom break; 557116612Sphantom case KW_NEGATIVE_SIGN: 558116612Sphantom rval = lc->negative_sign; 559116612Sphantom break; 560116612Sphantom case KW_INT_FRAC_DIGITS: 561116612Sphantom rval = &(lc->int_frac_digits); 562116612Sphantom break; 563116612Sphantom case KW_FRAC_DIGITS: 564116612Sphantom rval = &(lc->frac_digits); 565116612Sphantom break; 566116612Sphantom case KW_P_CS_PRECEDES: 567116612Sphantom rval = &(lc->p_cs_precedes); 568116612Sphantom break; 569116612Sphantom case KW_P_SEP_BY_SPACE: 570116612Sphantom rval = &(lc->p_sep_by_space); 571116612Sphantom break; 572116612Sphantom case KW_N_CS_PRECEDES: 573116612Sphantom rval = &(lc->n_cs_precedes); 574116612Sphantom break; 575116612Sphantom case KW_N_SEP_BY_SPACE: 576116612Sphantom rval = &(lc->n_sep_by_space); 577116612Sphantom break; 578116612Sphantom case KW_P_SIGN_POSN: 579116612Sphantom rval = &(lc->p_sign_posn); 580116612Sphantom break; 581116612Sphantom case KW_N_SIGN_POSN: 582116612Sphantom rval = &(lc->n_sign_posn); 583116612Sphantom break; 584116873Sphantom case KW_INT_P_CS_PRECEDES: 585116873Sphantom rval = &(lc->int_p_cs_precedes); 586116873Sphantom break; 587116873Sphantom case KW_INT_P_SEP_BY_SPACE: 588116873Sphantom rval = &(lc->int_p_sep_by_space); 589116873Sphantom break; 590116873Sphantom case KW_INT_N_CS_PRECEDES: 591116873Sphantom rval = &(lc->int_n_cs_precedes); 592116873Sphantom break; 593116873Sphantom case KW_INT_N_SEP_BY_SPACE: 594116873Sphantom rval = &(lc->int_n_sep_by_space); 595116873Sphantom break; 596116873Sphantom case KW_INT_P_SIGN_POSN: 597116873Sphantom rval = &(lc->int_p_sign_posn); 598116873Sphantom break; 599116873Sphantom case KW_INT_N_SIGN_POSN: 600116873Sphantom rval = &(lc->int_n_sign_posn); 601116873Sphantom break; 602116612Sphantom default: 603116612Sphantom break; 604116612Sphantom } 605116612Sphantom return (rval); 606105239Sphantom} 607105239Sphantom 608116612Sphantom/* 609116612Sphantom * keyword value and properties lookup 610116612Sphantom */ 611116612Sphantomint 612310040Svangyzenkwval_lookup(const char *kwname, char **kwval, int *cat, int *isstr) 613105239Sphantom{ 614116616Sphantom int rval; 615116616Sphantom size_t i; 616116612Sphantom 617116616Sphantom rval = 0; 618116612Sphantom for (i = 0; i < NKWINFO; i++) { 619116612Sphantom if (strcasecmp(kwname, kwinfo[i].name) == 0) { 620116612Sphantom rval = 1; 621116612Sphantom *cat = kwinfo[i].catid; 622116612Sphantom *isstr = kwinfo[i].isstr; 623116612Sphantom if (kwinfo[i].value_ref < KW_ZERO) { 624116612Sphantom *kwval = nl_langinfo(kwinfo[i].value_ref); 625116612Sphantom } else { 626116612Sphantom *kwval = kwval_lconv(kwinfo[i].value_ref); 627116612Sphantom } 628116612Sphantom break; 629116612Sphantom } 630116612Sphantom } 631116612Sphantom 632116612Sphantom return (rval); 633105239Sphantom} 634105239Sphantom 635116612Sphantom/* 636116612Sphantom * Show details about requested keyword according to '-k' and/or '-c' 637116612Sphantom * command line options specified. 638116612Sphantom */ 639105239Sphantomvoid 640310040Svangyzenshowdetails(const char *kw) 641105239Sphantom{ 642116616Sphantom int isstr, cat, tmpval; 643116616Sphantom char *kwval; 644105239Sphantom 645116612Sphantom if (kwval_lookup(kw, &kwval, &cat, &isstr) == 0) { 646116612Sphantom /* 647116612Sphantom * invalid keyword specified. 648116612Sphantom * XXX: any actions? 649116612Sphantom */ 650197764Sedwin fprintf(stderr, "Unknown keyword: `%s'\n", kw); 651105239Sphantom return; 652116612Sphantom } 653105239Sphantom 654116612Sphantom if (prt_categories) { 655310040Svangyzen if (prt_keywords) 656242743Sgrog printf("%-20s ", lookup_localecat(cat)); 657310040Svangyzen else 658242808Sgrog printf("%-20s\t%s\n", kw, lookup_localecat(cat)); 659116612Sphantom } 660105239Sphantom 661116612Sphantom if (prt_keywords) { 662116612Sphantom if (isstr) { 663116612Sphantom printf("%s=\"%s\"\n", kw, kwval); 664116612Sphantom } else { 665116616Sphantom tmpval = (char) *kwval; 666116616Sphantom printf("%s=%d\n", kw, tmpval); 667116612Sphantom } 668116612Sphantom } 669105239Sphantom 670116612Sphantom if (!prt_categories && !prt_keywords) { 671116612Sphantom if (isstr) { 672116612Sphantom printf("%s\n", kwval); 673116612Sphantom } else { 674116616Sphantom tmpval = (char) *kwval; 675116616Sphantom printf("%d\n", tmpval); 676116612Sphantom } 677105239Sphantom } 678105239Sphantom} 679116675Sphantom 680116675Sphantom/* 681116675Sphantom * Convert locale category id into string 682116675Sphantom */ 683116675Sphantomconst char * 684116675Sphantomlookup_localecat(int cat) 685116675Sphantom{ 686116675Sphantom size_t i; 687116675Sphantom 688116675Sphantom for (i = 0; i < NLCINFO; i++) 689116675Sphantom if (lcinfo[i].id == cat) { 690116675Sphantom return (lcinfo[i].name); 691116675Sphantom } 692116675Sphantom return ("UNKNOWN"); 693116675Sphantom} 694116675Sphantom 695116675Sphantom/* 696116675Sphantom * Show list of keywords 697116675Sphantom */ 698116675Sphantomvoid 699197764Sedwinshowkeywordslist(char *substring) 700116675Sphantom{ 701116675Sphantom size_t i; 702116675Sphantom 703242851Sgrog#define FMT "%-20s %-12s %-7s %-20s\n" 704116675Sphantom 705197764Sedwin if (substring == NULL) 706197764Sedwin printf("List of available keywords\n\n"); 707197764Sedwin else 708197764Sedwin printf("List of available keywords starting with '%s'\n\n", 709197764Sedwin substring); 710116675Sphantom printf(FMT, "Keyword", "Category", "Type", "Comment"); 711116675Sphantom printf("-------------------- ------------ ------- --------------------\n"); 712116675Sphantom for (i = 0; i < NKWINFO; i++) { 713197764Sedwin if (substring != NULL) { 714197764Sedwin if (strncmp(kwinfo[i].name, substring, 715197764Sedwin strlen(substring)) != 0) 716197764Sedwin continue; 717197764Sedwin } 718116675Sphantom printf(FMT, 719116675Sphantom kwinfo[i].name, 720116675Sphantom lookup_localecat(kwinfo[i].catid), 721116675Sphantom (kwinfo[i].isstr == 0) ? "number" : "string", 722116675Sphantom kwinfo[i].comment); 723116675Sphantom } 724116675Sphantom} 725