Tizen 2.1 base
[framework/uifw/xorg/lib/libx11.git] / src / xlibi18n / lcFile.c
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 #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. */
88 static int
89 parse_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
122 static int
123 parse_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 */
153 static int
154 parse_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. */
190 static 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
220 void
221 xlocaledir(
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
298 static void
299 xlocalelibdir(
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 */
377 typedef enum {
378   LtoR,         /* Map first field to second field */
379   RtoL          /* Map second field to first field */
380 } MapDirection;
381
382 static char *
383 resolve_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 = strdup(to);
427             break;
428         }
429     }
430     fclose(fp);
431     return name;
432 }
433
434 #define c_tolower(ch)   ((ch) >= 'A' && (ch) <= 'Z' ? (ch) - 'A' + 'a' : (ch))
435
436 static char *
437 lowercase(
438     char *dst,
439     const char *src)
440 {
441     const char *s;
442     char *t;
443
444     for (s = src, t = dst; *s; ++s, ++t)
445         *t = c_tolower(*s);
446     *t = '\0';
447     return dst;
448 }
449
450 /*
451  * normalize_lcname(): remove any '_' and '-' and convert any character
452  * to lower case after the <language>_<territory> part. If result is identical
453  * to argument, free result and
454  * return NULL.
455  */
456 static char *
457 normalize_lcname (const char *name)
458 {
459     char *p, *ret;
460     const char *tmp = name;
461
462     p = ret = Xmalloc(strlen(name) + 1);
463     if (!p)
464         return NULL;
465
466     if (tmp) {
467         while (*tmp && *tmp != '.' && *tmp != '@')
468             *p++ = *tmp++;
469         while (*tmp) {
470             if (*tmp != '-')
471                 *p++ = c_tolower(*tmp);
472             tmp++;
473         }
474     }
475     *p = '\0';
476
477     if (strcmp(ret, name) == 0) {
478         Xfree(ret);
479         return NULL;
480     }
481
482     return ret;
483 }
484
485 /************************************************************************/
486 char *
487 _XlcFileName(
488     XLCd lcd,
489     const char *category)
490 {
491     char *siname;
492     char cat[XLC_BUFSIZE], dir[XLC_BUFSIZE];
493     int i, n;
494     char *args[NUM_LOCALEDIR];
495     char *file_name = NULL;
496
497     if (lcd == (XLCd)NULL)
498         return NULL;
499
500     siname = XLC_PUBLIC(lcd, siname);
501
502     if (category)
503         lowercase(cat, category);
504     else
505         cat[0] = '\0';
506     xlocaledir(dir,XLC_BUFSIZE);
507     n = _XlcParsePath(dir, args, NUM_LOCALEDIR);
508     for (i = 0; i < n; ++i) {
509         char buf[PATH_MAX], *name;
510
511         name = NULL;
512         if ((5 + (args[i] ? strlen (args[i]) : 0) + strlen(cat)) < PATH_MAX) {
513             sprintf(buf, "%s/%s.dir", args[i], cat);
514             name = resolve_name(siname, buf, RtoL);
515         }
516         if (name == NULL) {
517             continue;
518         }
519         if (*name == '/') {
520             /* supposed to be absolute path name */
521             file_name = name;
522         } else {
523             file_name = Xmalloc(2 + (args[i] ? strlen (args[i]) : 0) +
524                                 (name ? strlen (name) : 0));
525             if (file_name != NULL)
526                 sprintf(file_name, "%s/%s", args[i], name);
527             Xfree(name);
528         }
529         if (isreadable(file_name)) {
530             break;
531         }
532         Xfree(file_name);
533         file_name = NULL;
534         /* Then, try with next dir */
535     }
536     return file_name;
537 }
538
539 /************************************************************************/
540 #ifndef LOCALE_ALIAS
541 #define LOCALE_ALIAS    "locale.alias"
542 #endif
543
544 int
545 _XlcResolveLocaleName(
546     const char* lc_name,
547     XLCdPublicPart* pub)
548 {
549     char dir[PATH_MAX], buf[PATH_MAX], *name = NULL;
550     char *dst;
551     int i, n, sinamelen;
552     char *args[NUM_LOCALEDIR];
553     static const char locale_alias[] = LOCALE_ALIAS;
554     char *tmp_siname;
555     char *nlc_name = NULL;
556
557     xlocaledir (dir, PATH_MAX);
558     n = _XlcParsePath(dir, args, NUM_LOCALEDIR);
559     for (i = 0; i < n; ++i) {
560         if ((2 + (args[i] ? strlen (args[i]) : 0) +
561             strlen (locale_alias)) < PATH_MAX) {
562             sprintf (buf, "%s/%s", args[i], locale_alias);
563             name = resolve_name (lc_name, buf, LtoR);
564             if (!name) {
565                 if (!nlc_name)
566                     nlc_name = normalize_lcname(lc_name);
567                 if (nlc_name)
568                     name = resolve_name (nlc_name, buf, LtoR);
569             }
570         }
571         if (name != NULL) {
572             break;
573         }
574     }
575     if (nlc_name) Xfree(nlc_name);
576
577     if (name == NULL) {
578         /* vendor locale name == Xlocale name, no expansion of alias */
579         pub->siname = strdup (lc_name);
580     } else {
581         pub->siname = name;
582     }
583
584     sinamelen = strlen (pub->siname);
585     if (sinamelen == 1 && pub->siname[0] == 'C') {
586         pub->language = pub->siname;
587         pub->territory = pub->codeset = NULL;
588         return 1;
589     }
590
591     /*
592      * pub->siname is in the format <lang>_<terr>.<codeset>, typical would
593      * be "en_US.ISO8859-1", "en_US.utf8", "ru_RU.KOI-8", or ja_JP.SJIS,
594      * although it could be ja.SJIS too.
595      */
596     tmp_siname = Xrealloc (pub->siname, 2 * (sinamelen + 1));
597     if (tmp_siname == NULL) {
598         return 0;
599     }
600     pub->siname = tmp_siname;
601
602     /* language */
603     dst = &pub->siname[sinamelen + 1];
604     strcpy (dst, pub->siname);
605     pub->language = dst;
606
607     /* territory */
608     dst = strchr (dst, '_');
609     if (dst) {
610         *dst = '\0';
611         pub->territory = ++dst;
612     } else
613         dst = &pub->siname[sinamelen + 1];
614
615     /* codeset */
616     dst = strchr (dst, '.');
617     if (dst) {
618         *dst = '\0';
619         pub->codeset = ++dst;
620     }
621
622     return (pub->siname[0] != '\0') ? 1 : 0;
623 }
624
625 /************************************************************************/
626 int
627 _XlcResolveI18NPath(char *buf, int buf_len)
628 {
629     if (buf != NULL) {
630         xlocaledir(buf, buf_len);
631     }
632     return 1;
633 }
634
635 char *
636 _XlcLocaleDirName(char *dir_name, size_t dir_len, char *lc_name)
637 {
638     char dir[PATH_MAX], buf[PATH_MAX], *name = NULL;
639     int i, n;
640     char *args[NUM_LOCALEDIR];
641     static char locale_alias[] = LOCALE_ALIAS;
642     char *target_name = (char*)0;
643     char *target_dir = (char*)0;
644     char *nlc_name = NULL;
645     static char*  last_dir_name = 0;
646     static size_t last_dir_len = 0;
647     static char*  last_lc_name = 0;
648
649     if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0
650        && dir_len >= last_dir_len) {
651         strcpy (dir_name, last_dir_name);
652         return dir_name;
653     }
654
655     xlocaledir (dir, PATH_MAX);
656     n = _XlcParsePath(dir, args, 256);
657     for (i = 0; i < n; ++i) {
658
659         if ((2 + (args[i] ? strlen(args[i]) : 0) +
660              strlen(locale_alias)) < PATH_MAX) {
661             sprintf (buf, "%s/%s", args[i], locale_alias);
662             name = resolve_name(lc_name, buf, LtoR);
663             if (!name) {
664                 if (!nlc_name)
665                     nlc_name = normalize_lcname(lc_name);
666                 if (nlc_name)
667                     name = resolve_name (nlc_name, buf, LtoR);
668             }
669         }
670
671         /* If name is not an alias, use lc_name for locale.dir search */
672         if (name == NULL)
673             name = lc_name;
674
675         /* look at locale.dir */
676
677         target_dir = args[i];
678         if (!target_dir) {
679             /* something wrong */
680             if (name != lc_name)
681                 Xfree(name);
682             continue;
683         }
684         if ((1 + strlen (target_dir) + strlen("locale.dir")) < PATH_MAX) {
685             sprintf(buf, "%s/locale.dir", target_dir);
686             target_name = resolve_name(name, buf, RtoL);
687         }
688         if (name != lc_name)
689             Xfree(name);
690         if (target_name != NULL) {
691             char *p = 0;
692             if ((p = strstr(target_name, "/XLC_LOCALE"))) {
693                 *p = '\0';
694                 break;
695             }
696             Xfree(target_name);
697             target_name = NULL;
698         }
699         name = NULL;
700     }
701     if (nlc_name) Xfree(nlc_name);
702
703     if (target_name == NULL) {
704         /* vendor locale name == Xlocale name, no expansion of alias */
705         target_dir = args[0];
706         target_name = lc_name;
707     }
708     /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */
709     strncpy(dir_name, target_dir, dir_len - 1);
710     if (strlen(target_dir) >= dir_len - 1) {
711         dir_name[dir_len - 1] = '\0';
712     } else  {
713         strcat(dir_name, "/");
714         strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1);
715         if (strlen(target_name) >= dir_len - strlen(dir_name) - 1)
716             dir_name[dir_len - 1] = '\0';
717     }
718     if (target_name != lc_name)
719         Xfree(target_name);
720
721     if (last_dir_name != 0)
722         Xfree (last_dir_name);
723     if (last_lc_name != 0)
724         Xfree (last_lc_name);
725     last_dir_len = strlen (dir_name) + 1;
726     last_dir_name = Xmalloc (last_dir_len);
727     strcpy (last_dir_name, dir_name);
728     last_lc_name = strdup (lc_name);
729
730     return dir_name;
731 }
732
733 char *
734 _XlcLocaleLibDirName(char *dir_name, size_t dir_len, char *lc_name)
735 {
736     char dir[PATH_MAX], buf[PATH_MAX], *name = NULL;
737     int i, n;
738     char *args[NUM_LOCALEDIR];
739     static char locale_alias[] = LOCALE_ALIAS;
740     char *target_name = (char*)0;
741     char *target_dir = (char*)0;
742     char *nlc_name = NULL;
743     static char*  last_dir_name = 0;
744     static size_t last_dir_len = 0;
745     static char*  last_lc_name = 0;
746
747     if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0
748        && dir_len >= last_dir_len) {
749         strcpy (dir_name, last_dir_name);
750         return dir_name;
751     }
752
753     xlocalelibdir (dir, PATH_MAX);
754     n = _XlcParsePath(dir, args, 256);
755     for (i = 0; i < n; ++i) {
756
757         if ((2 + (args[i] ? strlen(args[i]) : 0) +
758              strlen(locale_alias)) < PATH_MAX) {
759             sprintf (buf, "%s/%s", args[i], locale_alias);
760             name = resolve_name(lc_name, buf, LtoR);
761             if (!name) {
762                 if (!nlc_name)
763                     nlc_name = normalize_lcname(lc_name);
764                 if (nlc_name)
765                     name = resolve_name (nlc_name, buf, LtoR);
766             }
767         }
768
769         /* If name is not an alias, use lc_name for locale.dir search */
770         if (name == NULL)
771             name = lc_name;
772
773         /* look at locale.dir */
774
775         target_dir = args[i];
776         if (!target_dir) {
777             /* something wrong */
778             if (name != lc_name)
779                 Xfree(name);
780             continue;
781         }
782         if ((1 + strlen (target_dir) + strlen("locale.dir")) < PATH_MAX) {
783             sprintf(buf, "%s/locale.dir", target_dir);
784             target_name = resolve_name(name, buf, RtoL);
785         }
786         if (name != lc_name)
787             Xfree(name);
788         if (target_name != NULL) {
789             char *p = 0;
790             if ((p = strstr(target_name, "/XLC_LOCALE"))) {
791                 *p = '\0';
792                 break;
793             }
794             Xfree(target_name);
795             target_name = NULL;
796         }
797         name = NULL;
798     }
799     if (nlc_name) Xfree(nlc_name);
800
801     if (target_name == NULL) {
802         /* vendor locale name == Xlocale name, no expansion of alias */
803         target_dir = args[0];
804         target_name = lc_name;
805     }
806     /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */
807     strncpy(dir_name, target_dir, dir_len - 1);
808     if (strlen(target_dir) >= dir_len - 1) {
809         dir_name[dir_len - 1] = '\0';
810     } else  {
811         strcat(dir_name, "/");
812         strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1);
813         if (strlen(target_name) >= dir_len - strlen(dir_name) - 1)
814             dir_name[dir_len - 1] = '\0';
815     }
816     if (target_name != lc_name)
817         Xfree(target_name);
818
819     if (last_dir_name != 0)
820         Xfree (last_dir_name);
821     if (last_lc_name != 0)
822         Xfree (last_lc_name);
823     last_dir_len = strlen (dir_name) + 1;
824     last_dir_name = Xmalloc (last_dir_len);
825     strcpy (last_dir_name, dir_name);
826     last_lc_name = strdup (lc_name);
827
828     return dir_name;
829 }