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