4.4BSD/usr/src/contrib/X11R5-lib/lib/X/Xsi/XlcLoad.c

Compare this file to the similar file:
Show the results in this format:

/*
 * $XConsortium: XlcLoad.c,v 1.44 92/12/14 09:22:48 rws Exp $
 */

/*
 * Copyright 1990, 1991 by OMRON Corporation, NTT Software Corporation,
 *                      and Nippon Telegraph and Telephone Corporation
 * Copyright 1991 by the Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the names of OMRON, NTT Software, NTT, and M.I.T.
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission. OMRON, NTT Software,
 * NTT, and M.I.T. make no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without express or
 * implied warranty.
 *
 * OMRON, NTT SOFTWARE, NTT, AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL OMRON, NTT SOFTWARE, NTT, OR M.I.T. BE
 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 *	Authors: Li Yuhong		OMRON Corporation
 *		 Tatsuya Kato		NTT Software Corporation
 *		 Hiroshi Kuribayashi	OMRON Corporation
 *   
 */

#include <stdio.h>
#include <ctype.h>
#include "Xlibint.h"
#include "Xi18nint.h"
#include <X11/Xos.h>

#if __STDC__ && !defined(NORCONST)
#define RConst const
#else
#define RConst /**/
#endif

#ifdef X_NOT_STDC_ENV
extern char *getenv();
#endif

#ifndef X_NOT_POSIX
#ifdef _POSIX_SOURCE
#include <limits.h>
#else
#define _POSIX_SOURCE
#include <limits.h>
#undef _POSIX_SOURCE
#endif
#endif
#ifndef PATH_MAX
#include <sys/param.h>
#ifndef PATH_MAX
#ifdef MAXPATHLEN
#define PATH_MAX MAXPATHLEN
#else
#define PATH_MAX 1024
#endif
#endif
#endif

#ifndef XNLSPATHDEFAULT
#define XNLSPATHDEFAULT "/usr/lib/X11/nls"
#endif

#define END_LINE    "END"

#define MAXLINEBUF  1024

#ifndef lint
static int lock_tbl;
#endif

static XLocaleDB *_Xlctbl_;

static char    *LoadLocaleName();
static _CSID    WhichCS();
static Codeset *LoadCodeset();
static Bool     LoadCSMappingTable();
static Bool     LoadCVMappingTable();
static char    *LoadDesignateSequence();
static Fontset *LoadFontList();
static void	GetCTidToCSid();

#define	COMMENTMARK	'#'

static void
rmblank(buf)
    char           *buf;
{
    char           *ptr;
    int             len;

    ptr = buf;
    while (*ptr != '\0' && isspace(*ptr))
	ptr++;
    len = strlen(ptr);
    bcopy(ptr, buf, len + 1);
    ptr = buf + len;
    while ((ptr > buf) && isspace(*(ptr - 1))) {
	*--ptr = '\0';
    }
}

#ifdef _FSTDIO
	/* do not conflict with fgetline in bsd stdio */
#define fgetline fgetlineX
#endif

static char *
fgetline(buf, len, fp)
    char       *buf;
    int		len;
    FILE       *fp;
{
    while (fgets(buf, len, fp)) {
	rmblank(buf);
	if ((*buf != '\0') && (*buf != COMMENTMARK)) {
	    return buf;
	}
    }
    return NULL;
}

