lcFile.c revision 1ab64890
11ab64890Smrg/* $Xorg: lcFile.c,v 1.5 2000/12/12 12:44:05 coskrey Exp $ */
21ab64890Smrg/*
31ab64890Smrg *
41ab64890Smrg * Copyright IBM Corporation 1993
51ab64890Smrg *
61ab64890Smrg * All Rights Reserved
71ab64890Smrg *
81ab64890Smrg * License to use, copy, modify, and distribute this software and its
91ab64890Smrg * documentation for any purpose and without fee is hereby granted,
101ab64890Smrg * provided that the above copyright notice appear in all copies and that
111ab64890Smrg * both that copyright notice and this permission notice appear in
121ab64890Smrg * supporting documentation, and that the name of IBM not be
131ab64890Smrg * used in advertising or publicity pertaining to distribution of the
141ab64890Smrg * software without specific, written prior permission.
151ab64890Smrg *
161ab64890Smrg * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
171ab64890Smrg * ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS, AND
181ab64890Smrg * NONINFRINGEMENT OF THIRD PARTY RIGHTS, IN NO EVENT SHALL
191ab64890Smrg * IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
201ab64890Smrg * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
211ab64890Smrg * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
221ab64890Smrg * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
231ab64890Smrg * SOFTWARE.
241ab64890Smrg *
251ab64890Smrg*/
261ab64890Smrg/* $XFree86: xc/lib/X11/lcFile.c,v 3.32 2003/03/25 04:18:09 dawes Exp $ */
271ab64890Smrg
281ab64890Smrg#ifdef HAVE_CONFIG_H
291ab64890Smrg#include <config.h>
301ab64890Smrg#endif
311ab64890Smrg#include <stdlib.h>
321ab64890Smrg#include <stdio.h>
331ab64890Smrg#include <ctype.h>
341ab64890Smrg#include "Xlibint.h"
351ab64890Smrg#include "XlcPubI.h"
361ab64890Smrg#include <X11/Xos.h>
371ab64890Smrg#include <unistd.h>
381ab64890Smrg
391ab64890Smrg/************************************************************************/
401ab64890Smrg
411ab64890Smrg#ifdef __UNIXOS2__
421ab64890Smrg# define seteuid setuid
431ab64890Smrg#endif
441ab64890Smrg#define	iscomment(ch)	((ch) == '#' || (ch) == '\0')
451ab64890Smrg#if defined(WIN32)
461ab64890Smrg#define isreadable(f)	(_XAccessFile(f))
471ab64890Smrg#else
481ab64890Smrg#define isreadable(f)	((access((f), R_OK) != -1) ? 1 : 0)
491ab64890Smrg#endif
501ab64890Smrg
511ab64890Smrg#ifndef __UNIXOS2__
521ab64890Smrg#define LC_PATHDELIM ':'
531ab64890Smrg#else
541ab64890Smrg#define LC_PATHDELIM ';'
551ab64890Smrg#endif
561ab64890Smrg
571ab64890Smrg#define XLC_BUFSIZE 256
581ab64890Smrg
591ab64890Smrg#ifndef X_NOT_POSIX
601ab64890Smrg#ifdef _POSIX_SOURCE
611ab64890Smrg#include <limits.h>
621ab64890Smrg#else
631ab64890Smrg#define _POSIX_SOURCE
641ab64890Smrg#include <limits.h>
651ab64890Smrg#undef _POSIX_SOURCE
661ab64890Smrg#endif
671ab64890Smrg#endif
681ab64890Smrg#ifndef PATH_MAX
691ab64890Smrg#ifdef WIN32
701ab64890Smrg#define PATH_MAX 512
711ab64890Smrg#else
721ab64890Smrg#include <sys/param.h>
731ab64890Smrg#endif
741ab64890Smrg#ifndef PATH_MAX
751ab64890Smrg#ifdef MAXPATHLEN
761ab64890Smrg#define PATH_MAX MAXPATHLEN
771ab64890Smrg#else
781ab64890Smrg#define PATH_MAX 1024
791ab64890Smrg#endif
801ab64890Smrg#endif
811ab64890Smrg#endif
821ab64890Smrg
831ab64890Smrg#define NUM_LOCALEDIR	64
841ab64890Smrg
851ab64890Smrg/* Splits a NUL terminated line into constituents, at colons and newline
861ab64890Smrg   characters. Leading whitespace is removed from constituents. The
871ab64890Smrg   constituents are stored at argv[0..argsize-1]. The number of stored
881ab64890Smrg   constituents (<= argsize) is returned. The line is destructively
891ab64890Smrg   modified. */
901ab64890Smrgstatic int
911ab64890Smrgparse_line(
921ab64890Smrg    char *line,
931ab64890Smrg    char **argv,
941ab64890Smrg    int argsize)
951ab64890Smrg{
961ab64890Smrg    int argc = 0;
971ab64890Smrg    char *p = line;
981ab64890Smrg
991ab64890Smrg    while (argc < argsize) {
1001ab64890Smrg	while (isspace(*p)) {
1011ab64890Smrg	    ++p;
1021ab64890Smrg	}
1031ab64890Smrg	if (*p == '\0') {
1041ab64890Smrg	    break;
1051ab64890Smrg	}
1061ab64890Smrg	argv[argc++] = p;
1071ab64890Smrg	while (*p != ':' && *p != '\n' && *p != '\0') {
1081ab64890Smrg	    ++p;
1091ab64890Smrg	}
1101ab64890Smrg	if (*p == '\0') {
1111ab64890Smrg	    break;
1121ab64890Smrg	}
1131ab64890Smrg	*p++ = '\0';
1141ab64890Smrg    }
1151ab64890Smrg
1161ab64890Smrg    return argc;
1171ab64890Smrg}
1181ab64890Smrg
1191ab64890Smrg#ifdef __UNIXOS2__
1201ab64890Smrg
1211ab64890Smrg/* fg021216: entries in locale files are separated by colons while under
1221ab64890Smrg   OS/2, path entries are separated by semicolon, so we need two functions */
1231ab64890Smrg
1241ab64890Smrgstatic int
1251ab64890Smrgparse_line1(
1261ab64890Smrg    char *line,
1271ab64890Smrg    char **argv,
1281ab64890Smrg    int argsize)
1291ab64890Smrg{
1301ab64890Smrg    int argc = 0;
1311ab64890Smrg    char *p = line;
1321ab64890Smrg
1331ab64890Smrg    while (argc < argsize) {
1341ab64890Smrg	while (isspace(*p)) {
1351ab64890Smrg	    ++p;
1361ab64890Smrg	}
1371ab64890Smrg	if (*p == '\0') {
1381ab64890Smrg	    break;
1391ab64890Smrg	}
1401ab64890Smrg	argv[argc++] = p;
1411ab64890Smrg	while (*p != ';' && *p != '\n' && *p != '\0') {
1421ab64890Smrg	    ++p;
1431ab64890Smrg	}
1441ab64890Smrg	if (*p == '\0') {
1451ab64890Smrg	    break;
1461ab64890Smrg	}
1471ab64890Smrg	*p++ = '\0';
1481ab64890Smrg    }
1491ab64890Smrg
1501ab64890Smrg    return argc;
1511ab64890Smrg}
1521ab64890Smrg#elif defined(WIN32)
1531ab64890Smrg
1541ab64890Smrg/* this is parse_line but skips drive letters at the beginning of the entry */
1551ab64890Smrgstatic int
1561ab64890Smrgparse_line1(
1571ab64890Smrg    char *line,
1581ab64890Smrg    char **argv,
1591ab64890Smrg    int argsize)
1601ab64890Smrg{
1611ab64890Smrg    int argc = 0;
1621ab64890Smrg    char *p = line;
1631ab64890Smrg
1641ab64890Smrg    while (argc < argsize) {
1651ab64890Smrg	while (isspace(*p)) {
1661ab64890Smrg	    ++p;
1671ab64890Smrg	}
1681ab64890Smrg	if (*p == '\0') {
1691ab64890Smrg	    break;
1701ab64890Smrg	}
1711ab64890Smrg	argv[argc++] = p;
1721ab64890Smrg        if (isalpha(*p) && p[1] == ':') {
1731ab64890Smrg            p+= 2; /* skip drive letters */
1741ab64890Smrg        }
1751ab64890Smrg	while (*p != ':' && *p != '\n' && *p != '\0') {
1761ab64890Smrg	    ++p;
1771ab64890Smrg	}
1781ab64890Smrg	if (*p == '\0') {
1791ab64890Smrg	    break;
1801ab64890Smrg	}
1811ab64890Smrg	*p++ = '\0';
1821ab64890Smrg    }
1831ab64890Smrg
1841ab64890Smrg    return argc;
1851ab64890Smrg}
1861ab64890Smrg
1871ab64890Smrg#endif   /* __UNIXOS2__ */
1881ab64890Smrg
1891ab64890Smrg/* Splits a colon separated list of directories, and returns the constituent
1901ab64890Smrg   paths (without trailing slash). At most argsize constituents are stored
1911ab64890Smrg   at argv[0..argsize-1]. The number of stored constituents is returned. */
1921ab64890Smrgstatic int
1931ab64890Smrg_XlcParsePath(
1941ab64890Smrg    char *path,
1951ab64890Smrg    char **argv,
1961ab64890Smrg    int argsize)
1971ab64890Smrg{
1981ab64890Smrg    char *p = path;
1991ab64890Smrg    int n, i;
2001ab64890Smrg
2011ab64890Smrg#if !defined(__UNIXOS2__) && !defined(WIN32)
2021ab64890Smrg    n = parse_line(path, argv, argsize);
2031ab64890Smrg#else
2041ab64890Smrg    n = parse_line1(path, argv, argsize);
2051ab64890Smrg#endif
2061ab64890Smrg    for (i = 0; i < n; ++i) {
2071ab64890Smrg	int len;
2081ab64890Smrg	p = argv[i];
2091ab64890Smrg	len = strlen(p);
2101ab64890Smrg	if (len > 0 && p[len - 1] == '/') {
2111ab64890Smrg	    /* eliminate trailing slash */
2121ab64890Smrg	    p[len - 1] = '\0';
2131ab64890Smrg	}
2141ab64890Smrg    }
2151ab64890Smrg    return n;
2161ab64890Smrg}
2171ab64890Smrg
2181ab64890Smrg#ifndef XLOCALEDIR
2191ab64890Smrg#define XLOCALEDIR "/usr/lib/X11/locale"
2201ab64890Smrg#endif
2211ab64890Smrg
2221ab64890Smrgstatic void
2231ab64890Smrgxlocaledir(
2241ab64890Smrg    char *buf,
2251ab64890Smrg    int buf_len)
2261ab64890Smrg{
2271ab64890Smrg    char *p = buf;
2281ab64890Smrg    int len = 0;
2291ab64890Smrg
2301ab64890Smrg#ifndef NO_XLOCALEDIR
2311ab64890Smrg    char *dir;
2321ab64890Smrg    int priv = 1;
2331ab64890Smrg
2341ab64890Smrg    dir = getenv("XLOCALEDIR");
2351ab64890Smrg
2361ab64890Smrg    if (dir) {
2371ab64890Smrg#ifndef WIN32
2381ab64890Smrg	/*
2391ab64890Smrg	 * Only use the user-supplied path if the process isn't priviledged.
2401ab64890Smrg	 */
2411ab64890Smrg	if (getuid() == geteuid() && getgid() == getegid()) {
2421ab64890Smrg#if defined(HASSETUGID)
2431ab64890Smrg	    priv = issetugid();
2441ab64890Smrg#elif defined(HASGETRESUID)
2451ab64890Smrg	    {
2461ab64890Smrg		uid_t ruid, euid, suid;
2471ab64890Smrg		gid_t rgid, egid, sgid;
2481ab64890Smrg		if ((getresuid(&ruid, &euid, &suid) == 0) &&
2491ab64890Smrg		    (getresgid(&rgid, &egid, &sgid) == 0))
2501ab64890Smrg		    priv = (euid != suid) || (egid != sgid);
2511ab64890Smrg	    }
2521ab64890Smrg#else
2531ab64890Smrg	    /*
2541ab64890Smrg	     * If there are saved ID's the process might still be priviledged
2551ab64890Smrg	     * even though the above test succeeded.  If issetugid() and
2561ab64890Smrg	     * getresgid() aren't available, test this by trying to set
2571ab64890Smrg	     * euid to 0.
2581ab64890Smrg	     *
2591ab64890Smrg	     * Note: this only protects setuid-root clients.  It doesn't
2601ab64890Smrg	     * protect other setuid or any setgid clients.  If this tradeoff
2611ab64890Smrg	     * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def.
2621ab64890Smrg	     */
2631ab64890Smrg	    unsigned int oldeuid;
2641ab64890Smrg	    oldeuid = geteuid();
2651ab64890Smrg	    if (seteuid(0) != 0) {
2661ab64890Smrg		priv = 0;
2671ab64890Smrg	    } else {
2681ab64890Smrg		if (seteuid(oldeuid) == -1) {
2691ab64890Smrg		    /* XXX ouch, coudn't get back to original uid
2701ab64890Smrg		     what can we do ??? */
2711ab64890Smrg		    _exit(127);
2721ab64890Smrg		}
2731ab64890Smrg		priv = 1;
2741ab64890Smrg	    }
2751ab64890Smrg#endif
2761ab64890Smrg	}
2771ab64890Smrg#else
2781ab64890Smrg	priv = 0;
2791ab64890Smrg#endif
2801ab64890Smrg	if (!priv) {
2811ab64890Smrg	    len = strlen(dir);
2821ab64890Smrg	    strncpy(p, dir, buf_len);
2831ab64890Smrg	    if (len < buf_len) {
2841ab64890Smrg	        p[len++] = LC_PATHDELIM;
2851ab64890Smrg	        p += len;
2861ab64890Smrg	    }
2871ab64890Smrg	}
2881ab64890Smrg    }
2891ab64890Smrg#endif /* NO_XLOCALEDIR */
2901ab64890Smrg
2911ab64890Smrg    if (len < buf_len)
2921ab64890Smrg#ifndef __UNIXOS2__
2931ab64890Smrg      strncpy(p, XLOCALEDIR, buf_len - len);
2941ab64890Smrg#else
2951ab64890Smrg      strncpy(p,__XOS2RedirRoot(XLOCALEDIR), buf_len - len);
2961ab64890Smrg#endif
2971ab64890Smrg    buf[buf_len-1] = '\0';
2981ab64890Smrg}
2991ab64890Smrg
3001ab64890Smrgstatic void
3011ab64890Smrgxlocalelibdir(
3021ab64890Smrg    char *buf,
3031ab64890Smrg    int buf_len)
3041ab64890Smrg{
3051ab64890Smrg    char *p = buf;
3061ab64890Smrg    int len = 0;
3071ab64890Smrg
3081ab64890Smrg#ifndef NO_XLOCALEDIR
3091ab64890Smrg    char *dir;
3101ab64890Smrg    int priv = 1;
3111ab64890Smrg
3121ab64890Smrg    dir = getenv("XLOCALELIBDIR");
3131ab64890Smrg
3141ab64890Smrg    if (dir) {
3151ab64890Smrg#ifndef WIN32
3161ab64890Smrg	/*
3171ab64890Smrg	 * Only use the user-supplied path if the process isn't priviledged.
3181ab64890Smrg	 */
3191ab64890Smrg	if (getuid() == geteuid() && getgid() == getegid()) {
3201ab64890Smrg#if defined(HASSETUGID)
3211ab64890Smrg	    priv = issetugid();
3221ab64890Smrg#elif defined(HASGETRESUID)
3231ab64890Smrg	    {
3241ab64890Smrg		uid_t ruid, euid, suid;
3251ab64890Smrg		gid_t rgid, egid, sgid;
3261ab64890Smrg		if ((getresuid(&ruid, &euid, &suid) == 0) &&
3271ab64890Smrg		    (getresgid(&rgid, &egid, &sgid) == 0))
3281ab64890Smrg		    priv = (euid != suid) || (egid != sgid);
3291ab64890Smrg	    }
3301ab64890Smrg#else
3311ab64890Smrg	    /*
3321ab64890Smrg	     * If there are saved ID's the process might still be priviledged
3331ab64890Smrg	     * even though the above test succeeded.  If issetugid() and
3341ab64890Smrg	     * getresgid() aren't available, test this by trying to set
3351ab64890Smrg	     * euid to 0.
3361ab64890Smrg	     *
3371ab64890Smrg	     * Note: this only protects setuid-root clients.  It doesn't
3381ab64890Smrg	     * protect other setuid or any setgid clients.  If this tradeoff
3391ab64890Smrg	     * isn't acceptable, set DisableXLocaleDirEnv to YES in host.def.
3401ab64890Smrg	     */
3411ab64890Smrg	    unsigned int oldeuid;
3421ab64890Smrg	    oldeuid = geteuid();
3431ab64890Smrg	    if (seteuid(0) != 0) {
3441ab64890Smrg		priv = 0;
3451ab64890Smrg	    } else {
3461ab64890Smrg		if (seteuid(oldeuid) == -1) {
3471ab64890Smrg		    /* XXX ouch, coudn't get back to original uid
3481ab64890Smrg		     what can we do ??? */
3491ab64890Smrg		    _exit(127);
3501ab64890Smrg		}
3511ab64890Smrg		priv = 1;
3521ab64890Smrg	    }
3531ab64890Smrg#endif
3541ab64890Smrg	}
3551ab64890Smrg#else
3561ab64890Smrg	priv = 0;
3571ab64890Smrg#endif
3581ab64890Smrg	if (!priv) {
3591ab64890Smrg	    len = strlen(dir);
3601ab64890Smrg	    strncpy(p, dir, buf_len);
3611ab64890Smrg	    if (len < buf_len) {
3621ab64890Smrg	        p[len++] = LC_PATHDELIM;
3631ab64890Smrg	        p += len;
3641ab64890Smrg	    }
3651ab64890Smrg	}
3661ab64890Smrg    }
3671ab64890Smrg#endif /* NO_XLOCALEDIR */
3681ab64890Smrg
3691ab64890Smrg    if (len < buf_len)
3701ab64890Smrg#ifndef __UNIXOS2__
3711ab64890Smrg      strncpy(p, XLOCALELIBDIR, buf_len - len);
3721ab64890Smrg#else
3731ab64890Smrg      strncpy(p,__XOS2RedirRoot(XLOCALELIBDIR), buf_len - len);
3741ab64890Smrg#endif
3751ab64890Smrg    buf[buf_len-1] = '\0';
3761ab64890Smrg}
3771ab64890Smrg
3781ab64890Smrg/* Mapping direction */
3791ab64890Smrgtypedef enum {
3801ab64890Smrg  LtoR,		/* Map first field to second field */
3811ab64890Smrg  RtoL		/* Map second field to first field */
3821ab64890Smrg} MapDirection;
3831ab64890Smrg
3841ab64890Smrgstatic char *
3851ab64890Smrgresolve_name(
3861ab64890Smrg    const char *lc_name,
3871ab64890Smrg    char *file_name,
3881ab64890Smrg    MapDirection direction)
3891ab64890Smrg{
3901ab64890Smrg    FILE *fp;
3911ab64890Smrg    char buf[XLC_BUFSIZE], *name = NULL;
3921ab64890Smrg
3931ab64890Smrg    fp = _XFopenFile (file_name, "r");
3941ab64890Smrg    if (fp == NULL)
3951ab64890Smrg	return NULL;
3961ab64890Smrg
3971ab64890Smrg    while (fgets(buf, XLC_BUFSIZE, fp) != NULL) {
3981ab64890Smrg	char *p = buf;
3991ab64890Smrg	int n;
4001ab64890Smrg	char *args[2], *from, *to;
4011ab64890Smrg#ifdef __UNIXOS2__  /* Take out CR under OS/2 */
4021ab64890Smrg	int len;
4031ab64890Smrg
4041ab64890Smrg	len = strlen(p);
4051ab64890Smrg	if (len > 1) {
4061ab64890Smrg	    if (*(p+len-2) == '\r' && *(p+len-1) == '\n') {
4071ab64890Smrg		*(p+len-2) = '\n';
4081ab64890Smrg		*(p+len-1) = '\0';
4091ab64890Smrg	    }
4101ab64890Smrg	}
4111ab64890Smrg#endif
4121ab64890Smrg	while (isspace(*p)) {
4131ab64890Smrg	    ++p;
4141ab64890Smrg	}
4151ab64890Smrg	if (iscomment(*p)) {
4161ab64890Smrg	    continue;
4171ab64890Smrg	}
4181ab64890Smrg	n = parse_line(p, args, 2);		/* get first 2 fields */
4191ab64890Smrg	if (n != 2) {
4201ab64890Smrg	    continue;
4211ab64890Smrg	}
4221ab64890Smrg	if (direction == LtoR) {
4231ab64890Smrg	    from = args[0], to = args[1];	/* left to right */
4241ab64890Smrg	} else {
4251ab64890Smrg	    from = args[1], to = args[0];	/* right to left */
4261ab64890Smrg	}
4271ab64890Smrg	if (! strcmp(from, lc_name)) {
4281ab64890Smrg	    name = Xmalloc(strlen(to) + 1);
4291ab64890Smrg	    if (name != NULL) {
4301ab64890Smrg		strcpy(name, to);
4311ab64890Smrg	    }
4321ab64890Smrg	    break;
4331ab64890Smrg	}
4341ab64890Smrg    }
4351ab64890Smrg    fclose(fp);
4361ab64890Smrg    return name;
4371ab64890Smrg}
4381ab64890Smrg
4391ab64890Smrg#define	c_tolower(ch)	((ch) >= 'A' && (ch) <= 'Z' ? (ch) - 'A' + 'a' : (ch))
4401ab64890Smrg
4411ab64890Smrgstatic char *
4421ab64890Smrglowercase(
4431ab64890Smrg    char *dst,
4441ab64890Smrg    const char *src)
4451ab64890Smrg{
4461ab64890Smrg    const char *s;
4471ab64890Smrg    char *t;
4481ab64890Smrg
4491ab64890Smrg    for (s = src, t = dst; *s; ++s, ++t)
4501ab64890Smrg	*t = c_tolower(*s);
4511ab64890Smrg    *t = '\0';
4521ab64890Smrg    return dst;
4531ab64890Smrg}
4541ab64890Smrg
4551ab64890Smrg/*
4561ab64890Smrg * normalize_lcname(): remove any '_' and '-' and convert any character
4571ab64890Smrg * to lower case after the <language>_<territory> part. If result is identical
4581ab64890Smrg * to argument, free result and
4591ab64890Smrg * return NULL.
4601ab64890Smrg */
4611ab64890Smrgstatic char *
4621ab64890Smrgnormalize_lcname (const char *name)
4631ab64890Smrg{
4641ab64890Smrg    char *p, *ret;
4651ab64890Smrg    const char *tmp = name;
4661ab64890Smrg
4671ab64890Smrg    p = ret = Xmalloc(strlen(name) + 1);
4681ab64890Smrg    if (!p)
4691ab64890Smrg	return NULL;
4701ab64890Smrg
4711ab64890Smrg    if (tmp) {
4721ab64890Smrg	while (*tmp && *tmp != '.' && *tmp != '@')
4731ab64890Smrg	    *p++ = *tmp++;
4741ab64890Smrg	while (*tmp) {
4751ab64890Smrg	    if (*tmp != '-')
4761ab64890Smrg		*p++ = c_tolower(*tmp);
4771ab64890Smrg	    tmp++;
4781ab64890Smrg	}
4791ab64890Smrg    }
4801ab64890Smrg    *p = '\0';
4811ab64890Smrg
4821ab64890Smrg    if (strcmp(ret, name) == 0) {
4831ab64890Smrg	Xfree(ret);
4841ab64890Smrg	return NULL;
4851ab64890Smrg    }
4861ab64890Smrg
4871ab64890Smrg    return ret;
4881ab64890Smrg}
4891ab64890Smrg
4901ab64890Smrg/************************************************************************/
4911ab64890Smrgchar *
4921ab64890Smrg_XlcFileName(
4931ab64890Smrg    XLCd lcd,
4941ab64890Smrg    const char *category)
4951ab64890Smrg{
4961ab64890Smrg    char *siname;
4971ab64890Smrg    char cat[XLC_BUFSIZE], dir[XLC_BUFSIZE];
4981ab64890Smrg    int i, n;
4991ab64890Smrg    char *args[NUM_LOCALEDIR];
5001ab64890Smrg    char *file_name = NULL;
5011ab64890Smrg
5021ab64890Smrg    if (lcd == (XLCd)NULL)
5031ab64890Smrg	return NULL;
5041ab64890Smrg
5051ab64890Smrg    siname = XLC_PUBLIC(lcd, siname);
5061ab64890Smrg
5071ab64890Smrg    lowercase(cat, category);
5081ab64890Smrg    xlocaledir(dir,XLC_BUFSIZE);
5091ab64890Smrg    n = _XlcParsePath(dir, args, NUM_LOCALEDIR);
5101ab64890Smrg    for (i = 0; i < n; ++i) {
5111ab64890Smrg	char buf[PATH_MAX], *name;
5121ab64890Smrg
5131ab64890Smrg	name = NULL;
5141ab64890Smrg	if ((5 + (args[i] ? strlen (args[i]) : 0) +
5151ab64890Smrg	    (cat ? strlen (cat) : 0)) < PATH_MAX) {
5161ab64890Smrg	    sprintf(buf, "%s/%s.dir", args[i], cat);
5171ab64890Smrg	    name = resolve_name(siname, buf, RtoL);
5181ab64890Smrg	}
5191ab64890Smrg	if (name == NULL) {
5201ab64890Smrg	    continue;
5211ab64890Smrg	}
5221ab64890Smrg	if (*name == '/') {
5231ab64890Smrg	    /* supposed to be absolute path name */
5241ab64890Smrg	    file_name = name;
5251ab64890Smrg	} else {
5261ab64890Smrg	    file_name = Xmalloc(2 + (args[i] ? strlen (args[i]) : 0) +
5271ab64890Smrg				(name ? strlen (name) : 0));
5281ab64890Smrg	    if (file_name != NULL)
5291ab64890Smrg		sprintf(file_name, "%s/%s", args[i], name);
5301ab64890Smrg	    Xfree(name);
5311ab64890Smrg	}
5321ab64890Smrg	if (isreadable(file_name)) {
5331ab64890Smrg	    break;
5341ab64890Smrg	}
5351ab64890Smrg	Xfree(file_name);
5361ab64890Smrg	file_name = NULL;
5371ab64890Smrg	/* Then, try with next dir */
5381ab64890Smrg    }
5391ab64890Smrg    return file_name;
5401ab64890Smrg}
5411ab64890Smrg
5421ab64890Smrg/************************************************************************/
5431ab64890Smrg#ifndef LOCALE_ALIAS
5441ab64890Smrg#define LOCALE_ALIAS    "locale.alias"
5451ab64890Smrg#endif
5461ab64890Smrg
5471ab64890Smrgint
5481ab64890Smrg_XlcResolveLocaleName(
5491ab64890Smrg    const char* lc_name,
5501ab64890Smrg    XLCdPublicPart* pub)
5511ab64890Smrg{
5521ab64890Smrg    char dir[PATH_MAX], buf[PATH_MAX], *name = NULL;
5531ab64890Smrg    char *dst;
5541ab64890Smrg    int i, n, sinamelen;
5551ab64890Smrg    char *args[NUM_LOCALEDIR];
5561ab64890Smrg    static const char locale_alias[] = LOCALE_ALIAS;
5571ab64890Smrg    char *tmp_siname;
5581ab64890Smrg    char *nlc_name = NULL;
5591ab64890Smrg
5601ab64890Smrg    xlocaledir (dir, PATH_MAX);
5611ab64890Smrg    n = _XlcParsePath(dir, args, NUM_LOCALEDIR);
5621ab64890Smrg    for (i = 0; i < n; ++i) {
5631ab64890Smrg	if ((2 + (args[i] ? strlen (args[i]) : 0) +
5641ab64890Smrg	    strlen (locale_alias)) < PATH_MAX) {
5651ab64890Smrg	    sprintf (buf, "%s/%s", args[i], locale_alias);
5661ab64890Smrg	    name = resolve_name (lc_name, buf, LtoR);
5671ab64890Smrg	    if (!name) {
5681ab64890Smrg		if (!nlc_name)
5691ab64890Smrg		    nlc_name = normalize_lcname(lc_name);
5701ab64890Smrg		if (nlc_name)
5711ab64890Smrg		    name = resolve_name (nlc_name, buf, LtoR);
5721ab64890Smrg	    }
5731ab64890Smrg	}
5741ab64890Smrg	if (name != NULL) {
5751ab64890Smrg	    break;
5761ab64890Smrg	}
5771ab64890Smrg    }
5781ab64890Smrg    if (nlc_name) Xfree(nlc_name);
5791ab64890Smrg
5801ab64890Smrg    if (name == NULL) {
5811ab64890Smrg	/* vendor locale name == Xlocale name, no expansion of alias */
5821ab64890Smrg	pub->siname = Xmalloc (strlen (lc_name) + 1);
5831ab64890Smrg	strcpy (pub->siname, lc_name);
5841ab64890Smrg    } else {
5851ab64890Smrg	pub->siname = name;
5861ab64890Smrg    }
5871ab64890Smrg
5881ab64890Smrg    sinamelen = strlen (pub->siname);
5891ab64890Smrg    if (sinamelen == 1 && pub->siname[0] == 'C') {
5901ab64890Smrg	pub->language = pub->siname;
5911ab64890Smrg	pub->territory = pub->codeset = NULL;
5921ab64890Smrg	return 1;
5931ab64890Smrg    }
5941ab64890Smrg
5951ab64890Smrg    /*
5961ab64890Smrg     * pub->siname is in the format <lang>_<terr>.<codeset>, typical would
5971ab64890Smrg     * be "en_US.ISO8859-1", "en_US.utf8", "ru_RU.KOI-8", or ja_JP.SJIS,
5981ab64890Smrg     * although it could be ja.SJIS too.
5991ab64890Smrg     */
6001ab64890Smrg    tmp_siname = Xrealloc (pub->siname, 2 * (sinamelen + 1));
6011ab64890Smrg    if (tmp_siname == NULL) {
6021ab64890Smrg	return 0;
6031ab64890Smrg    }
6041ab64890Smrg    pub->siname = tmp_siname;
6051ab64890Smrg
6061ab64890Smrg    /* language */
6071ab64890Smrg    dst = &pub->siname[sinamelen + 1];
6081ab64890Smrg    strcpy (dst, pub->siname);
6091ab64890Smrg    pub->language = dst;
6101ab64890Smrg
6111ab64890Smrg    /* territory */
6121ab64890Smrg    dst = strchr (dst, '_');
6131ab64890Smrg    if (dst) {
6141ab64890Smrg	*dst = '\0';
6151ab64890Smrg	pub->territory = ++dst;
6161ab64890Smrg    } else
6171ab64890Smrg	dst = &pub->siname[sinamelen + 1];
6181ab64890Smrg
6191ab64890Smrg    /* codeset */
6201ab64890Smrg    dst = strchr (dst, '.');
6211ab64890Smrg    if (dst) {
6221ab64890Smrg	*dst = '\0';
6231ab64890Smrg	pub->codeset = ++dst;
6241ab64890Smrg    }
6251ab64890Smrg
6261ab64890Smrg    return (pub->siname[0] != '\0') ? 1 : 0;
6271ab64890Smrg}
6281ab64890Smrg
6291ab64890Smrg/************************************************************************/
6301ab64890Smrgint
6311ab64890Smrg_XlcResolveI18NPath(buf, buf_len)
6321ab64890Smrg    char *buf;
6331ab64890Smrg    int buf_len;
6341ab64890Smrg{
6351ab64890Smrg    if (buf != NULL) {
6361ab64890Smrg	xlocaledir(buf, buf_len);
6371ab64890Smrg    }
6381ab64890Smrg    return 1;
6391ab64890Smrg}
6401ab64890Smrg
6411ab64890Smrgchar *
6421ab64890Smrg_XlcLocaleDirName(dir_name, dir_len, lc_name)
6431ab64890Smrg     char *dir_name;
6441ab64890Smrg     size_t dir_len;
6451ab64890Smrg     char *lc_name;
6461ab64890Smrg{
6471ab64890Smrg    char dir[PATH_MAX], buf[PATH_MAX], *name = NULL;
6481ab64890Smrg    int i, n;
6491ab64890Smrg    char *args[NUM_LOCALEDIR];
6501ab64890Smrg    static char locale_alias[] = LOCALE_ALIAS;
6511ab64890Smrg    char *target_name = (char*)0;
6521ab64890Smrg    char *target_dir = (char*)0;
6531ab64890Smrg    char *nlc_name = NULL;
6541ab64890Smrg    static char*  last_dir_name = 0;
6551ab64890Smrg    static size_t last_dir_len = 0;
6561ab64890Smrg    static char*  last_lc_name = 0;
6571ab64890Smrg
6581ab64890Smrg    if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0
6591ab64890Smrg       && dir_len >= last_dir_len) {
6601ab64890Smrg        strcpy (dir_name, last_dir_name);
6611ab64890Smrg        return dir_name;
6621ab64890Smrg    }
6631ab64890Smrg
6641ab64890Smrg    xlocaledir (dir, PATH_MAX);
6651ab64890Smrg    n = _XlcParsePath(dir, args, 256);
6661ab64890Smrg    for (i = 0; i < n; ++i) {
6671ab64890Smrg
6681ab64890Smrg	if ((2 + (args[i] ? strlen(args[i]) : 0) +
6691ab64890Smrg 	     strlen(locale_alias)) < PATH_MAX) {
6701ab64890Smrg 	    sprintf (buf, "%s/%s", args[i], locale_alias);
6711ab64890Smrg 	    name = resolve_name(lc_name, buf, LtoR);
6721ab64890Smrg	    if (!name) {
6731ab64890Smrg		if (!nlc_name)
6741ab64890Smrg		    nlc_name = normalize_lcname(lc_name);
6751ab64890Smrg		if (nlc_name)
6761ab64890Smrg		    name = resolve_name (nlc_name, buf, LtoR);
6771ab64890Smrg	    }
6781ab64890Smrg 	}
6791ab64890Smrg
6801ab64890Smrg 	/* If name is not an alias, use lc_name for locale.dir search */
6811ab64890Smrg 	if (name == NULL)
6821ab64890Smrg 	    name = lc_name;
6831ab64890Smrg
6841ab64890Smrg 	/* look at locale.dir */
6851ab64890Smrg
6861ab64890Smrg 	target_dir = args[i];
6871ab64890Smrg 	if (!target_dir) {
6881ab64890Smrg 	    /* something wrong */
6891ab64890Smrg 	    if (name != lc_name)
6901ab64890Smrg 		Xfree(name);
6911ab64890Smrg 	    continue;
6921ab64890Smrg 	}
6931ab64890Smrg 	if ((1 + (target_dir ? strlen (target_dir) : 0) +
6941ab64890Smrg 	     strlen("locale.dir")) < PATH_MAX) {
6951ab64890Smrg 	    sprintf(buf, "%s/locale.dir", target_dir);
6961ab64890Smrg 	    target_name = resolve_name(name, buf, RtoL);
6971ab64890Smrg 	}
6981ab64890Smrg 	if (name != lc_name)
6991ab64890Smrg 	    Xfree(name);
7001ab64890Smrg 	if (target_name != NULL) {
7011ab64890Smrg 	    char *p = 0;
7021ab64890Smrg 	    if ((p = strstr(target_name, "/XLC_LOCALE"))) {
7031ab64890Smrg 		*p = '\0';
7041ab64890Smrg 		break;
7051ab64890Smrg 	    }
7061ab64890Smrg 	    Xfree(target_name);
7071ab64890Smrg 	    target_name = NULL;
7081ab64890Smrg 	}
7091ab64890Smrg 	name = NULL;
7101ab64890Smrg    }
7111ab64890Smrg    if (nlc_name) Xfree(nlc_name);
7121ab64890Smrg
7131ab64890Smrg    if (target_name == NULL) {
7141ab64890Smrg 	/* vendor locale name == Xlocale name, no expansion of alias */
7151ab64890Smrg 	target_dir = args[0];
7161ab64890Smrg 	target_name = lc_name;
7171ab64890Smrg    }
7181ab64890Smrg    /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */
7191ab64890Smrg    strncpy(dir_name, target_dir, dir_len - 1);
7201ab64890Smrg    if (strlen(target_dir) >= dir_len - 1) {
7211ab64890Smrg	dir_name[dir_len - 1] = '\0';
7221ab64890Smrg    } else  {
7231ab64890Smrg	strcat(dir_name, "/");
7241ab64890Smrg	strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1);
7251ab64890Smrg	if (strlen(target_name) >= dir_len - strlen(dir_name) - 1)
7261ab64890Smrg	    dir_name[dir_len - 1] = '\0';
7271ab64890Smrg    }
7281ab64890Smrg    if (target_name != lc_name)
7291ab64890Smrg 	Xfree(target_name);
7301ab64890Smrg
7311ab64890Smrg    if (last_dir_name != 0)
7321ab64890Smrg	Xfree (last_dir_name);
7331ab64890Smrg    if (last_lc_name != 0)
7341ab64890Smrg	Xfree (last_lc_name);
7351ab64890Smrg    last_dir_len = strlen (dir_name) + 1;
7361ab64890Smrg    last_dir_name = Xmalloc (last_dir_len);
7371ab64890Smrg    strcpy (last_dir_name, dir_name);
7381ab64890Smrg    last_lc_name = Xmalloc (strlen (lc_name) + 1);
7391ab64890Smrg    strcpy (last_lc_name, lc_name);
7401ab64890Smrg
7411ab64890Smrg    return dir_name;
7421ab64890Smrg}
7431ab64890Smrg
7441ab64890Smrgchar *
7451ab64890Smrg_XlcLocaleLibDirName(dir_name, dir_len, lc_name)
7461ab64890Smrg     char *dir_name;
7471ab64890Smrg     size_t dir_len;
7481ab64890Smrg     char *lc_name;
7491ab64890Smrg{
7501ab64890Smrg    char dir[PATH_MAX], buf[PATH_MAX], *name = NULL;
7511ab64890Smrg    int i, n;
7521ab64890Smrg    char *args[NUM_LOCALEDIR];
7531ab64890Smrg    static char locale_alias[] = LOCALE_ALIAS;
7541ab64890Smrg    char *target_name = (char*)0;
7551ab64890Smrg    char *target_dir = (char*)0;
7561ab64890Smrg    char *nlc_name = NULL;
7571ab64890Smrg    static char*  last_dir_name = 0;
7581ab64890Smrg    static size_t last_dir_len = 0;
7591ab64890Smrg    static char*  last_lc_name = 0;
7601ab64890Smrg
7611ab64890Smrg    if (last_lc_name != 0 && strcmp (last_lc_name, lc_name) == 0
7621ab64890Smrg       && dir_len >= last_dir_len) {
7631ab64890Smrg	strcpy (dir_name, last_dir_name);
7641ab64890Smrg	return dir_name;
7651ab64890Smrg    }
7661ab64890Smrg
7671ab64890Smrg    xlocalelibdir (dir, PATH_MAX);
7681ab64890Smrg    n = _XlcParsePath(dir, args, 256);
7691ab64890Smrg    for (i = 0; i < n; ++i) {
7701ab64890Smrg
7711ab64890Smrg	if ((2 + (args[i] ? strlen(args[i]) : 0) +
7721ab64890Smrg 	     strlen(locale_alias)) < PATH_MAX) {
7731ab64890Smrg 	    sprintf (buf, "%s/%s", args[i], locale_alias);
7741ab64890Smrg 	    name = resolve_name(lc_name, buf, LtoR);
7751ab64890Smrg	    if (!name) {
7761ab64890Smrg		if (!nlc_name)
7771ab64890Smrg		    nlc_name = normalize_lcname(lc_name);
7781ab64890Smrg		if (nlc_name)
7791ab64890Smrg		    name = resolve_name (nlc_name, buf, LtoR);
7801ab64890Smrg	    }
7811ab64890Smrg 	}
7821ab64890Smrg
7831ab64890Smrg 	/* If name is not an alias, use lc_name for locale.dir search */
7841ab64890Smrg 	if (name == NULL)
7851ab64890Smrg 	    name = lc_name;
7861ab64890Smrg
7871ab64890Smrg 	/* look at locale.dir */
7881ab64890Smrg
7891ab64890Smrg 	target_dir = args[i];
7901ab64890Smrg 	if (!target_dir) {
7911ab64890Smrg 	    /* something wrong */
7921ab64890Smrg 	    if (name != lc_name)
7931ab64890Smrg 		Xfree(name);
7941ab64890Smrg 	    continue;
7951ab64890Smrg 	}
7961ab64890Smrg 	if ((1 + (target_dir ? strlen (target_dir) : 0) +
7971ab64890Smrg 	     strlen("locale.dir")) < PATH_MAX) {
7981ab64890Smrg 	    sprintf(buf, "%s/locale.dir", target_dir);
7991ab64890Smrg 	    target_name = resolve_name(name, buf, RtoL);
8001ab64890Smrg 	}
8011ab64890Smrg 	if (name != lc_name)
8021ab64890Smrg 	    Xfree(name);
8031ab64890Smrg 	if (target_name != NULL) {
8041ab64890Smrg 	    char *p = 0;
8051ab64890Smrg 	    if ((p = strstr(target_name, "/XLC_LOCALE"))) {
8061ab64890Smrg 		*p = '\0';
8071ab64890Smrg 		break;
8081ab64890Smrg 	    }
8091ab64890Smrg 	    Xfree(target_name);
8101ab64890Smrg 	    target_name = NULL;
8111ab64890Smrg 	}
8121ab64890Smrg 	name = NULL;
8131ab64890Smrg    }
8141ab64890Smrg    if (nlc_name) Xfree(nlc_name);
8151ab64890Smrg
8161ab64890Smrg    if (target_name == NULL) {
8171ab64890Smrg 	/* vendor locale name == Xlocale name, no expansion of alias */
8181ab64890Smrg 	target_dir = args[0];
8191ab64890Smrg 	target_name = lc_name;
8201ab64890Smrg    }
8211ab64890Smrg    /* snprintf(dir_name, dir_len, "%s/%", target_dir, target_name); */
8221ab64890Smrg    strncpy(dir_name, target_dir, dir_len - 1);
8231ab64890Smrg    if (strlen(target_dir) >= dir_len - 1) {
8241ab64890Smrg	dir_name[dir_len - 1] = '\0';
8251ab64890Smrg    } else  {
8261ab64890Smrg	strcat(dir_name, "/");
8271ab64890Smrg	strncat(dir_name, target_name, dir_len - strlen(dir_name) - 1);
8281ab64890Smrg	if (strlen(target_name) >= dir_len - strlen(dir_name) - 1)
8291ab64890Smrg	    dir_name[dir_len - 1] = '\0';
8301ab64890Smrg    }
8311ab64890Smrg    if (target_name != lc_name)
8321ab64890Smrg 	Xfree(target_name);
8331ab64890Smrg
8341ab64890Smrg    if (last_dir_name != 0)
8351ab64890Smrg	Xfree (last_dir_name);
8361ab64890Smrg    if (last_lc_name != 0)
8371ab64890Smrg	Xfree (last_lc_name);
8381ab64890Smrg    last_dir_len = strlen (dir_name) + 1;
8391ab64890Smrg    last_dir_name = Xmalloc (last_dir_len);
8401ab64890Smrg    strcpy (last_dir_name, dir_name);
8411ab64890Smrg    last_lc_name = Xmalloc (strlen (lc_name) + 1);
8421ab64890Smrg    strcpy (last_lc_name, lc_name);
8431ab64890Smrg
8441ab64890Smrg    return dir_name;
8451ab64890Smrg}
846