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