static char *
fgettoken(buf, len, fp, sep, strch, endch)
    char       *buf;
    int		len;
    FILE       *fp;
    char       *sep, strch, endch;
{
    static char	buffer[256];
    static char	*ptr = NULL;
    static int	start_flag = 0, end_flag = 0;
    char	*last, *p;

    while (1) {
	if (ptr == NULL) {
            if (end_flag != 0) {
                start_flag = end_flag = 0;
                return NULL;
            }
	    if (fgetline(buffer, len, fp) == NULL) {
		return NULL;
	    }
            if (!strncmp(buffer, "END", 3) || !strncmp(buffer, "XLC", 3))
                return NULL;
	    ptr = buffer;
	}
	if (start_flag == 0) {
	    if ((ptr = index(ptr, strch)) == NULL) {
		continue;
	    }
	    ptr++;
	    start_flag = strch;
	}

	if (p = index(ptr, endch)) {
	    end_flag = endch;
	    *p = '\0';
	}
	for (; *ptr != '\0'; ptr++) {
	    if (!index(sep, *ptr)) {
		break;
	    }
	}
	
	if (*ptr == '\0') {
	    ptr = NULL;
	    if (end_flag != 0) {
		start_flag = 0;
		end_flag = 0;
		return NULL;
	    }
	    continue;
	}

	for (last = ptr; *ptr != '\0'; ptr++) {
	    if (index(sep, *ptr)) {
		break;
	    }
	}

	if (*ptr == '\0') {
	    ptr = NULL;
	}
	else {
	    *ptr++ = '\0';
	}

	strcpy(buf, last);
	rmblank(buf);
	if (*buf != '\0') {
	    return buf;
	}
    }
}

static char *
escape(str)
    char       *str;
{
    char	buf[128],
                *ptr,
                *ret;

    ptr = buf;
    while (*str) {
	switch (*str) {
	case '\\':
	    str++;
	    if ((*str >= '0') && (*str <= '7')) {
		int i, sum;

		sum = 0;
		for (i = 0; (i < 3) && *str; i++, str++) {
		    if ((*str < '0') || (*str > '7')) {
			break;
		    }
		    sum = sum * 8 + (*str - '0');
		}
		*ptr++ = (sum & 0xff);
		continue;
	    } else if (*str == 'e' || *str == 'E') {
		*ptr++ = 0x1b;
		continue;
	    }
	    /* through into default: */
	default:
	    *ptr++ = *str++;
	    break;
	}
    }
    *ptr = '\0';
    if (ret = (char *) Xmalloc((unsigned)strlen(buf) + 1))
	strcpy(ret, buf);
    return ret;
}


static XLocaleDB *
_XlcLoadLocale(file_name, errp)
    char           *file_name;
    int		   *errp;
{
    FILE           *fp;
    char            buf[MAXLINEBUF];
    XLocaleDB      *xlocale; 

    _XInitCTEncoding();          /* should done in system initial time. */
    *errp = BadAccess;
    if ((fp = fopen(file_name, "r")) == NULL)
        return ((XLocaleDB *)NULL);
    *errp = BadAlloc;
    xlocale = (XLocaleDB *) Xmalloc(sizeof(XLocaleDB));
    if (!xlocale) {
	fclose(fp);
	return ((XLocaleDB *)NULL);
    }

    while (fgetline(buf, MAXLINEBUF, fp) != NULL) {
        if (!strncmp(buf, "XLC_ALL", 7)) {
            if ((xlocale->lc_name = LoadLocaleName(fp)) == NULL)
                return (NULL);
        } else if (!strncmp(buf, "XLC_ENCODING", 12)) {
            if ((xlocale->lc_encoding = LoadLocaleName(fp)) == NULL)
                return (NULL);
        } else if (!strncmp(buf, "XLC_CODESET", 11)) {
            if ((xlocale->lc_codeset = LoadCodeset(fp)) == NULL)
                return (NULL);
        } else if (!strncmp(buf, "XLC_FONTSET", 11)) {
            if ((xlocale->lc_fontset = LoadFontList(fp)) == NULL)
                return (NULL);
        } else {
	    *errp = BadValue;
            return (NULL);
        }
    }
    GetCTidToCSid(xlocale->lc_fontset);
    /*
     * set locale name for each category.
     */
    fclose(fp);
    *errp = 0;
    return (xlocale);
}

static char *
LoadLocaleName(fp)
    FILE           *fp;
{
    char            buf[MAXLINEBUF];
    char           *s;

    if (fgetline(buf, MAXLINEBUF, fp) == NULL)
        return (NULL);
    if (s = Xmalloc((unsigned)strlen(buf) + 1)) {
	(void) strcpy(s, buf);
	if ((fgetline(buf, MAXLINEBUF, fp)) == NULL)
	    return (NULL);
    }
    return (s);
}

