lcFile.c revision 9c019ec5
1/* 2 * 3 * Copyright IBM Corporation 1993 4 * 5 * All Rights Reserved 6 * 7 * License to use, copy, modify, and distribute this software and its 8 * documentation for any purpose and without fee is hereby granted, 9 * provided that the above copyright notice appear in all copies and that 10 * both that copyright notice and this permission notice appear in 11 * supporting documentation, and that the name of IBM not be 12 * used in advertising or publicity pertaining to distribution of the 13 * software without specific, written prior permission. 14 * 15 * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING 16 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS, AND 17 * NONINFRINGEMENT OF THIRD PARTY RIGHTS, IN NO EVENT SHALL 18 * IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR 19 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 20 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, 21 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 22 * SOFTWARE. 23 * 24*/ 25 26#ifdef HAVE_CONFIG_H 27#include <config.h> 28#endif 29#include <stdlib.h> 30#include <stdio.h> 31#include <ctype.h> 32#include "Xlibint.h" 33#include "XlcPubI.h" 34#include <X11/Xos.h> 35#include <unistd.h> 36 37/************************************************************************/ 38 39#ifndef HAVE_SETEUID 40# define seteuid setuid 41#endif 42#define iscomment(ch) ((ch) == '#' || (ch) == '\0') 43#if defined(WIN32) 44#define isreadable(f) (_XAccessFile(f)) 45#else 46#define isreadable(f) ((access((f), R_OK) != -1) ? 1 : 0) 47#endif 48 49#ifndef __UNIXOS2__ 50#define LC_PATHDELIM ':' 51#else 52#define LC_PATHDELIM ';' 53#endif 54 55#define XLC_BUFSIZE 256 56 57#include "pathmax.h" 58 59#define NUM_LOCALEDIR 64 60 61/* Splits a NUL terminated line into constituents, at colons and newline 62 characters. Leading whitespace is removed from constituents. The 63 constituents are stored at argv[0..argsize-1]. The number of stored 64 constituents (<= argsize) is returned. The line is destructively 65 modified. */ 66static int 67parse_line( 68 char *line, 69 char **argv, 70 int argsize) 71{ 72 int argc = 0; 73 char *p = line; 74 75 while (argc < argsize) { 76 while (isspace(*p)) { 77 ++p; 78 } 79 if (*p == '\0') { 80 break; 81 } 82 argv[argc++] = p; 83 while (*p != ':' && *p != '\n' && *p != '\0') { 84 ++p; 85 } 86 if (*p == '\0') { 87 break; 88 } 89 *p++ = '\0'; 90 } 91 92 return argc; 93} 94 95#ifdef __UNIXOS2__ 96 97/* fg021216: entries in locale files are separated by colons while under 98 OS/2, path entries are separated by semicolon, so we need two functions */ 99 100static int 101parse_line1( 102 char *line, 103 char **argv, 104 int argsize) 105{ 106 int argc = 0; 107 char *p = line; 108 109 while (argc < argsize) { 110 while (isspace(*p)) { 111 ++p; 112 } 113 if (*p == '\0') { 114 break; 115 } 116 argv[argc++] = p; 117 while (*p != ';' && *p != '\n' && *p != '\0') { 118 ++p; 119 } 120 if (*p == '\0') { 121 break; 122 } 123 *p++ = '\0'; 124 } 125 126 return argc; 127} 128#elif defined(WIN32) 129 130/* this is parse_line but skips drive letters at the beginning of the entry */ 131static int 132parse_line1( 133 char *line, 134 char **argv, 135 int argsize) 136{ 137 int argc = 0; 138 char *p = line; 139 140 while (argc < argsize) { 141 while (isspace(*p)) { 142 ++p; 143 } 144 if (*p == '\0') { 145 break; 146 } 147 argv[argc++] = p; 148 if (isalpha(*p) && p[1] == ':') { 149 p+= 2; /* skip drive letters */ 150 } 151 while (*p != ':' && *p != '\n' && *p != '\0') { 152 ++p; 153 } 154 if (*p == '\0') { 155 break; 156 } 157 *p++ = '\0'; 158 } 159 160 return argc; 161} 162 163#endif /* __UNIXOS2__ */ 164 165/* Splits a colon separated list of directories, and returns the constituent 166 paths (without trailing slash). At most argsize constituents are stored 167 at argv[0..argsize-1]. The number of stored constituents is returned. */ 168static int 169_XlcParsePath( 170 char *path, 171 char **argv, 172 int argsize) 173{ 174 char *p = path; 175 int n, i; 176 177#if !defined(__UNIXOS2__) && !defined(WIN32) 178 n = parse_line(path, argv, argsize); 179#else 180 n = parse_line1(path, argv, argsize); 181#endif 182 for (i = 0; i < n; ++i) { 183 int len; 184 p = argv[i]; 185 len = (int) strlen(p); 186 if (len > 0 && p[len - 1] == '/') { 187 /* eliminate trailing slash */ 188 p[len - 1] = '\0'; 189 } 190 } 191 return n; 192} 193 194#ifndef XLOCALEDIR 195#define XLOCALEDIR "/usr/lib/X11/locale" 196#endif 197 198void 199xlocaledir( 200 char *buf, 201 int buf_len) 202{ 203 char *p = buf; 204 int len = 0; 205 206#ifndef NO_XLOCALEDIR 207 char *dir; 208 int priv = 1; 209 210 dir = getenv("XLOCALEDIR"); 211 212 if (dir) { 213#ifndef WIN32 214 /* 215 * Only use the user-supplied path if the process isn't privileged. 216 */ 217 if (getuid() == geteuid() && getgid() == getegid()) { 218#if defined(HASSETUGID) 219 priv = issetugid(); 220#elif defined(HASGETRESUID) 221 { 222 uid_t ruid, euid, suid; 223 gid_t rgid, egid, sgid; 224 if ((getresuid(&ruid, &euid, &suid) == 0) && 225 (getresgid(&rgid, &egid, &sgid) == 0)) 226 priv = (euid != suid) || (egid != sgid); 227 } 228#else 229 /* 230 * If there are saved ID's the process might still be privileged 231 * even though the above test succeeded. If issetugid() and 232 * getresgid() aren't available, test this by trying to set 233 * euid to 0. 234 * 235 * Note: this only protects setuid-root clients. It doesn't 236 * protect other setuid or any setgid clients. If this tradeoff 237 * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def. 238 */ 239 unsigned int oldeuid; 240 oldeuid = geteuid(); 241 if (seteuid(0) != 0) { 242 priv = 0; 243 } else { 244 if (seteuid(oldeuid) == -1) { 245 /* XXX ouch, couldn't get back to original uid 246 what can we do ??? */ 247 _exit(127); 248 } 249 priv = 1; 250 } 251#endif 252 } 253#else 254 priv = 0; 255#endif 256 if (!priv) { 257 len = (int) strlen(dir); 258 strncpy(p, dir, (size_t) buf_len); 259 if (len < buf_len) { 260 p[len++] = LC_PATHDELIM; 261 p += len; 262 } 263 } 264 } 265#endif /* NO_XLOCALEDIR */ 266 267 if (len < buf_len) 268#ifndef __UNIXOS2__ 269 strncpy(p, XLOCALEDIR, (size_t) (buf_len - len)); 270#else 271 strncpy(p,__XOS2RedirRoot(XLOCALEDIR), buf_len - len); 272#endif 273 buf[buf_len-1] = '\0'; 274} 275 276static void 277xlocalelibdir( 278 char *buf, 279 int buf_len) 280{ 281 char *p = buf; 282 int len = 0; 283 284#ifndef NO_XLOCALEDIR 285 char *dir; 286 int priv = 1; 287 288 dir = getenv("XLOCALELIBDIR"); 289 290 if (dir) { 291#ifndef WIN32 292 /* 293 * Only use the user-supplied path if the process isn't privileged. 294 */ 295 if (getuid() == geteuid() && getgid() == getegid()) { 296#if defined(HASSETUGID) 297 priv = issetugid(); 298#elif defined(HASGETRESUID) 299 { 300 uid_t ruid, euid, suid; 301 gid_t rgid, egid, sgid; 302 if ((getresuid(&ruid, &euid, &suid) == 0) && 303 (getresgid(&rgid, &egid, &sgid) == 0)) 304 priv = (euid != suid) || (egid != sgid); 305 } 306#else 307 /* 308 * If there are saved ID's the process might still be privileged 309 * even though the above test succeeded. If issetugid() and 310 * getresgid() aren't available, test this by trying to set 311 * euid to 0. 312 * 313 * Note: this only protects setuid-root clients. It doesn't 314 * protect other setuid or any setgid clients. If this tradeoff 315 * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def. 316 */ 317 unsigned int oldeuid; 318 oldeuid = geteuid(); 319 if (seteuid(0) != 0) { 320 priv = 0; 321 } else { 322 if (seteuid(oldeuid) == -1) { 323 /* XXX ouch, couldn't get back to original uid 324 what can we do ??? */ 325 _exit(127); 326 } 327 priv = 1; 328 } 329#endif 330 } 331#else 332 priv = 0; 333#endif 334 if (!priv) { 335 len = (int) strlen(dir); 336 strncpy(p, dir, (size_t) buf_len); 337 if (len < buf_len) { 338 p[len++] = LC_PATHDELIM; 339 p += len; 340 } 341 } 342 } 343#endif /* NO_XLOCALEDIR */ 344 345 if (len < buf_len) 346#ifndef __UNIXOS2__ 347 strncpy(p, XLOCALELIBDIR, (size_t) (buf_len - len)); 348#else 349 strncpy(p,__XOS2RedirRoot(XLOCALELIBDIR), (size_t) (buf_len - len)); 350#endif 351 buf[buf_len-1] = '\0'; 352} 353 354/* Mapping direction */ 355typedef enum { 356 LtoR, /* Map first field to second field */ 357 RtoL /* Map second field to first field */ 358} MapDirection; 359 360static char * 361resolve_name( 362 const char *lc_name, 363 char *file_name, 364 MapDirection direction) 365{ 366 FILE *fp; 367 char buf[XLC_BUFSIZE], *name = NULL; 368 369 fp = _XFopenFile (file_name, "r"); 370 if (fp == NULL) 371 return NULL; 372 373 while (fgets(buf, XLC_BUFSIZE, fp) != NULL) { 374 char *p = buf; 375 int n; 376 char *args[2], *from, *to; 377#ifdef __UNIXOS2__ /* Take out CR under OS/2 */ 378 int len; 379 380 len = strlen(p); 381 if (len > 1) { 382 if (*(p+len-2) == '\r' && *(p+len-1) == '\n') { 383 *(p+len-2) = '\n'; 384 *(p+len-1) = '\0'; 385 } 386 } 387#endif 388 while (isspace(*p)) { 389 ++p; 390 } 391 if (iscomment(*p)) { 392 continue; 393 } 394 n = parse_line(p, args, 2); /* get first 2 fields */ 395 if (n != 2) { 396 continue; 397 } 398 if (direction == LtoR) { 399 from = args[0], to = args[1]; /* left to right */ 400 } else { 401 from = args[1], to = args[0]; /* right to left */ 402 } 403 if (! strcmp(from, lc_name)) { 404 name = strdup(to); 405 break; 406 } 407 } 408 fclose(fp); 409 return name; 410} 411 412#define c_tolower(ch) ((ch) >= 'A' && (ch) <= 'Z' ? (ch) - 'A' + 'a' : (ch)) 413 414static char * 415lowercase( 416 char *dst, 417 const char *src) 418{ 419 const char *s; 420 char *t; 421 422 for (s = src, t = dst; *s; ++s, ++t) 423 *t = (char) c_tolower(*s); 424 *t = '\0'; 425 return dst; 426} 427 428/* 429 * normalize_lcname(): remove any '_' and '-' and convert any character 430 * to lower case after the <language>_<territory> part. If result is identical 431 * to argument, free result and 432 * return NULL. 433 */ 434static char * 435normalize_lcname (const char *name) 436{ 437 char *p, *ret; 438 const char *tmp = name; 439 440 p = ret = Xmalloc(strlen(name) + 1); 441 if (!p) 442 return NULL; 443 444 if (tmp) { 445 while (*tmp && *tmp != '.' && *tmp != '@') 446 *p++ = *tmp++; 447 while (*tmp) { 448 if (*tmp != '-') 449 *p++ = (char) c_tolower(*tmp); 450 tmp++; 451 } 452 } 453 *p = '\0'; 454 455 if (strcmp(ret, name) == 0) { 456 Xfree(ret); 457 return NULL; 458 } 459 460 return ret; 461} 462 463/************************************************************************/ 464char * 465_XlcFileName( 466 XLCd lcd, 467 const char *category) 468{ 469 char *siname; 470 char cat[XLC_BUFSIZE], dir[XLC_BUFSIZE]; 471 int i, n; 472 char *args[NUM_LOCALEDIR]; 473 char *file_name = NULL; 474 475 if (lcd == (XLCd)NULL) 476 return NULL; 477 478 siname = XLC_PUBLIC(lcd, siname); 479 480 if (category) 481 lowercase(cat, category); 482 else 483 cat[0] = '\0'; 484 xlocaledir(dir,XLC_BUFSIZE); 485 n = _XlcParsePath(dir, args, NUM_LOCALEDIR); 486 for (i = 0; i < n; ++i) { 487 char buf[PATH_MAX], *name; 488 489 if (args[i] == NULL) 490 continue; 491 492 name = NULL; 493 if (snprintf(buf, PATH_MAX, "%s/%s.dir", args[i], cat) < PATH_MAX) { 494 name = resolve_name(siname, buf, RtoL); 495 } 496 if (name == NULL) { 497 continue; 498 } 499 if (*name == '/') { 500 /* supposed to be absolute path name */ 501 file_name = name; 502 } else { 503 if (snprintf(buf, PATH_MAX, "%s/%s", args[i], name) < PATH_MAX) 504 file_name = strdup(buf); 505 else 506 file_name = NULL; 507 Xfree(name); 508 } 509 if (file_name && isreadable(file_name)) { 510 break; 511 } 512 Xfree(file_name); 513 file_name = NULL; 514 /* Then, try with next dir */ 515 } 516 return file_name; 517} 518 519/************************************************************************/ 520#ifndef LOCALE_ALIAS 521#define LOCALE_ALIAS "locale.alias" 522#endif 523 524int 525_XlcResolveLocaleName( 526 const char* lc_name, 527 XLCdPublicPart* pub) 528{ 529 char dir[PATH_MAX], buf[PATH_MAX], *name = NULL; 530 char *dst; 531 int i, n, sinamelen; 532 char *args[NUM_LOCALEDIR]; 533 static const char locale_alias[] = LOCALE_ALIAS; 534 char *tmp_siname; 535 char *nlc_name = NULL; 536 537 xlocaledir (dir, PATH_MAX); 538 n = _XlcParsePath(dir, args, NUM_LOCALEDIR); 539 for (i = 0; i < n; ++i) { 540 if (args[i] == NULL) 541 continue; 542 543 if (snprintf (buf, PATH_MAX, "%s/%s", args[i], locale_alias) 544 < PATH_MAX) { 545 name = resolve_name (lc_name, buf, LtoR); 546 if (!name) { 547 if (!nlc_name) 548 nlc_name = normalize_lcname(lc_name); 549 if (nlc_name) 550 name = resolve_name (nlc_name, buf, LtoR); 551 } 552 } 553 if (name != NULL) { 554 break; 555 } 556 } 557 Xfree(nlc_name); 558 559 if (name == NULL) { 560 /* vendor locale name == Xlocale name, no expansion of alias */ 561 pub->siname = strdup (lc_name); 562 } else { 563 pub->siname = name; 564 } 565 566 sinamelen = (int) strlen (pub->siname); 567 if (sinamelen == 1 && pub->siname[0] == 'C') { 568 pub->language = pub->siname; 569 pub->territory = pub->codeset = NULL; 570 return 1; 571 } 572 573 /* 574 * pub->siname is in the format <lang>_<terr>.<codeset>, typical would 575 * be "en_US.ISO8859-1", "en_US.utf8", "ru_RU.KOI-8", or ja_JP.SJIS, 576 * although it could be ja.SJIS too. 577 */ 578 tmp_siname = Xrealloc (pub->siname, 2 * (sinamelen + 1)); 579 if (tmp_siname == NULL) { 580 return 0; 581 } 582 pub->siname = tmp_siname; 583 584 /* language */ 585 dst = &pub->siname[sinamelen + 1]; 586 strcpy (dst, pub->siname); 587 pub->language = dst; 588 589 /* territory */ 590 dst = strchr (dst, '_'); 591 if (dst) { 592 *dst = '\0'; 593 pub->territory = ++dst; 594 } else 595 dst = &pub->siname[sinamelen + 1]; 596 597 /* codeset */ 598 dst = strchr (dst, '.'); 599 if (dst) { 600 *dst = '\0'; 601 pub->codeset = ++dst; 602 } 603 604 return (pub->siname[0] != '\0') ? 1 : 0; 605} 606 607/************************************************************************/ 608int 609_XlcResolveI18NPath(char *buf, int buf_len) 610{ 611 if (buf != NULL) { 612 xlocaledir(buf, buf_len); 613 } 614 return 1; 615} 616 617char * 618_XlcLocaleDirName(char *dir_name, size_t dir_len, const char *lc_name) 619{ 620 char dir[PATH_MAX], buf[PATH_MAX]; 621 int i, n; 622 char *args[NUM_LOCALEDIR]; 623 static char locale_alias[] = LOCALE_ALIAS; 624 char *target_name = NULL; 625 char *target_dir = NULL; 626 char *nlc_name = NULL; 627 static char* last_dir_name = 0; 628 static size_t last_dir_len = 0; 629 static char* last_lc_name = 0; 630 631 if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0 632 && dir_len >= last_dir_len) { 633 strcpy (dir_name, last_dir_name); 634 return dir_name; 635 } 636 637 xlocaledir (dir, PATH_MAX); 638 n = _XlcParsePath(dir, args, NUM_LOCALEDIR); 639 for (i = 0; i < n; ++i) { 640 char *name = NULL; 641 642 if (args[i] == NULL) 643 continue; 644 645 if (snprintf (buf, PATH_MAX, "%s/%s", args[i], locale_alias) 646 < PATH_MAX) { 647 name = resolve_name(lc_name, buf, LtoR); 648 if (!name) { 649 if (!nlc_name) 650 nlc_name = normalize_lcname(lc_name); 651 if (nlc_name) 652 name = resolve_name (nlc_name, buf, LtoR); 653 } 654 } 655 656 /* look at locale.dir */ 657 658 target_dir = args[i]; 659 if (snprintf(buf, PATH_MAX, "%s/locale.dir", target_dir) < PATH_MAX) { 660 /* If name is not an alias, use lc_name for locale.dir search */ 661 target_name = resolve_name(name ? name : lc_name, buf, RtoL); 662 } 663 Xfree(name); 664 name = NULL; 665 if (target_name != NULL) { 666 char *p = 0; 667 if ((p = strstr(target_name, "/XLC_LOCALE"))) { 668 *p = '\0'; 669 break; 670 } 671 Xfree(target_name); 672 target_name = NULL; 673 } 674 } 675 Xfree(nlc_name); 676 677 if (target_name == NULL) 678 /* vendor locale name == Xlocale name, no expansion of alias */ 679 snprintf(dir_name, dir_len, "%s/%s", args[0], lc_name); 680 else 681 snprintf(dir_name, dir_len, "%s/%s", target_dir, target_name); 682 683 Xfree(target_name); 684 Xfree (last_dir_name); 685 Xfree (last_lc_name); 686 687 last_dir_len = strlen (dir_name) + 1; 688 last_dir_name = Xmalloc (last_dir_len); 689 strcpy (last_dir_name, dir_name); 690 last_lc_name = strdup (lc_name); 691 692 return dir_name; 693} 694 695char * 696_XlcLocaleLibDirName(char *dir_name, size_t dir_len, const char *lc_name) 697{ 698 char dir[PATH_MAX], buf[PATH_MAX]; 699 int i, n; 700 char *args[NUM_LOCALEDIR]; 701 static char locale_alias[] = LOCALE_ALIAS; 702 char *target_name = NULL; 703 char *target_dir = NULL; 704 char *nlc_name = NULL; 705 static char* last_dir_name = 0; 706 static size_t last_dir_len = 0; 707 static char* last_lc_name = 0; 708 709 if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0 710 && dir_len >= last_dir_len) { 711 strcpy (dir_name, last_dir_name); 712 return dir_name; 713 } 714 715 xlocalelibdir (dir, PATH_MAX); 716 n = _XlcParsePath(dir, args, NUM_LOCALEDIR); 717 for (i = 0; i < n; ++i) { 718 char *name = NULL; 719 720 if (args[i] == NULL) 721 continue; 722 723 if (snprintf (buf, PATH_MAX, "%s/%s", args[i], locale_alias) 724 < PATH_MAX) { 725 name = resolve_name(lc_name, buf, LtoR); 726 if (!name) { 727 if (!nlc_name) 728 nlc_name = normalize_lcname(lc_name); 729 if (nlc_name) 730 name = resolve_name (nlc_name, buf, LtoR); 731 } 732 } 733 734 /* look at locale.dir */ 735 736 target_dir = args[i]; 737 if (snprintf(buf, PATH_MAX, "%s/locale.dir", target_dir) < PATH_MAX) { 738 /* If name is not an alias, use lc_name for locale.dir search */ 739 target_name = resolve_name(name ? name : lc_name, buf, RtoL); 740 } 741 Xfree(name); 742 name = NULL; 743 if (target_name != NULL) { 744 char *p = 0; 745 if ((p = strstr(target_name, "/XLC_LOCALE"))) { 746 *p = '\0'; 747 break; 748 } 749 Xfree(target_name); 750 target_name = NULL; 751 } 752 } 753 Xfree(nlc_name); 754 755 if (target_name == NULL) 756 /* vendor locale name == Xlocale name, no expansion of alias */ 757 snprintf(dir_name, dir_len, "%s/%s", args[0], lc_name); 758 else 759 snprintf(dir_name, dir_len, "%s/%s", target_dir, target_name); 760 Xfree(target_name); 761 Xfree (last_dir_name); 762 Xfree (last_lc_name); 763 764 last_dir_len = strlen (dir_name) + 1; 765 last_dir_name = Xmalloc (last_dir_len); 766 strcpy (last_dir_name, dir_name); 767 last_lc_name = strdup (lc_name); 768 769 return dir_name; 770} 771