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