static Codeset *
LoadCodeset(fp)
    FILE           *fp;
{
    char            buf[MAXLINEBUF],
                   *ptr, *nptr;
    int             num;
    Codeset        *codeset;
    int		    mb_cur_max = 0;
    _CSID	    cds_GL, cds_GR;
	
#define MAGIC_NO 0xff

    if (fgetline(buf, MAXLINEBUF, fp) == NULL) {
        return (NULL);
    }
    codeset = (Codeset *) Xmalloc(sizeof(Codeset));
    if (!codeset)
	return ((Codeset *)NULL);
    ptr = buf;
    if (nptr = index(ptr, ':')) *nptr = '\0';

    if ((*ptr == 'l') || (*ptr == 'L')) {
	codeset->cds_type = CDS_STATELESS;
	cds_GL = cds_GR = CODESET0;
        LoadCSMappingTable(fp, codeset);
    } else if ((*ptr == 'f') || (*ptr == 'F')) {
	codeset->cds_type = CDS_STATEFUL;
	cds_GL = cds_GR = MAGIC_NO;	/* not indicate */
    } else {
	codeset->cds_type = CDS_SELFDEFINED;
	cds_GL = cds_GR = MAGIC_NO;	/* not indicate */
    }
    codeset->cds_mb_cur_max = 1;
    for (num = 0; ptr = nptr; num++) {
	ptr++;
	if (nptr = index(ptr, ':')) *nptr = '\0';
	mb_cur_max =
        codeset->cds_mblen[num] = atoi(ptr);
        if (codeset->cds_type == CDS_STATEFUL) {
            codeset->cds_dsg[num] = LoadDesignateSequence(fp);
	    mb_cur_max += strlen(codeset->cds_dsg[num]);
	    /* set inital designate */
	    if (_XctisGLdsg(codeset->cds_dsg[num])) {
		if (cds_GL == MAGIC_NO)
		    cds_GL = num;
	    } else if (cds_GR == MAGIC_NO) {
		cds_GR = num;
	    }
        }
	codeset->cds_mb_cur_max = max(codeset->cds_mb_cur_max, mb_cur_max);
    }
    if (cds_GR == MAGIC_NO)
	cds_GR = cds_GL;
    codeset->mb_init = (GL << 16) | cds_GR << 8 | cds_GL;
    codeset->cds_num = num;
    LoadCVMappingTable(fp, num, codeset);

    while (fgetline(buf, MAXLINEBUF, fp)) { /* read trailing garbage */
    }
    return codeset;
#undef MAGIC_NO
}

static          Bool
LoadCSMappingTable(fp, codeset)
    FILE           *fp;
    Codeset        *codeset;
{
    char            buf[MAXLINEBUF];
    int             count = 0;

    while (fgettoken(buf, MAXLINEBUF, fp, ", ", '{', '}')) {
        codeset->cds_map[count++] = WhichCS(buf);
    }
    if (count < 256) {
        return False;
    }
    return True;
}

#define MAXSPLITS   8096    /* should be max. 96 x 96 */

