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