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