static          Bool
LoadCVMappingTable(fp, num_cs, ccs)
    FILE           *fp;
    int            num_cs;
    Codeset        *ccs;
{
    char            buf[MAXLINEBUF];
    Range          *cnv;
    int             num = 0,
                    i, lastnum;

    if ( ! ( cnv = (Range *)Xmalloc(MAXSPLITS*sizeof(Range))))
	return False;

    ccs->cds_msbon = 0;     /* use msb on */
    for (i = 1, ccs->cds_cnvindex[0] = 0; i <= num_cs; i++) {
        lastnum = num;
        while (fgettoken(buf, MAXLINEBUF, fp, ", ", '(', ')')) {
            sscanf(buf, "%x=%x:%x", &(cnv[num].mb_start),
                    &(cnv[num].cs_start), &(cnv[num].cs_end));
            cnv[num].mb_end = cnv[num].mb_start +
                              cnv[num].cs_end - cnv[num].cs_start;
	    if (cnv[num].mb_start & 0x80808080 || cnv[num].mb_end & 0x80808080)
		ccs->cds_msbon = 1;     /* use msb on */
            num++;
        }
        if (lastnum == num) /* no more conversion element */
            break;
        ccs->cds_cnvindex[i] = num;
    }
    /*
     * Notes: the number of charsets >= number of fonts
     *        No font, no conversion list, e.g, charset #3 of UJIS.
     *
     * The last one [num_cs] of index is guider.
     */
    for (; i <= num_cs; i++)
        ccs->cds_cnvindex[i] = num;

    /* set which half (GL/GR) of font should be used. */
    for (i = 0; i < num_cs; i++) {
	ccs->cs_offset[i] = cnv[ccs->cds_cnvindex[i]].cs_start & 0x8080;
    }

    ccs->cds_cnvlist = (Range *) Xmalloc((unsigned)sizeof(Range) * num);
    if (!ccs->cds_cnvlist) {
	Xfree((char *)cnv);
	return False;
    }
#ifdef  FASTCOPY
    /* not used */
    (void) bcopy((char *)cnv, (char *)ccs->cds_cnvlist,
                 sizeof(Range) * num);
#else
    for (i = 0; i < num; i++) {
        ccs->cds_cnvlist[i].mb_start = cnv[i].mb_start;
        ccs->cds_cnvlist[i].mb_end = cnv[i].mb_end;
        ccs->cds_cnvlist[i].cs_start = cnv[i].cs_start;
        ccs->cds_cnvlist[i].cs_end = cnv[i].cs_end;
    }
#endif
    Xfree((char *)cnv);
    return True;

}

static char *
LoadDesignateSequence(fp)
    FILE       *fp;
{
    char      	buf[MAXLINEBUF];

    if (fgetline(buf, MAXLINEBUF, fp) == NULL) {
        return False;
    }
    return escape(buf);
}

static _CSID
WhichCS(s)
    char       *s;
{
    _CSID      	n;

    if (!strncmp(s, "CS0", 3)) return CODESET0;
    if (!strncmp(s, "CS1", 3)) return CODESET1;
    if (!strncmp(s, "CS2", 3)) return CODESET2;
    if (!strncmp(s, "CS3", 3)) return CODESET3;
    if (!strncmp(s, "CS4", 3)) return CODESET4;
    if (!strncmp(s, "CS5", 3)) return CODESET5;
    if (!strncmp(s, "CS6", 3)) return CODESET6;
    if (!strncmp(s, "CS7", 3)) return CODESET7;
    if (!strncmp(s, "CS8", 3)) return CODESET8;
    if (!strncmp(s, "CS9", 3)) return CODESET9;
    if (!strncmp(s, "CSA", 3)) return CODESETA;
    if (!strncmp(s, "CSB", 3)) return CODESETB;
    if (!strncmp(s, "CSC", 3)) return CODESETC;
    if (!strncmp(s, "CSD", 3)) return CODESETD;
    if (!strncmp(s, "CSE", 3)) return CODESETE;
    if (!strncmp(s, "CSF", 3)) return CODESETF;
    if (!strncmp(s, "C0", 2)) return C0;
    if (!strncmp(s, "C1", 2)) return C1;
    if (!strncmp(s, "ND", 2)) return ND;
    if (!strncmp(s, "-", 1)) {
        s++;
        n = '0' - *s;
        return n;
    }
    return ND;
}

