1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * locale -- get current locale information 29 * 30 * Copyright 1991, 1993 by Mortice Kern Systems Inc. All rights reserved. 31 * 32 */ 33 34 /* 35 * locale: get locale-specific information 36 * usage: locale [-a|-m] 37 * locale [-ck] name ... 38 */ 39 40 /* 41 * New members added in the struct lconv by IEEE Std 1003.1-2001 42 * are always activated in the locale object. 43 * See <iso/locale_iso.h>. 44 */ 45 #define _LCONV_C99 46 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <limits.h> 50 #include <string.h> 51 #include <dirent.h> 52 #include <ctype.h> 53 #include <stddef.h> 54 #include <nl_types.h> 55 #include <langinfo.h> 56 #include <locale.h> 57 #include <sys/types.h> 58 #include <sys/stat.h> 59 60 #define LC_LOCDEF 999 /* Random number! */ 61 62 #define LOCALE_DIR "/usr/lib/locale/" 63 #define CHARMAP_DIR "/usr/lib/localedef/src/" 64 #define CHARMAP_NAME "charmap.src" 65 66 #define GET_LOCALE 0 67 #define GET_CHARMAP 1 68 #define CSSIZE 128 69 70 #ifndef isblank 71 #define isblank(c) ((__ctype + 1)[c] & _B) 72 #endif 73 74 enum types { 75 TYPE_STR, /* char * */ 76 TYPE_GROUP, /* char *, for mon_grouping, and grouping */ 77 TYPE_INT, /* int */ 78 TYPE_CHR, /* char, printed as signed integer */ 79 TYPE_PCHR, /* char, printed as printable character */ 80 TYPE_CTP, /* ctype entry */ 81 TYPE_CNVL, /* convert to lower */ 82 TYPE_CNVU, /* convert to upper */ 83 TYPE_COLLEL /* print the multi-character collating elements */ 84 }; 85 86 static int print_locale_info(char *keyword, int cflag, int kflag); 87 static int print_category(int category, int cflag, int kflag); 88 static int print_keyword(char *name, int cflag, int kflag); 89 static void usage(void); 90 static void print_all_info(int); 91 static void print_cur_locale(void); 92 static void outstr(char *s); 93 static void outchar(int); 94 static void prt_ctp(char *); 95 static void prt_cnv(char *); 96 static void prt_collel(char *); 97 static char get_escapechar(void); 98 static char get_commentchar(void); 99 100 static char *save_loc; 101 102 /* 103 * yes/no is not in the localeconv structure for xpg style. 104 * We dummy up a new structure for purposes of the code below. 105 * If YESEXPR is available per XPG4, we use it. 106 * Otherwise, use YESSTR, the old method with less functionality from XPG3. 107 */ 108 struct yesno { 109 char *yes_expr; 110 char *no_expr; 111 char *yes_str; 112 char *no_str; 113 }; 114 115 struct dtconv { 116 char *date_time_format; 117 char *date_format; 118 char *time_format; 119 char *time_format_ampm; 120 char *am_string; 121 char *pm_string; 122 char *abbrev_day_names[7]; 123 char *day_names[7]; 124 char *abbrev_month_names[12]; 125 char *month_names[12]; 126 char *era; 127 char *era_d_fmt; 128 char *era_d_t_fmt; 129 char *era_t_fmt; 130 char *alt_digits; 131 }; 132 133 struct localedef { 134 char *charmap; 135 char *code_set_name; 136 char escape_char; 137 char comment_char; 138 int mb_cur_max; 139 int mb_cur_min; 140 }; 141 142 static struct yesno * 143 getyesno(void) 144 { 145 static struct yesno yn; 146 static int loaded = 0; 147 148 if (loaded) { 149 return (&yn); 150 /* NOTREACHED */ 151 } 152 153 yn.yes_expr = strdup(nl_langinfo(YESEXPR)); 154 yn.no_expr = strdup(nl_langinfo(NOEXPR)); 155 yn.yes_str = strdup(nl_langinfo(YESSTR)); 156 yn.no_str = strdup(nl_langinfo(NOSTR)); 157 158 loaded = 1; 159 return (&yn); 160 } 161 162 static struct dtconv * 163 localedtconv(void) 164 { 165 static struct dtconv _dtconv; 166 static int loaded = 0; 167 168 if (loaded) { 169 return (&_dtconv); 170 /* NOTREACHED */ 171 } 172 173 _dtconv.date_time_format = strdup(nl_langinfo(D_T_FMT)); 174 _dtconv.date_format = strdup(nl_langinfo(D_FMT)); 175 _dtconv.time_format = strdup(nl_langinfo(T_FMT)); 176 _dtconv.time_format_ampm = strdup(nl_langinfo(T_FMT_AMPM)); 177 _dtconv.am_string = strdup(nl_langinfo(AM_STR)); 178 _dtconv.pm_string = strdup(nl_langinfo(PM_STR)); 179 _dtconv.abbrev_day_names[0] = strdup(nl_langinfo(ABDAY_1)); 180 _dtconv.abbrev_day_names[1] = strdup(nl_langinfo(ABDAY_2)); 181 _dtconv.abbrev_day_names[2] = strdup(nl_langinfo(ABDAY_3)); 182 _dtconv.abbrev_day_names[3] = strdup(nl_langinfo(ABDAY_4)); 183 _dtconv.abbrev_day_names[4] = strdup(nl_langinfo(ABDAY_5)); 184 _dtconv.abbrev_day_names[5] = strdup(nl_langinfo(ABDAY_6)); 185 _dtconv.abbrev_day_names[6] = strdup(nl_langinfo(ABDAY_7)); 186 _dtconv.day_names[0] = strdup(nl_langinfo(DAY_1)); 187 _dtconv.day_names[1] = strdup(nl_langinfo(DAY_2)); 188 _dtconv.day_names[2] = strdup(nl_langinfo(DAY_3)); 189 _dtconv.day_names[3] = strdup(nl_langinfo(DAY_4)); 190 _dtconv.day_names[4] = strdup(nl_langinfo(DAY_5)); 191 _dtconv.day_names[5] = strdup(nl_langinfo(DAY_6)); 192 _dtconv.day_names[6] = strdup(nl_langinfo(DAY_7)); 193 _dtconv.abbrev_month_names[0] = strdup(nl_langinfo(ABMON_1)); 194 _dtconv.abbrev_month_names[1] = strdup(nl_langinfo(ABMON_2)); 195 _dtconv.abbrev_month_names[2] = strdup(nl_langinfo(ABMON_3)); 196 _dtconv.abbrev_month_names[3] = strdup(nl_langinfo(ABMON_4)); 197 _dtconv.abbrev_month_names[4] = strdup(nl_langinfo(ABMON_5)); 198 _dtconv.abbrev_month_names[5] = strdup(nl_langinfo(ABMON_6)); 199 _dtconv.abbrev_month_names[6] = strdup(nl_langinfo(ABMON_7)); 200 _dtconv.abbrev_month_names[7] = strdup(nl_langinfo(ABMON_8)); 201 _dtconv.abbrev_month_names[8] = strdup(nl_langinfo(ABMON_9)); 202 _dtconv.abbrev_month_names[9] = strdup(nl_langinfo(ABMON_10)); 203 _dtconv.abbrev_month_names[10] = strdup(nl_langinfo(ABMON_11)); 204 _dtconv.abbrev_month_names[11] = strdup(nl_langinfo(ABMON_12)); 205 _dtconv.month_names[0] = strdup(nl_langinfo(MON_1)); 206 _dtconv.month_names[1] = strdup(nl_langinfo(MON_2)); 207 _dtconv.month_names[2] = strdup(nl_langinfo(MON_3)); 208 _dtconv.month_names[3] = strdup(nl_langinfo(MON_4)); 209 _dtconv.month_names[4] = strdup(nl_langinfo(MON_5)); 210 _dtconv.month_names[5] = strdup(nl_langinfo(MON_6)); 211 _dtconv.month_names[6] = strdup(nl_langinfo(MON_7)); 212 _dtconv.month_names[7] = strdup(nl_langinfo(MON_8)); 213 _dtconv.month_names[8] = strdup(nl_langinfo(MON_9)); 214 _dtconv.month_names[9] = strdup(nl_langinfo(MON_10)); 215 _dtconv.month_names[10] = strdup(nl_langinfo(MON_11)); 216 _dtconv.month_names[11] = strdup(nl_langinfo(MON_12)); 217 _dtconv.era = strdup(nl_langinfo(ERA)); 218 _dtconv.era_d_fmt = strdup(nl_langinfo(ERA_D_FMT)); 219 _dtconv.era_d_t_fmt = strdup(nl_langinfo(ERA_D_T_FMT)); 220 _dtconv.era_t_fmt = strdup(nl_langinfo(ERA_T_FMT)); 221 _dtconv.alt_digits = strdup(nl_langinfo(ALT_DIGITS)); 222 223 loaded = 1; 224 return (&_dtconv); 225 } 226 227 static struct localedef * 228 localeldconv(void) 229 { 230 static struct localedef _locdef; 231 static int loaded = 0; 232 233 if (loaded) { 234 return (&_locdef); 235 /* NOTREACHED */ 236 } 237 238 _locdef.charmap = strdup(nl_langinfo(CODESET)); 239 _locdef.code_set_name = strdup(nl_langinfo(CODESET)); 240 _locdef.mb_cur_max = MB_CUR_MAX; 241 _locdef.mb_cur_min = 1; 242 _locdef.escape_char = get_escapechar(); 243 _locdef.comment_char = get_commentchar(); 244 245 loaded = 1; 246 return (&_locdef); 247 } 248 249 /* 250 * The locale_name array also defines a canonical ordering for the categories. 251 * The function tocanon() translates the LC_* manifests to their canonical 252 * values. 253 */ 254 static struct locale_name { 255 char *name; 256 int category; 257 } locale_name[] = { 258 {"LC_CTYPE", LC_CTYPE}, 259 {"LC_NUMERIC", LC_NUMERIC}, 260 {"LC_TIME", LC_TIME}, 261 {"LC_COLLATE", LC_COLLATE}, 262 {"LC_MONETARY", LC_MONETARY}, 263 {"LC_MESSAGES", LC_MESSAGES}, 264 {"LC_ALL", LC_ALL}, 265 NULL 266 }; 267 268 /* 269 * The structure key contains all keywords string name, 270 * symbolic name, category, and type (STR INT ...) 271 * the type will decide the way the value of the item be printed out 272 */ 273 static struct key { 274 char *name; 275 void *(*structure)(void); 276 int offset; 277 int count; 278 int category; 279 enum types type; 280 } key[] = { 281 282 #define SPECIAL 0, 0, 0, 283 {"lower", SPECIAL LC_CTYPE, TYPE_CTP}, 284 {"upper", SPECIAL LC_CTYPE, TYPE_CTP}, 285 {"alpha", SPECIAL LC_CTYPE, TYPE_CTP}, 286 {"digit", SPECIAL LC_CTYPE, TYPE_CTP}, 287 {"space", SPECIAL LC_CTYPE, TYPE_CTP}, 288 {"cntrl", SPECIAL LC_CTYPE, TYPE_CTP}, 289 {"punct", SPECIAL LC_CTYPE, TYPE_CTP}, 290 {"graph", SPECIAL LC_CTYPE, TYPE_CTP}, 291 {"print", SPECIAL LC_CTYPE, TYPE_CTP}, 292 {"xdigit", SPECIAL LC_CTYPE, TYPE_CTP}, 293 {"blank", SPECIAL LC_CTYPE, TYPE_CTP}, 294 295 {"tolower", SPECIAL LC_CTYPE, TYPE_CNVL}, 296 {"toupper", SPECIAL LC_CTYPE, TYPE_CNVU}, 297 298 {"collating-element", 0, 0, 0, LC_COLLATE, TYPE_COLLEL}, 299 {"character-collation", 0, 1, 0, LC_COLLATE, TYPE_COLLEL}, 300 301 #define dt(member, count) \ 302 (void *(*)(void))localedtconv, \ 303 offsetof(struct dtconv, member), \ 304 count, \ 305 LC_TIME, \ 306 TYPE_STR 307 {"d_t_fmt", dt(date_time_format, 1)}, 308 {"d_fmt", dt(date_format, 1)}, 309 {"t_fmt", dt(time_format, 1)}, 310 {"t_fmt_ampm", dt(time_format_ampm, 1)}, 311 {"am_pm", dt(am_string, 2)}, 312 {"day", dt(day_names, 7)}, 313 {"abday", dt(abbrev_day_names, 7)}, 314 {"mon", dt(month_names, 12)}, 315 {"abmon", dt(abbrev_month_names, 12)}, 316 {"era", dt(era, 1)}, 317 {"era_d_fmt", dt(era_d_fmt, 1)}, 318 {"era_d_t_fmt", dt(era_d_t_fmt, 1)}, 319 {"era_t_fmt", dt(era_t_fmt, 1)}, 320 {"alt_digits", dt(alt_digits, 1)}, 321 322 #undef dt 323 324 #define lc(member, locale, type) \ 325 (void *(*)(void))localeconv, \ 326 offsetof(struct lconv, member), \ 327 1, \ 328 locale, \ 329 type 330 {"decimal_point", lc(decimal_point, LC_NUMERIC, TYPE_STR) }, 331 {"thousands_sep", lc(thousands_sep, LC_NUMERIC, TYPE_STR) }, 332 {"grouping", lc(grouping, LC_NUMERIC, TYPE_GROUP)}, 333 {"int_curr_symbol", lc(int_curr_symbol, LC_MONETARY, TYPE_STR)}, 334 {"currency_symbol", lc(currency_symbol, LC_MONETARY, TYPE_STR)}, 335 {"mon_decimal_point", lc(mon_decimal_point, LC_MONETARY, TYPE_STR)}, 336 {"mon_thousands_sep", lc(mon_thousands_sep, LC_MONETARY, TYPE_STR)}, 337 {"mon_grouping", lc(mon_grouping, LC_MONETARY, TYPE_GROUP)}, 338 {"positive_sign", lc(positive_sign, LC_MONETARY, TYPE_STR)}, 339 {"negative_sign", lc(negative_sign, LC_MONETARY, TYPE_STR)}, 340 341 {"int_frac_digits", lc(int_frac_digits, LC_MONETARY, TYPE_CHR)}, 342 {"frac_digits", lc(frac_digits, LC_MONETARY, TYPE_CHR)}, 343 {"p_cs_precedes", lc(p_cs_precedes, LC_MONETARY, TYPE_CHR)}, 344 {"p_sep_by_space", lc(p_sep_by_space, LC_MONETARY, TYPE_CHR)}, 345 {"n_cs_precedes", lc(n_cs_precedes, LC_MONETARY, TYPE_CHR)}, 346 {"n_sep_by_space", lc(n_sep_by_space, LC_MONETARY, TYPE_CHR)}, 347 {"p_sign_posn", lc(p_sign_posn, LC_MONETARY, TYPE_CHR)}, 348 {"n_sign_posn", lc(n_sign_posn, LC_MONETARY, TYPE_CHR)}, 349 {"int_p_cs_precedes", lc(int_p_cs_precedes, LC_MONETARY, TYPE_CHR)}, 350 {"int_p_sep_by_space", lc(int_p_sep_by_space, LC_MONETARY, TYPE_CHR)}, 351 {"int_n_cs_precedes", lc(int_n_cs_precedes, LC_MONETARY, TYPE_CHR)}, 352 {"int_n_sep_by_space", lc(int_n_sep_by_space, LC_MONETARY, TYPE_CHR)}, 353 {"int_p_sign_posn", lc(int_p_sign_posn, LC_MONETARY, TYPE_CHR)}, 354 {"int_n_sign_posn", lc(int_n_sign_posn, LC_MONETARY, TYPE_CHR)}, 355 356 #undef lc 357 #define lc(member) \ 358 (void *(*)(void))getyesno, \ 359 offsetof(struct yesno, member), \ 360 1, \ 361 LC_MESSAGES, \ 362 TYPE_STR 363 {"yesexpr", lc(yes_expr)}, 364 {"noexpr", lc(no_expr)}, 365 {"yesstr", lc(yes_str)}, 366 {"nostr", lc(no_str)}, 367 #undef lc 368 369 /* 370 * Following keywords have no official method of obtaining them 371 */ 372 #define ld(member, locale, type) \ 373 (void *(*)(void))localeldconv, \ 374 offsetof(struct localedef, member), \ 375 1, \ 376 locale, \ 377 type 378 {"charmap", ld(charmap, LC_LOCDEF, TYPE_STR)}, 379 {"code_set_name", ld(code_set_name, LC_LOCDEF, TYPE_STR)}, 380 {"escape_char", ld(escape_char, LC_LOCDEF, TYPE_PCHR)}, 381 {"comment_char", ld(comment_char, LC_LOCDEF, TYPE_PCHR)}, 382 {"mb_cur_max", ld(mb_cur_max, LC_LOCDEF, TYPE_INT)}, 383 {"mb_cur_min", ld(mb_cur_min, LC_LOCDEF, TYPE_INT)}, 384 #undef ld 385 386 {NULL, NULL, 0, 0} 387 }; 388 389 static char escapec; 390 391 int 392 main(int argc, char **argv) 393 { 394 int c; 395 int retval = 0; 396 int cflag, kflag, aflag, mflag; 397 398 (void) setlocale(LC_ALL, ""); 399 #if !defined(TEXT_DOMAIN) 400 #define TEXT_DOMAIN "SYS_TEST" 401 #endif 402 (void) textdomain(TEXT_DOMAIN); 403 404 cflag = kflag = aflag = mflag = 0; 405 406 while ((c = getopt(argc, argv, "amck")) != EOF) { 407 switch (c) { 408 case 'a': 409 aflag = 1; 410 break; 411 case 'm': 412 mflag = 1; 413 break; 414 case 'c': 415 cflag = 1; 416 break; 417 case 'k': 418 kflag = 1; 419 break; 420 default: 421 usage(); 422 /* NOTREACHED */ 423 break; 424 } 425 } 426 427 /* -a OR -m OR (-c and/or -k) */ 428 if ((aflag && mflag) || ((aflag || mflag) && (cflag || kflag))) { 429 usage(); 430 /* NOTREACHED */ 431 } 432 433 escapec = get_escapechar(); 434 435 if (aflag) { 436 print_all_info(GET_LOCALE); 437 /* NOTREACHED */ 438 } 439 440 if (mflag) { 441 print_all_info(GET_CHARMAP); 442 /* NOTREACHED */ 443 } 444 445 if (optind == argc && !cflag && !kflag) { 446 print_cur_locale(); 447 /* NOTREACHED */ 448 } 449 if (optind == argc) { 450 usage(); 451 /* NOTREACHED */ 452 } 453 454 for (; optind < argc; optind++) { 455 retval += print_locale_info(argv[optind], cflag, kflag); 456 } 457 return (retval); 458 } 459 460 /* 461 * No options or operands. 462 * Print out the current locale names from the environment, or implied. 463 * Variables directly set in the environment are printed as-is, those 464 * implied are printed in quotes. 465 * The strings are printed ``appropriately quoted for possible later re-entry 466 * to the shell''. We use the routine outstr to do this -- however we 467 * want the shell escape character, the backslash, not the locale escape 468 * character, so we quietly save and restore the locale escape character. 469 */ 470 static void 471 print_cur_locale(void) 472 { 473 char *lc_allp; 474 char *env, *eff; 475 int i; 476 477 if ((env = getenv("LANG")) != NULL) { 478 (void) printf("LANG=%s\n", env); 479 } else { 480 (void) printf("LANG=\n"); 481 } 482 483 lc_allp = getenv("LC_ALL"); 484 485 for (i = 0; i < LC_ALL; i++) { 486 (void) printf("%s=", locale_name[i].name); 487 eff = setlocale(i, NULL); 488 if (eff == NULL) { 489 eff = ""; 490 } 491 env = getenv(locale_name[i].name); 492 493 if (env == NULL) { 494 (void) putchar('"'); 495 outstr(eff); 496 (void) putchar('"'); 497 } else { 498 if (strcmp(env, eff) != 0) { 499 (void) putchar('"'); 500 outstr(eff); 501 (void) putchar('"'); 502 } else { 503 outstr(eff); 504 } 505 } 506 (void) putchar('\n'); 507 } 508 509 (void) printf("LC_ALL="); 510 if (lc_allp != NULL) { 511 outstr(lc_allp); 512 } 513 (void) putchar('\n'); 514 exit(0); 515 } 516 517 static int num_of_loc = 0; 518 static int num_of_entries = 0; 519 static char **entries = NULL; 520 521 static void 522 add_loc_entry(char *loc) 523 { 524 #define _INC_NUM 10 525 char *s; 526 527 if (num_of_loc >= num_of_entries) { 528 char **tmp; 529 num_of_entries += _INC_NUM; 530 tmp = realloc(entries, sizeof (char *) * num_of_entries); 531 if (tmp == NULL) { 532 /* restoring original locale */ 533 (void) setlocale(LC_ALL, save_loc); 534 (void) fprintf(stderr, 535 gettext("locale: cannot allocate buffer")); 536 exit(1); 537 } 538 entries = tmp; 539 } 540 s = strdup(loc); 541 if (s == NULL) { 542 /* restoring original locale */ 543 (void) setlocale(LC_ALL, save_loc); 544 (void) fprintf(stderr, 545 gettext("locale: cannot allocate buffer")); 546 exit(1); 547 } 548 entries[num_of_loc] = s; 549 550 num_of_loc++; 551 } 552 553 static int 554 loccmp(const char **str1, const char **str2) 555 { 556 return (strcmp(*str1, *str2)); 557 } 558 559 static void 560 show_loc_entry(void) 561 { 562 int i; 563 564 qsort(entries, num_of_loc, sizeof (char *), 565 (int (*)(const void *, const void *))loccmp); 566 for (i = 0; i < num_of_loc; i++) { 567 (void) printf("%s\n", entries[i]); 568 } 569 } 570 571 static void 572 check_loc(char *loc) 573 { 574 int cat; 575 576 /* first, try LC_ALL */ 577 if (setlocale(LC_ALL, loc) != NULL) { 578 /* succeeded */ 579 add_loc_entry(loc); 580 return; 581 } 582 583 /* 584 * LC_ALL failed. 585 * try each category. 586 */ 587 for (cat = 0; cat <= _LastCategory; cat++) { 588 if (setlocale(cat, loc) != NULL) { 589 /* succeeded */ 590 add_loc_entry(loc); 591 return; 592 } 593 } 594 595 /* loc is not a valid locale */ 596 } 597 598 /* 599 * print_all_info(): Print out all the locales and 600 * charmaps supported by the system 601 */ 602 static void 603 print_all_info(int flag) 604 { 605 struct dirent *direntp; 606 DIR *dirp; 607 char *filename; /* filename[PATH_MAX] */ 608 char *p; 609 610 if ((filename = malloc(PATH_MAX)) == NULL) { 611 (void) fprintf(stderr, 612 gettext("locale: cannot allocate buffer")); 613 exit(1); 614 } 615 616 (void) memset(filename, 0, PATH_MAX); 617 618 if (flag == GET_LOCALE) { 619 /* save the current locale */ 620 save_loc = setlocale(LC_ALL, NULL); 621 622 (void) strcpy(filename, LOCALE_DIR); 623 add_loc_entry("POSIX"); 624 } else { /* CHARMAP */ 625 (void) strcpy(filename, CHARMAP_DIR); 626 } 627 628 if ((dirp = opendir(filename)) == NULL) { 629 if (flag == GET_LOCALE) 630 exit(0); 631 else { /* CHARMAP */ 632 (void) fprintf(stderr, gettext( 633 "locale: charmap information not available.\n")); 634 exit(2); 635 } 636 } 637 638 p = filename + strlen(filename); 639 while ((direntp = readdir(dirp)) != NULL) { 640 struct stat stbuf; 641 642 (void) strcpy(p, direntp->d_name); 643 if (stat(filename, &stbuf) < 0) { 644 continue; 645 } 646 647 if (flag == GET_LOCALE) { 648 if (S_ISDIR(stbuf.st_mode) && 649 (direntp->d_name[0] != '.') && 650 /* "POSIX" has already been printed out */ 651 strcmp(direntp->d_name, "POSIX") != 0) { 652 check_loc(direntp->d_name); 653 } 654 } else { /* CHARMAP */ 655 if (S_ISDIR(stbuf.st_mode) && 656 direntp->d_name[0] != '.') { 657 struct dirent *direntc; 658 DIR *dirc; 659 char *charmap; 660 char *c; 661 662 if ((charmap = malloc(PATH_MAX)) == NULL) { 663 (void) fprintf(stderr, 664 gettext("locale: cannot allocate buffer")); 665 exit(1); 666 } 667 668 (void) memset(charmap, 0, PATH_MAX); 669 670 (void) strcpy(charmap, filename); 671 672 if ((dirc = opendir(charmap)) == NULL) { 673 exit(0); 674 } 675 676 c = charmap + strlen(charmap); 677 *c++ = '/'; 678 while ((direntc = readdir(dirc)) != NULL) { 679 struct stat stbuf; 680 681 (void) strcpy(c, direntc->d_name); 682 if (stat(charmap, &stbuf) < 0) { 683 continue; 684 } 685 686 if (S_ISREG(stbuf.st_mode) && 687 (strcmp(direntc->d_name, 688 CHARMAP_NAME) == 0) && 689 (direntc->d_name[0] != '.')) { 690 (void) printf("%s/%s\n", 691 p, direntc->d_name); 692 } 693 } 694 (void) closedir(dirc); 695 free(charmap); 696 } 697 } 698 } 699 if (flag == GET_LOCALE) { 700 /* restore the saved loc */ 701 (void) setlocale(LC_ALL, save_loc); 702 show_loc_entry(); 703 } 704 (void) closedir(dirp); 705 free(filename); 706 exit(0); 707 } 708 709 /* 710 * Print out the keyword value or category info. 711 * Call print_category() to print the entire locale category, if the name 712 * given is recognized as a category. 713 * Otherwise, assume that it is a keyword, and call print_keyword(). 714 */ 715 static int 716 print_locale_info(char *name, int cflag, int kflag) 717 { 718 int i; 719 720 for (i = 0; locale_name[i].name != NULL; i++) { 721 if (strcmp(locale_name[i].name, name) == 0) { 722 /* 723 * name is a category name 724 * print out all keywords in this category 725 */ 726 return (print_category(locale_name[i].category, 727 cflag, kflag)); 728 } 729 } 730 731 /* The name is a keyword name */ 732 return (print_keyword(name, cflag, kflag)); 733 } 734 735 /* 736 * Print out the value of the keyword 737 */ 738 static int 739 print_keyword(char *name, int cflag, int kflag) 740 { 741 int i, j; 742 int first_flag = 1; 743 int found = 0; 744 745 for (i = 0; key[i].name != NULL; i++) { 746 if (strcmp(key[i].name, name) != 0) { 747 continue; 748 } 749 750 found = 1; 751 if (first_flag && cflag && key[i].category != LC_LOCDEF) { 752 /* print out this category's name */ 753 (void) printf("%s\n", 754 locale_name[key[i].category].name); 755 } 756 if (kflag) { 757 (void) printf("%s=", name); 758 } 759 switch (key[i].type) { 760 761 /* 762 * The grouping fields are a set of bytes, each of which 763 * is the numeric value of the next group size, terminated 764 * by a \0, or by CHAR_MAX 765 */ 766 case TYPE_GROUP: 767 { 768 void *s; 769 char *q; 770 int first = 1; 771 772 s = (*key[i].structure)(); 773 /* LINTED */ 774 q = *(char **)((char *)s + key[i].offset); 775 if (*q == '\0') { 776 (void) printf("-1"); 777 break; 778 } 779 while (*q != '\0' && *q != CHAR_MAX) { 780 if (!first) { 781 (void) putchar(';'); 782 } 783 first = 0; 784 (void) printf("%u", 785 *(unsigned char *)q++); 786 } 787 /* CHAR_MAX: no further grouping performed. */ 788 if (!first) { 789 (void) putchar(';'); 790 } 791 if (*q == CHAR_MAX) { 792 (void) printf("-1"); 793 } else { 794 (void) putchar('0'); 795 } 796 } 797 break; 798 799 /* 800 * Entries like decimal_point states ``the decimal-point 801 * character...'' not string. However, it is a char *. 802 * This assumes single, narrow, character. 803 * Should it permit multibyte characters? 804 * Should it permit a whole string, in that case? 805 */ 806 case TYPE_STR: 807 { 808 void *s; 809 char **q; 810 811 s = (*key[i].structure)(); 812 /* LINTED */ 813 q = (char **)((char *)s + key[i].offset); 814 for (j = 0; j < key[i].count; j++) { 815 if (j != 0) { 816 (void) printf(";"); 817 } 818 if (kflag) { 819 (void) printf("\""); 820 outstr(q[j]); 821 (void) printf("\""); 822 } else { 823 (void) printf("%s", q[j]); 824 } 825 } 826 } 827 break; 828 829 case TYPE_INT: 830 { 831 void *s; 832 int *q; 833 834 s = (*key[i].structure)(); 835 /* LINTED */ 836 q = (int *)((char *)s + key[i].offset); 837 (void) printf("%d", *q); 838 } 839 break; 840 841 /* 842 * TYPE_CHR: Single byte integer. 843 */ 844 case TYPE_CHR: 845 { 846 void *s; 847 char *q; 848 849 s = (*key[i].structure)(); 850 q = (char *)((char *)s + key[i].offset); 851 if (*q == CHAR_MAX) { 852 (void) printf("-1"); 853 } else { 854 (void) printf("%u", 855 *(unsigned char *)q); 856 } 857 } 858 break; 859 860 /* 861 * TYPE_PCHR: Single byte, printed as a character if printable 862 */ 863 case TYPE_PCHR: 864 { 865 void *s; 866 char *q; 867 868 s = (*key[i].structure)(); 869 q = (char *)((char *)s + key[i].offset); 870 if (isprint(*(unsigned char *)q)) { 871 if (kflag) { 872 (void) printf("\""); 873 if ((*q == '\\') || 874 (*q == ';') || 875 (*q == '"')) { 876 (void) putchar(escapec); 877 (void) printf("%c", 878 *(unsigned char *)q); 879 } else { 880 (void) printf("%c", 881 *(unsigned char *)q); 882 } 883 (void) printf("\""); 884 } else { 885 (void) printf("%c", 886 *(unsigned char *)q); 887 } 888 } else if (*q == (char)-1) { 889 /* In case no signed chars */ 890 (void) printf("-1"); 891 } else { 892 (void) printf("%u", 893 *(unsigned char *)q); 894 } 895 } 896 break; 897 898 case TYPE_CTP: 899 { 900 prt_ctp(key[i].name); 901 } 902 break; 903 904 case TYPE_CNVU: 905 { 906 prt_cnv(key[i].name); 907 } 908 break; 909 910 case TYPE_CNVL: 911 { 912 prt_cnv(key[i].name); 913 } 914 break; 915 916 case TYPE_COLLEL: 917 { 918 prt_collel(key[i].name); 919 } 920 break; 921 } 922 } 923 if (found) { 924 (void) printf("\n"); 925 return (0); 926 } else { 927 (void) fprintf(stderr, 928 gettext("Unknown keyword name '%s'.\n"), name); 929 return (1); 930 } 931 } 932 933 /* 934 * Strings being outputed have to use an unambiguous format -- escape 935 * any potentially bad output characters. 936 * The standard says that any control character shall be preceeded by 937 * the escape character. But it doesn't say that you can format that 938 * character at all. 939 * Question: If the multibyte character contains a quoting character, 940 * should that *byte* be escaped? 941 */ 942 static void 943 outstr(char *s) 944 { 945 wchar_t ws; 946 int c; 947 size_t mbcurmax = MB_CUR_MAX; 948 949 while (*s != '\0') { 950 c = mbtowc(&ws, s, mbcurmax); 951 if (c < 0) { 952 s++; 953 } else if (c == 1) { 954 outchar(*s++); 955 } else { 956 for (; c > 0; c--) { 957 (void) putchar(*s++); 958 } 959 } 960 } 961 } 962 963 static void 964 outchar(int c) 965 { 966 unsigned char uc; 967 968 uc = (unsigned char) c; 969 970 if ((uc == '\\') || (uc == ';') || (uc == '"')) { 971 (void) putchar(escapec); 972 (void) putchar(uc); 973 } else if (iscntrl(uc)) { 974 (void) printf("%cx%02x", escapec, uc); 975 } else { 976 (void) putchar(uc); 977 } 978 } 979 980 /* 981 * print_category(): Print out all the keyword's value 982 * in the given category 983 */ 984 static int 985 print_category(int category, int cflag, int kflag) 986 { 987 int i; 988 int retval = 0; 989 990 if (category == LC_ALL) { 991 retval += print_category(LC_CTYPE, cflag, kflag); 992 retval += print_category(LC_NUMERIC, cflag, kflag); 993 retval += print_category(LC_TIME, cflag, kflag); 994 retval += print_category(LC_COLLATE, cflag, kflag); 995 retval += print_category(LC_MONETARY, cflag, kflag); 996 retval += print_category(LC_MESSAGES, cflag, kflag); 997 } else { 998 if (cflag) { 999 (void) printf("%s\n", 1000 locale_name[category].name); 1001 } 1002 1003 for (i = 0; key[i].name != NULL; i++) { 1004 if (key[i].category == category) { 1005 retval += print_keyword(key[i].name, 0, kflag); 1006 } 1007 } 1008 } 1009 return (retval); 1010 } 1011 1012 /* 1013 * usage message for locale 1014 */ 1015 static void 1016 usage(void) 1017 { 1018 (void) fprintf(stderr, gettext( 1019 "Usage: locale [-a|-m]\n" 1020 " locale [-ck] name ...\n")); 1021 exit(2); 1022 } 1023 1024 static void 1025 prt_ctp(char *name) 1026 { 1027 int idx, i, mem; 1028 int first = 1; 1029 1030 static const char *reg_names[] = { 1031 "upper", "lower", "alpha", "digit", "space", "cntrl", 1032 "punct", "graph", "print", "xdigit", "blank", NULL 1033 }; 1034 for (idx = 0; reg_names[idx] != NULL; idx++) { 1035 if (strcmp(name, reg_names[idx]) == 0) { 1036 break; 1037 } 1038 } 1039 if (reg_names[idx] == NULL) { 1040 return; 1041 } 1042 1043 for (i = 0; i < CSSIZE; i++) { 1044 mem = 0; 1045 switch (idx) { 1046 case 0: 1047 mem = isupper(i); 1048 break; 1049 case 1: 1050 mem = islower(i); 1051 break; 1052 case 2: 1053 mem = isalpha(i); 1054 break; 1055 case 3: 1056 mem = isdigit(i); 1057 break; 1058 case 4: 1059 mem = isspace(i); 1060 break; 1061 case 5: 1062 mem = iscntrl(i); 1063 break; 1064 case 6: 1065 mem = ispunct(i); 1066 break; 1067 case 7: 1068 mem = isgraph(i); 1069 break; 1070 case 8: 1071 mem = isprint(i); 1072 break; 1073 case 9: 1074 mem = isxdigit(i); 1075 break; 1076 case 10: 1077 mem = isblank(i); 1078 break; 1079 } 1080 if (mem) { 1081 if (!first) { 1082 (void) putchar(';'); 1083 } 1084 first = 0; 1085 (void) printf("\""); 1086 outchar(i); 1087 (void) printf("\""); 1088 } 1089 } 1090 } 1091 1092 static void 1093 prt_cnv(char *name) 1094 { 1095 int idx, i, q; 1096 int first = 1; 1097 1098 static const char *reg_names[] = { 1099 "toupper", "tolower", NULL 1100 }; 1101 for (idx = 0; reg_names[idx] != NULL; idx++) { 1102 if (strcmp(name, reg_names[idx]) == 0) { 1103 break; 1104 } 1105 } 1106 if (reg_names[idx] == NULL) { 1107 return; 1108 } 1109 1110 for (i = 0; i < CSSIZE; i++) { 1111 switch (idx) { 1112 case 0: 1113 q = toupper(i); 1114 if (q == i) { 1115 continue; 1116 } 1117 if (!first) { 1118 (void) putchar(';'); 1119 } 1120 first = 0; 1121 /* BEGIN CSTYLED */ 1122 (void) printf("\"<'"); 1123 /* END CSTYLED */ 1124 outchar(i); 1125 (void) printf("','"); 1126 outchar(q); 1127 (void) printf("'>\""); 1128 break; 1129 case 1: 1130 q = tolower(i); 1131 if (q == i) { 1132 continue; 1133 } 1134 if (!first) { 1135 (void) putchar(';'); 1136 } 1137 first = 0; 1138 /* BEGIN CSTYLED */ 1139 (void) printf("\"<'"); 1140 /* END CSTYLED */ 1141 outchar(i); 1142 (void) printf("','"); 1143 outchar(q); 1144 (void) printf("'>\""); 1145 break; 1146 } 1147 } 1148 } 1149 1150 /* 1151 * prt_collel(): Stub for the collate class which does nothing. 1152 */ 1153 /* ARGSUSED */ 1154 static void 1155 prt_collel(char *name) 1156 { 1157 } 1158 1159 static char 1160 get_escapechar(void) 1161 { 1162 return ('\\'); 1163 } 1164 1165 static char 1166 get_commentchar(void) 1167 { 1168 return ('#'); 1169 } 1170