lcFile.c revision 57f47464
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#ifdef __UNIXOS2__ 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#ifndef X_NOT_POSIX 58#ifdef _POSIX_SOURCE 59#include <limits.h> 60#else 61#define _POSIX_SOURCE 62#include <limits.h> 63#undef _POSIX_SOURCE 64#endif 65#endif 66#ifndef PATH_MAX 67#ifdef WIN32 68#define PATH_MAX 512 69#else 70#include <sys/param.h> 71#endif 72#ifndef PATH_MAX 73#ifdef MAXPATHLEN 74#define PATH_MAX MAXPATHLEN 75#else 76#define PATH_MAX 1024 77#endif 78#endif 79#endif 80 81#define NUM_LOCALEDIR 64 82 83/* Splits a NUL terminated line into constituents, at colons and newline 84 characters. Leading whitespace is removed from constituents. The 85 constituents are stored at argv[0..argsize-1]. The number of stored 86 constituents (<= argsize) is returned. The line is destructively 87 modified. */ 88static int 89parse_line( 90 char *line, 91 char **argv, 92 int argsize) 93{ 94 int argc = 0; 95 char *p = line; 96 97 while (argc < argsize) { 98 while (isspace(*p)) { 99 ++p; 100 } 101 if (*p == '\0') { 102 break; 103 } 104 argv[argc++] = p; 105 while (*p != ':' && *p != '\n' && *p != '\0') { 106 ++p; 107 } 108 if (*p == '\0') { 109 break; 110 } 111 *p++ = '\0'; 112 } 113 114 return argc; 115} 116 117#ifdef __UNIXOS2__ 118 119/* fg021216: entries in locale files are separated by colons while under 120 OS/2, path entries are separated by semicolon, so we need two functions */ 121 122static int 123parse_line1( 124 char *line, 125 char **argv, 126 int argsize) 127{ 128 int argc = 0; 129 char *p = line; 130 131 while (argc < argsize) { 132 while (isspace(*p)) { 133 ++p; 134 } 135 if (*p == '\0') { 136 break; 137 } 138 argv[argc++] = p; 139 while (*p != ';' && *p != '\n' && *p != '\0') { 140 ++p; 141 } 142 if (*p == '\0') { 143 break; 144 } 145 *p++ = '\0'; 146 } 147 148 return argc; 149} 150#elif defined(WIN32) 151 152/* this is parse_line but skips drive letters at the beginning of the entry */ 153static int 154parse_line1( 155 char *line, 156 char **argv, 157 int argsize) 158{ 159 int argc = 0; 160 char *p = line; 161 162 while (argc < argsize) { 163 while (isspace(*p)) { 164 ++p; 165 } 166 if (*p == '\0') { 167 break; 168 } 169 argv[argc++] = p; 170 if (isalpha(*p) && p[1] == ':') { 171 p+= 2; /* skip drive letters */ 172 } 173 while (*p != ':' && *p != '\n' && *p != '\0') { 174 ++p; 175 } 176 if (*p == '\0') { 177 break; 178 } 179 *p++ = '\0'; 180 } 181 182 return argc; 183} 184 185#endif /* __UNIXOS2__ */ 186 187/* Splits a colon separated list of directories, and returns the constituent 188 paths (without trailing slash). At most argsize constituents are stored 189 at argv[0..argsize-1]. The number of stored constituents is returned. */ 190static int 191_XlcParsePath( 192 char *path, 193 char **argv, 194 int argsize) 195{ 196 char *p = path; 197 int n, i; 198 199#if !defined(__UNIXOS2__) && !defined(WIN32) 200 n = parse_line(path, argv, argsize); 201#else 202 n = parse_line1(path, argv, argsize); 203#endif 204 for (i = 0; i < n; ++i) { 205 int len; 206 p = argv[i]; 207 len = strlen(p); 208 if (len > 0 && p[len - 1] == '/') { 209 /* eliminate trailing slash */ 210 p[len - 1] = '\0'; 211 } 212 } 213 return n; 214} 215 216#ifndef XLOCALEDIR 217#define XLOCALEDIR "/usr/lib/X11/locale" 218#endif 219 220void 221xlocaledir( 222 char *buf, 223 int buf_len) 224{ 225 char *p = buf; 226 int len = 0; 227 228#ifndef NO_XLOCALEDIR 229 char *dir; 230 int priv = 1; 231 232 dir = getenv("XLOCALEDIR"); 233 234 if (dir) { 235#ifndef WIN32 236 /* 237 * Only use the user-supplied path if the process isn't priviledged. 238 */ 239 if (getuid() == geteuid() && getgid() == getegid()) { 240#if defined(HASSETUGID) 241 priv = issetugid(); 242#elif defined(HASGETRESUID) 243 { 244 uid_t ruid, euid, suid; 245 gid_t rgid, egid, sgid; 246 if ((getresuid(&ruid, &euid, &suid) == 0) && 247 (getresgid(&rgid, &egid, &sgid) == 0)) 248 priv = (euid != suid) || (egid != sgid); 249 } 250#else 251 /* 252 * If there are saved ID's the process might still be priviledged 253 * even though the above test succeeded. If issetugid() and 254 * getresgid() aren't available, test this by trying to set 255 * euid to 0. 256 * 257 * Note: this only protects setuid-root clients. It doesn't 258 * protect other setuid or any setgid clients. If this tradeoff 259 * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def. 260 */ 261 unsigned int oldeuid; 262 oldeuid = geteuid(); 263 if (seteuid(0) != 0) { 264 priv = 0; 265 } else { 266 if (seteuid(oldeuid) == -1) { 267 /* XXX ouch, coudn't get back to original uid 268 what can we do ??? */ 269 _exit(127); 270 } 271 priv = 1; 272 } 273#endif 274 } 275#else 276 priv = 0; 277#endif 278 if (!priv) { 279 len = strlen(dir); 280 strncpy(p, dir, buf_len); 281 if (len < buf_len) { 282 p[len++] = LC_PATHDELIM; 283 p += len; 284 } 285 } 286 } 287#endif /* NO_XLOCALEDIR */ 288 289 if (len < buf_len) 290#ifndef __UNIXOS2__ 291 strncpy(p, XLOCALEDIR, buf_len - len); 292#else 293 strncpy(p,__XOS2RedirRoot(XLOCALEDIR), buf_len - len); 294#endif 295 buf[buf_len-1] = '\0'; 296} 297 298static void 299xlocalelibdir( 300 char *buf, 301 int buf_len) 302{ 303 char *p = buf; 304 int len = 0; 305 306#ifndef NO_XLOCALEDIR 307 char *dir; 308 int priv = 1; 309 310 dir = getenv("XLOCALELIBDIR"); 311 312 if (dir) { 313#ifndef WIN32 314 /* 315 * Only use the user-supplied path if the process isn't priviledged. 316 */ 317 if (getuid() == geteuid() && getgid() == getegid()) { 318#if defined(HASSETUGID) 319 priv = issetugid(); 320#elif defined(HASGETRESUID) 321 { 322 uid_t ruid, euid, suid; 323 gid_t rgid, egid, sgid; 324 if ((getresuid(&ruid, &euid, &suid) == 0) && 325 (getresgid(&rgid, &egid, &sgid) == 0)) 326 priv = (euid != suid) || (egid != sgid); 327 } 328#else 329 /* 330 * If there are saved ID's the process might still be priviledged 331 * even though the above test succeeded. If issetugid() and 332 * getresgid() aren't available, test this by trying to set 333 * euid to 0. 334 * 335 * Note: this only protects setuid-root clients. It doesn't 336 * protect other setuid or any setgid clients. If this tradeoff 337 * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def. 338 */ 339 unsigned int oldeuid; 340 oldeuid = geteuid(); 341 if (seteuid(0) != 0) { 342 priv = 0; 343 } else { 344 if (seteuid(oldeuid) == -1) { 345 /* XXX ouch, coudn't get back to original uid 346 what can we do ??? */ 347 _exit(127); 348 } 349 priv = 1; 350 } 351#endif 352 } 353#else 354 priv = 0; 355#endif 356 if (!priv) { 357 len = strlen(dir); 358 strncpy(p, dir, buf_len); 359 if (len < buf_len) { 360 p[len++] = LC_PATHDELIM; 361 p += len; 362 } 363 } 364 } 365#endif /* NO_XLOCALEDIR */ 366 367 if (len < buf_len) 368#ifndef __UNIXOS2__ 369 strncpy(p, XLOCALELIBDIR, buf_len - len); 370#else 371 strncpy(p,__XOS2RedirRoot(XLOCALELIBDIR), buf_len - len); 372#endif 373 buf[buf_len-1] = '\0'; 374} 375 376/* Mapping direction */ 377typedef enum { 378 LtoR, /* Map first field to second field */ 379 RtoL /* Map second field to first field */ 380} MapDirection; 381 382static char * 383resolve_name( 384 const char *lc_name, 385 char *file_name, 386 MapDirection direction) 387{ 388 FILE *fp; 389 char buf[XLC_BUFSIZE], *name = NULL; 390 391 fp = _XFopenFile (file_name, "r"); 392 if (fp == NULL) 393 return NULL; 394 395 while (fgets(buf, XLC_BUFSIZE, fp) != NULL) { 396 char *p = buf; 397 int n; 398 char *args[2], *from, *to; 399#ifdef __UNIXOS2__ /* Take out CR under OS/2 */ 400 int len; 401 402 len = strlen(p); 403 if (len > 1) { 404 if (*(p+len-2) == '\r' && *(p+len-1) == '\n') { 405 *(p+len-2) = '\n'; 406 *(p+len-1) = '\0'; 407 } 408 } 409#endif 410 while (isspace(*p)) { 411 ++p; 412 } 413 if (iscomment(*p)) { 414 continue; 415 } 416 n = parse_line(p, args, 2); /* get first 2 fields */ 417 if (n != 2) { 418 continue; 419 } 420 if (direction == LtoR) { 421 from = args[0], to = args[1]; /* left to right */ 422 } else { 423 from = args[1], to = args[0]; /* right to left */ 424 } 425 if (! strcmp(from, lc_name)) { 426 name = Xmalloc(strlen(to) + 1); 427 if (name != NULL) { 428 strcpy(name, to); 429 } 430 break; 431 } 432 } 433 fclose(fp); 434 return name; 435} 436 437#define c_tolower(ch) ((ch) >= 'A' && (ch) <= 'Z' ? (ch) - 'A' + 'a' : (ch)) 438 439static char * 440lowercase( 441 char *dst, 442 const char *src) 443{ 444 const char *s; 445 char *t; 446 447 for (s = src, t = dst; *s; ++s, ++t) 448 *t = c_tolower(*s); 449 *t = '\0'; 450 return dst; 451} 452 453/* 454 * normalize_lcname(): remove any '_' and '-' and convert any character 455 * to lower case after the <language>_<territory> part. If result is identical 456 * to argument, free result and 457 * return NULL. 458 */ 459static char * 460normalize_lcname (const char *name) 461{ 462 char *p, *ret; 463 const char *tmp = name; 464 465 p = ret = Xmalloc(strlen(name) + 1); 466 if (!p) 467 return NULL; 468 469 if (tmp) { 470 while (*tmp && *tmp != '.' && *tmp != '@') 471 *p++ = *tmp++; 472 while (*tmp) { 473 if (*tmp != '-') 474 *p++ = c_tolower(*tmp); 475 tmp++; 476 } 477 } 478 *p = '\0'; 479 480 if (strcmp(ret, name) == 0) { 481 Xfree(ret); 482 return NULL; 483 } 484 485 return ret; 486} 487 488/************************************************************************/ 489char * 490_XlcFileName( 491 XLCd lcd, 492 const char *category) 493{ 494 char *siname; 495 char cat[XLC_BUFSIZE], dir[XLC_BUFSIZE]; 496 int i, n; 497 char *args[NUM_LOCALEDIR]; 498 char *file_name = NULL; 499 500 if (lcd == (XLCd)NULL) 501 return NULL; 502 503 siname = XLC_PUBLIC(lcd, siname); 504 505 if (category) 506 lowercase(cat, category); 507 else 508 cat[0] = '\0'; 509 xlocaledir(dir,XLC_BUFSIZE); 510 n = _XlcParsePath(dir, args, NUM_LOCALEDIR); 511 for (i = 0; i < n; ++i) { 512 char buf[PATH_MAX], *name; 513 514 name = NULL; 515 if ((5 + (args[i] ? strlen (args[i]) : 0) + strlen(cat)) < 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(char *buf, int buf_len) 632{ 633 if (buf != NULL) { 634 xlocaledir(buf, buf_len); 635 } 636 return 1; 637} 638 639char * 640_XlcLocaleDirName(char *dir_name, size_t dir_len, char *lc_name) 641{ 642 char dir[PATH_MAX], buf[PATH_MAX], *name = NULL; 643 int i, n; 644 char *args[NUM_LOCALEDIR]; 645 static char locale_alias[] = LOCALE_ALIAS; 646 char *target_name = (char*)0; 647 char *target_dir = (char*)0; 648 char *nlc_name = NULL; 649 static char* last_dir_name = 0; 650 static size_t last_dir_len = 0; 651 static char* last_lc_name = 0; 652 653 if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0 654 && dir_len >= last_dir_len) { 655 strcpy (dir_name, last_dir_name); 656 return dir_name; 657 } 658 659 xlocaledir (dir, PATH_MAX); 660 n = _XlcParsePath(dir, args, 256); 661 for (i = 0; i < n; ++i) { 662 663 if ((2 + (args[i] ? strlen(args[i]) : 0) + 664 strlen(locale_alias)) < PATH_MAX) { 665 sprintf (buf, "%s/%s", args[i], locale_alias); 666 name = resolve_name(lc_name, buf, LtoR); 667 if (!name) { 668 if (!nlc_name) 669 nlc_name = normalize_lcname(lc_name); 670 if (nlc_name) 671 name = resolve_name (nlc_name, buf, LtoR); 672 } 673 } 674 675 /* If name is not an alias, use lc_name for locale.dir search */ 676 if (name == NULL) 677 name = lc_name; 678 679 /* look at locale.dir */ 680 681 target_dir = args[i]; 682 if (!target_dir) { 683 /* something wrong */ 684 if (name != lc_name) 685 Xfree(name); 686 continue; 687 } 688 if ((1 + strlen (target_dir) + strlen("locale.dir")) < PATH_MAX) { 689 sprintf(buf, "%s/locale.dir", target_dir); 690 target_name = resolve_name(name, buf, RtoL); 691 } 692 if (name != lc_name) 693 Xfree(name); 694 if (target_name != NULL) { 695 char *p = 0; 696 if ((p = strstr(target_name, "/XLC_LOCALE"))) { 697 *p = '\0'; 698 break; 699 } 700 Xfree(target_name); 701 target_name = NULL; 702 } 703 name = NULL; 704 } 705 if (nlc_name) Xfree(nlc_name); 706 707 if (target_name == NULL) { 708 /* vendor locale name == Xlocale name, no expansion of alias */ 709 target_dir = args[0]; 710 target_name = lc_name; 711 } 712 /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */ 713 strncpy(dir_name, target_dir, dir_len - 1); 714 if (strlen(target_dir) >= dir_len - 1) { 715 dir_name[dir_len - 1] = '\0'; 716 } else { 717 strcat(dir_name, "/"); 718 strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1); 719 if (strlen(target_name) >= dir_len - strlen(dir_name) - 1) 720 dir_name[dir_len - 1] = '\0'; 721 } 722 if (target_name != lc_name) 723 Xfree(target_name); 724 725 if (last_dir_name != 0) 726 Xfree (last_dir_name); 727 if (last_lc_name != 0) 728 Xfree (last_lc_name); 729 last_dir_len = strlen (dir_name) + 1; 730 last_dir_name = Xmalloc (last_dir_len); 731 strcpy (last_dir_name, dir_name); 732 last_lc_name = Xmalloc (strlen (lc_name) + 1); 733 strcpy (last_lc_name, lc_name); 734 735 return dir_name; 736} 737 738char * 739_XlcLocaleLibDirName(char *dir_name, size_t dir_len, char *lc_name) 740{ 741 char dir[PATH_MAX], buf[PATH_MAX], *name = NULL; 742 int i, n; 743 char *args[NUM_LOCALEDIR]; 744 static char locale_alias[] = LOCALE_ALIAS; 745 char *target_name = (char*)0; 746 char *target_dir = (char*)0; 747 char *nlc_name = NULL; 748 static char* last_dir_name = 0; 749 static size_t last_dir_len = 0; 750 static char* last_lc_name = 0; 751 752 if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0 753 && dir_len >= last_dir_len) { 754 strcpy (dir_name, last_dir_name); 755 return dir_name; 756 } 757 758 xlocalelibdir (dir, PATH_MAX); 759 n = _XlcParsePath(dir, args, 256); 760 for (i = 0; i < n; ++i) { 761 762 if ((2 + (args[i] ? strlen(args[i]) : 0) + 763 strlen(locale_alias)) < PATH_MAX) { 764 sprintf (buf, "%s/%s", args[i], locale_alias); 765 name = resolve_name(lc_name, buf, LtoR); 766 if (!name) { 767 if (!nlc_name) 768 nlc_name = normalize_lcname(lc_name); 769 if (nlc_name) 770 name = resolve_name (nlc_name, buf, LtoR); 771 } 772 } 773 774 /* If name is not an alias, use lc_name for locale.dir search */ 775 if (name == NULL) 776 name = lc_name; 777 778 /* look at locale.dir */ 779 780 target_dir = args[i]; 781 if (!target_dir) { 782 /* something wrong */ 783 if (name != lc_name) 784 Xfree(name); 785 continue; 786 } 787 if ((1 + strlen (target_dir) + strlen("locale.dir")) < PATH_MAX) { 788 sprintf(buf, "%s/locale.dir", target_dir); 789 target_name = resolve_name(name, buf, RtoL); 790 } 791 if (name != lc_name) 792 Xfree(name); 793 if (target_name != NULL) { 794 char *p = 0; 795 if ((p = strstr(target_name, "/XLC_LOCALE"))) { 796 *p = '\0'; 797 break; 798 } 799 Xfree(target_name); 800 target_name = NULL; 801 } 802 name = NULL; 803 } 804 if (nlc_name) Xfree(nlc_name); 805 806 if (target_name == NULL) { 807 /* vendor locale name == Xlocale name, no expansion of alias */ 808 target_dir = args[0]; 809 target_name = lc_name; 810 } 811 /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */ 812 strncpy(dir_name, target_dir, dir_len - 1); 813 if (strlen(target_dir) >= dir_len - 1) { 814 dir_name[dir_len - 1] = '\0'; 815 } else { 816 strcat(dir_name, "/"); 817 strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1); 818 if (strlen(target_name) >= dir_len - strlen(dir_name) - 1) 819 dir_name[dir_len - 1] = '\0'; 820 } 821 if (target_name != lc_name) 822 Xfree(target_name); 823 824 if (last_dir_name != 0) 825 Xfree (last_dir_name); 826 if (last_lc_name != 0) 827 Xfree (last_lc_name); 828 last_dir_len = strlen (dir_name) + 1; 829 last_dir_name = Xmalloc (last_dir_len); 830 strcpy (last_dir_name, dir_name); 831 last_lc_name = Xmalloc (strlen (lc_name) + 1); 832 strcpy (last_lc_name, lc_name); 833 834 return dir_name; 835} 836