static Bool
LoadOneCharset(str, cset)
    char       *str;
    Charset    *cset;
{
    wchar	woffset;
    char       *esc;
    char       *nstr;
    extern Bool _XRegisterCharSet();

    if (nstr = index(str, ':')) *nstr = '\0';
    cset->cs_name = (char *) Xmalloc((unsigned)strlen(str) + 1);
    if (!cset->cs_name)
	return (False);
    strcpy(cset->cs_name, str);
    if (!(str = nstr))
        return False;
    str++;
    if (nstr = index(str, ':')) *nstr = '\0';
    cset->cs_GLorGR = (strcmp(str, "GL") == 0) ? GL : GR;
    if (!nstr) {
        /*
         * standard CharsetRegistry-CharsetEncoding registered by X,
         */
        if (_XcwNameGetAll(cset->cs_name, &esc, &woffset, cset->cs_GLorGR) == False)
            return False;

        cset->cs_des = strcpy(Xmalloc((unsigned)strlen(esc) + 1), esc);
	if (!cset->cs_des)
	    return (False);
        cset->cs_woff = woffset;
        /*
         * only 2 or 1 bytes font encoding in X.
         */
        cset->cs_len = (cset->cs_des[1] == '$') ? 2 : 1;
        return True;
    }
    /*
     * The form: CharsetRegistry-CharsetEncoding:woffset:escapeseq
     */
    if (!(str = nstr))
        return False;
    str++;
    if (nstr = index(str, ':')) *nstr = '\0';
    sscanf(str, "%x", &woffset);
    if (!(str = nstr))
        return False;
    str++;
    if (nstr = index(str, ':')) *nstr = '\0';
    cset->cs_des = escape(str);
    cset->cs_woff = woffset;
    if (cset->cs_des[1] == 0x25)
	cset->cs_len = cset->cs_des[3] - '0';
    else
	cset->cs_len = (cset->cs_des[1] == '$') ? 2 : 1;
    return _XRegisterCharSet(cset->cs_name, cset->cs_des, cset->cs_woff,
			     cset->cs_GLorGR, cset->cs_len);
}

static Fontset *
LoadFontList(fp)
    FILE       *fp;
{
    Fontset    *flist;
    Charset    *csets[MAXCHARSETS];
    char        buf[MAXLINEBUF];
    char       *s;
    int         cnt, i;

    cnt = 0;
    while ((s = fgetline(buf, MAXLINEBUF, fp)) != NULL) {
        if (!strncmp(s, END_LINE, 3))
            break;
        csets[cnt] = (Charset *) Xmalloc(sizeof(Charset));
	if (!csets[cnt]) {
	    for (cnt--; cnt >= 0; cnt--)
		Xfree((char *)csets[cnt]);
	    return ((Fontset *)NULL);
	}
        if (LoadOneCharset(buf, csets[cnt]) == True)
            cnt++;
        else {
            /* should return error. */  
	    for (cnt--; cnt >= 0; cnt--)
		Xfree((char *)csets[cnt]);
	    return ((Fontset *)NULL);
	}
    }               /* end of while */
    flist = (Fontset *) Xmalloc(sizeof(Fontset));
    if (!flist) {
	for (cnt--; cnt >= 0; cnt--)
	    Xfree((char *)csets[cnt]);
	return ((Fontset *)NULL);
    }
    flist->fs_num = cnt;
    flist->fs_cset = (Charset **) Xmalloc((unsigned)cnt * sizeof(Charset *));
    if (!flist->fs_cset) {
	Xfree((char *)flist);
	for (cnt--; cnt >= 0; cnt--)
	    Xfree((char *)csets[cnt]);
	return ((Fontset *)NULL);
    }
    for (i = 0; i < cnt; i++) {
      /* To avoid the bug of gcc 1.37 optimization,
       * temporary variable `temp' was introduced
       * by demizu on Feb 19, 1991 */
      register Charset *temp;
	temp = csets[i];
	flist->fs_cset[i] = temp;

    }
    return (flist);
}

static void
GetCTidToCSid(fontset)
    Fontset	*fontset;
{
    int i;
    for (i = 0; i < fontset->fs_num; i++) {
	fontset->fs_cset[i]->cs_id =
		_XcwNameGetGLorGRId(fontset->fs_cset[i]->cs_name,
				    fontset->fs_cset[i]->cs_GLorGR);
    }
}

/* Read/Load locale file */
struct _NLSAlias {
    char *lc_alias;
    char *lc_name;
};

static struct _NLSAlias *NLSAlias = NULL;

struct _NLSDir {
    char *dir;
    char *lc_name;
    char *fn;
};

static struct _NLSDir *NLSDir = NULL;

#ifndef lint
static int lock;
#endif

#define XREALLOC(p, size) ((p) ? Xrealloc(p, size) : Xmalloc(size))
static void
ReadNLSAlias(dir)
    char       	       *dir;
{
    char 		fname[PATH_MAX];
    char 		alias[PATH_MAX];
    char 		lang_name[PATH_MAX];
    char 		str[PATH_MAX];
    int 		count, i;
    struct _NLSAlias   *p, *ptr = NULL;
    static 		size = 0;
    FILE       	       *fp;


    sprintf(fname, "%s/nls.alias", dir);
    if ((fp = fopen(fname, "r")) == NULL)
	return;

    if (fgetline(str, PATH_MAX, fp) == NULL) {
	fclose(fp);
	return;
    }
    count = sscanf(str, "%d", &i);
    if (i < 1) {
	fclose(fp);
	return;
    }

    NLSAlias = (struct _NLSAlias *) XREALLOC((char *)NLSAlias,
			(unsigned)sizeof(struct _NLSAlias) * (size + i + 1));

    if (!NLSAlias) {
	fclose(fp);
	return;
    }

    ptr = NLSAlias + size;
    for (;;) {
	if (fgetline(str, PATH_MAX, fp) == NULL)
	    break;
	count = sscanf(str, "%s %s", alias, lang_name);
	if (count != 2)
	    break;
	for (p = NLSAlias; p != ptr; p++)
	    if (strcmp(alias, p->lc_alias) == 0)
		break;
	if (p != ptr)
	    continue;
	ptr->lc_alias = Xmalloc((unsigned)strlen(alias)+1);
	if (!ptr->lc_alias)
	    break;
	strcpy(ptr->lc_alias, alias);
	ptr->lc_name = Xmalloc((unsigned)strlen(lang_name)+1);
	if (!ptr->lc_name)
	    break;
	strcpy(ptr->lc_name, lang_name);
	ptr++;
	size++;
    }
    ptr->lc_alias = NULL;
    fclose(fp);
}

static void
ReadNLSDir(dir)
    char       	       *dir;
{
    char 		fname[PATH_MAX];
    char 		file_name[PATH_MAX];
    char 		lang_name[PATH_MAX];
    char 		str[PATH_MAX];
    char       	       *tmp;
    int 		count, i;
    struct _NLSDir     *p, *ptr = NULL;
    static 		size = 0;
    FILE	       *fp;

    sprintf(fname, "%s/nls.dir", dir);
    if ((fp = fopen(fname, "r")) == NULL)
	return;

    if (fgetline(str, PATH_MAX, fp) == NULL) {
	fclose(fp);
	return;
    }
    /* read number of locales */
    count = sscanf(str, "%d", &i);
    if (i < 1) {
	fclose(fp);
	return;
    }

    NLSDir = (struct _NLSDir *) XREALLOC((char *)NLSDir,
			(unsigned)sizeof(struct _NLSDir) * (size + i + 1));

    if (!NLSDir) {
	fclose(fp);
	return;
    }

    ptr = NLSDir + size;
    tmp = ptr->dir = Xmalloc((unsigned)strlen(dir) + 1);
    if (!ptr->dir) {
	fclose(fp);
	return;
    }
    strcpy(ptr->dir, dir);
    for (;;) {
	if (fgetline(str, PATH_MAX, fp) == NULL)
	    break;
	count = sscanf(str, "%s %s", file_name, lang_name);
	if(count == 1)
	    strcpy(lang_name, file_name);
	else if (count < 1)
	    break;
	for (p = NLSDir; p != ptr; p++)
	    if (strcmp(lang_name, p->lc_name) == 0)
		break;
	if (p != ptr)
	    continue;
	ptr->fn = Xmalloc((unsigned)strlen(file_name)+1);
	if (!ptr->fn)
	    break;
	strcpy(ptr->fn, file_name);
	ptr->lc_name = Xmalloc((unsigned)strlen(lang_name)+1);
	if (!ptr->lc_name)
	    break;
	strcpy(ptr->lc_name, lang_name);
	ptr->dir = tmp;
	ptr++;
	size++;
    }
    ptr->dir = NULL;
    fclose(fp);
    fp = NULL;
}


static void
ReadNLS()
{
    char 		nlspath[PATH_MAX];
    char 	       *path;
    char	       *dir;
    char 	       *env;

    LockMutex(&lock);
    if (NLSDir != NULL) {
	UnlockMutex(&lock);
	return;
    }
    if ((env = getenv("XNLSPATH")) == NULL) {
	env = XNLSPATHDEFAULT;
    }
    path = nlspath;
    strcpy(path, env);

    while (1) {
	if (path == NULL) {
	    UnlockMutex(&lock);
	    return;
	}
	dir = path;
	if ((path = index(dir, ':')) != NULL) {
	    *path++ = '\0';
	}

	ReadNLSDir(dir);
	ReadNLSAlias(dir);
    }
    UnlockMutex(&lock);
}

static void
_XlcFindNLSDir(lc_name, fname)
    char       	       *lc_name;
    char       	       *fname;
{
    struct _NLSDir     *ptr;

    ReadNLS();
    ptr = NLSDir;
    if (!ptr) {
	*fname = 0;
	return;
    }
    while (1) {
	if (ptr->dir == NULL) {
	    *fname = 0;
	    break;
	}
	if (strcmp(lc_name, ptr->lc_name) == 0) {
	   sprintf(fname, "%s/%s", ptr->dir, ptr->fn);
	   break;
	}
	ptr++;
    }
}

static char *lastBadLCName;

static XLocaleDB *
_XlcGetLocaleDB(lc_name)
    char	*lc_name;	/* locale name */
{
    XLocaleDB	  *xlc_db;
    char	  fn[PATH_MAX];
    int		  err;

    LockMutex(&lock_tbl);
    for (xlc_db = _Xlctbl_; xlc_db; xlc_db = xlc_db->next) {
	if (!strcmp(lc_name, xlc_db->lc_name)) {
	    UnlockMutex(&lock_tbl);
	    return xlc_db;
	}
    }
    _XlcFindNLSDir(lc_name, fn);
    if (*fn) {
	xlc_db = _XlcLoadLocale(fn, &err);
	if (xlc_db) {
	    xlc_db->next = _Xlctbl_;
	    _Xlctbl_ = xlc_db;
	} else if ((err != BadAccess || strcmp(lc_name, "C")) &&
		   (!lastBadLCName || strcmp(lc_name, lastBadLCName))) {
	    if (err == BadAccess)
		fprintf(stderr, "Xlib: missing locale file: %s\n", fn);
	    else if (err == BadAlloc)
		fprintf(stderr, "Xlib: failed to load locale file: %s\n", fn);
	    else
		fprintf(stderr, "Xlib: bad format locale file: %s\n", fn);
	    if (lastBadLCName)
		Xfree(lastBadLCName);
	    if (lastBadLCName = Xmalloc(strlen(lc_name) + 1))
		strcpy(lastBadLCName, lc_name);
	}
    }
    UnlockMutex(&lock_tbl);
    return (xlc_db);
}

static char *
_XlcResolveName(lc_name)
    char	        *lc_name;
{
    struct _NLSAlias    *ptr;

    ReadNLS();
    if (NLSAlias)
	for (ptr = NLSAlias; ptr->lc_alias != NULL ; ptr++) {
	    if (!strcmp(lc_name, ptr->lc_alias)) {
		return (ptr->lc_name);
	    }
	}
    return(lc_name);
}

/* list up supported locale name */
/* need to call Xfree(list) by caller */
int
_XlcListLocale(list)
    char 	      **list[];
{
    struct _NLSDir     *ptr;
    char 	      **p;
    int			i;

    ReadNLS();
    for (i = 0, ptr = NLSDir; ptr->dir != NULL ; ptr++, i++) ;
    p = *list = (char **) Xmalloc((unsigned)sizeof(char *) * (i + 1));
    if (!p)
	return (0);
    for (ptr = NLSDir; ptr->dir != NULL ; ptr++)
	*p++ = ptr->lc_name;
    *p = NULL;
    return(i);
}

XLocale
_XlcMakeLocale(lc_name)
    char       *lc_name;	/* locale name */
{
    char       *p;
    char       *lc_alias;
    char	lang[256];
    XLocaleDB  *xlc_db;
    XLocale	xlc;

    /* extract locale name */
    lc_alias = _XlcResolveName(lc_name);

    p = index(lc_alias, '@');
    if (p) {
	strncpy(lang, lc_alias, p - lc_alias);
	lang[p - lc_alias] = '\0';
	lc_alias = lang;
    }

    xlc_db = _XlcGetLocaleDB(lc_alias);
    if (!xlc_db)
	return NULL;
    xlc = (XLocale)Xmalloc(sizeof(XLocaleRec));
    if (!xlc)
	return NULL;

    xlc->xlc_db = xlc_db;
    xlc->lc_lang = xlc_db->lc_name;
    _Xctinit(xlc);
    _Xmbinit(xlc);

    return xlc;
}

XLocale
_XlcDupLocale(xlocale)
    XLocale	xlocale;
{
    XLocale	new;

    if ((new = (XLocale) Xmalloc(sizeof(XLocaleRec))) == NULL) {
	return ((XLocale)NULL);
    }
    new->xlc_db = xlocale->xlc_db;
    new->lc_lang = xlocale->lc_lang;
    _Xctinit(new);
    _Xmbinit(new);

    return new;
}

XLocale
_XFallBackConvert()
{
    static XLocale xlc_xlc;
    char *osname = setlocale(LC_CTYPE, (char *)NULL);
    char *name;
    XLocale xlc;
#if !defined(X_NOT_STDC_ENV) && !defined(X_LOCALE)
    char siname[256];
    char *_XlcMapOSLocaleName();
#endif

    if (xlc_xlc && !strcmp(xlc_xlc->lc_lang, osname))
	return xlc_xlc;
#if !defined(X_NOT_STDC_ENV) && !defined(X_LOCALE)
    name = _XlcMapOSLocaleName(osname, siname);
#else
    name = osname;
#endif
    xlc = _XlcMakeLocale(name);
    if (!xlc)
	return NULL;
    name = Xmalloc(strlen(osname) + 1);
    if (!name) {
	_XlcFreeLocale(xlc);
	return NULL;
    }
    strcpy(name, osname);
    xlc->lc_lang = name;
    if (xlc_xlc)
	_XlcFreeLocale(xlc_xlc);
    xlc_xlc = xlc;
    return xlc;
}

static void
_Xsimbinit(state)
    XPointer state;
{
    XLocale xlc = (XLocale)state;

    _Xmbinit(xlc);
}

/*ARGSUSED*/
static void
_Xsimbfinish(state)
    XPointer state;
{
}

static char
_Xsimbchar(state, str, lenp)
    XPointer state;
    char *str;
    int *lenp;
{
    _CSID csid;
    char ret = 'a';
    int cscode;
    int dlen;

    csid = _Xmbcsid((XLocale)state, str);
    dlen = _Xmbdlen((XLocale)state, str);
    if (dlen > 0) {
	for (*lenp = 0; ; ) {
	    str += dlen;
	    *lenp += dlen;
	    dlen = _Xmbdlen((XLocale)state, str);
	    if (dlen == 0)  return (ret);
	}
    }
    csid = _Xmbcsid((XLocale)state, str);
    if (csid == C0) {
	ret = *str;
    } else if (csid == CODESET0) {
	_Xmbctocsc((XLocale)state, str, &cscode);
	if (cscode < 0x7f)
	    ret = (char)cscode;
    }
    *lenp = _Xmblen((XLocale)state);
    return (ret);
}

static char *
_Xsilcname(state)
    XPointer state;
{
    return ((XLocale)state)->lc_lang;
}

static void _Xsidestroy(state)
    XPointer state;
{
    _XlcFreeLocale((XLocale)state);
}

static RConst XrmMethodsRec mb_methods = {
    _Xsimbinit,
    _Xsimbchar,
    _Xsimbfinish,
    _Xsilcname,
    _Xsidestroy
};

XrmMethods
_XrmInitParseInfo(state)
    XPointer *state;
{
    XLocale xlc = _XFallBackConvert();

    if (xlc)
	xlc = _XlcDupLocale(xlc);
    if (!xlc)
	return NULL;
    *state = (XPointer)xlc;
    return (XrmMethods)&mb_methods;
}