Home | History | Annotate | Line # | Download | only in xlibi18n
      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  *  (c) Copyright 1995 FUJITSU LIMITED
     27  *  This is source code modified by FUJITSU LIMITED under the Joint
     28  *  Development Agreement for the CDE/Motif PST.
     29  */
     30 
     31 
     32 
     33 #ifndef	NOT_X_ENV
     34 
     35 #ifdef HAVE_CONFIG_H
     36 #include <config.h>
     37 #endif
     38 #include <X11/Xlib.h>
     39 #include <X11/Xresource.h>
     40 #include "Xlibint.h"
     41 #include "XlcPubI.h"
     42 #include "reallocarray.h"
     43 
     44 #else	/* NOT_X_ENV */
     45 
     46 #define	Xmalloc	malloc
     47 #define	Xrealloc	realloc
     48 #define	Xfree	free
     49 
     50 #endif	/* NOT_X_ENV */
     51 
     52 /* specifying NOT_X_ENV allows users to just use
     53    the database parsing routine. */
     54 /* For UDC/VW */
     55 #ifndef	BUFSIZE
     56 #define	BUFSIZE	2048
     57 #endif
     58 
     59 #ifdef COMMENT
     60 #ifdef  BUFSIZE
     61 #undef BUFSIZE
     62 #endif
     63 #define BUFSIZE 6144 /* 2048*3 */
     64 #endif
     65 
     66 #include <stdio.h>
     67 
     68 typedef struct _DatabaseRec {
     69     char *category;
     70     char *name;
     71     char **value;
     72     int value_num;
     73     struct _DatabaseRec *next;
     74 } DatabaseRec, *Database;
     75 
     76 typedef enum {
     77     S_NULL,	/* outside category */
     78     S_CATEGORY,	/* inside category */
     79     S_NAME,	/* has name, expecting values */
     80     S_VALUE
     81 } ParseState;
     82 
     83 typedef enum {
     84     T_NEWLINE,
     85     T_COMMENT,
     86     T_SEMICOLON,
     87     T_DOUBLE_QUOTE,
     88     T_LEFT_BRACE,
     89     T_RIGHT_BRACE,
     90     T_SPACE,
     91     T_TAB,
     92     T_BACKSLASH,
     93     T_NUMERIC_HEX,
     94     T_NUMERIC_DEC,
     95     T_NUMERIC_OCT,
     96     T_DEFAULT
     97 } Token;
     98 
     99 typedef struct {
    100     Token token;	/* token id */
    101     int len;		/* length of token sequence */
    102 } TokenTable;
    103 
    104 static int f_newline (const char *str, Token token, Database *db);
    105 static int f_comment (const char *str, Token token, Database *db);
    106 static int f_semicolon (const char *str, Token token, Database *db);
    107 static int f_double_quote (const char *str, Token token, Database *db);
    108 static int f_left_brace (const char *str, Token token, Database *db);
    109 static int f_right_brace (const char *str, Token token, Database *db);
    110 static int f_white (const char *str, Token token, Database *db);
    111 static int f_backslash (const char *str, Token token, Database *db);
    112 static int f_numeric (const char *str, Token token, Database *db);
    113 static int f_default (const char *str, Token token, Database *db);
    114 
    115 static const TokenTable token_tbl[] = {
    116     { T_NEWLINE,      1 },
    117     { T_COMMENT,      1 },
    118     { T_SEMICOLON,    1 },
    119     { T_DOUBLE_QUOTE, 1 },
    120     { T_LEFT_BRACE,   1 },
    121     { T_RIGHT_BRACE,  1 },
    122     { T_SPACE,        1 },
    123     { T_TAB,          1 },
    124     { T_BACKSLASH,    1 },
    125     { T_NUMERIC_HEX,  2 },
    126     { T_NUMERIC_DEC,  2 },
    127     { T_NUMERIC_OCT,  2 },
    128     { T_DEFAULT,      1 } /* any character */
    129 };
    130 
    131 #define	SYM_CR          '\r'
    132 #define	SYM_NEWLINE	'\n'
    133 #define	SYM_COMMENT	'#'
    134 #define	SYM_SEMICOLON	';'
    135 #define	SYM_DOUBLE_QUOTE	'"'
    136 #define	SYM_LEFT_BRACE	'{'
    137 #define	SYM_RIGHT_BRACE	'}'
    138 #define	SYM_SPACE	' '
    139 #define	SYM_TAB		'\t'
    140 #define	SYM_BACKSLASH	'\\'
    141 
    142 /************************************************************************/
    143 
    144 #define MAX_NAME_NEST	64
    145 
    146 typedef struct {
    147     ParseState pre_state;
    148     char *category;
    149     char *name[MAX_NAME_NEST];
    150     int nest_depth;
    151     char **value;
    152     int value_len;
    153     int value_num;
    154     int bufsize;        /* bufMaxSize >= bufsize >= 0 */
    155     int bufMaxSize;     /* default : BUFSIZE */
    156     char *buf;
    157 } DBParseInfo;
    158 
    159 static DBParseInfo parse_info;
    160 
    161 static void
    162 init_parse_info (void)
    163 {
    164     static int allocated /* = 0 */;
    165     char *ptr;
    166     int size;
    167     if (!allocated) {
    168 	bzero(&parse_info, sizeof(DBParseInfo));
    169 	parse_info.buf = Xmalloc(BUFSIZE);
    170 	parse_info.bufMaxSize = BUFSIZE;
    171 	allocated = 1;
    172 	return;
    173     }
    174     ptr = parse_info.buf;
    175     size = parse_info.bufMaxSize;
    176     bzero(&parse_info, sizeof(DBParseInfo));
    177     parse_info.buf = ptr;
    178     parse_info.bufMaxSize = size;
    179 }
    180 
    181 static void
    182 clear_parse_info (void)
    183 {
    184     int i;
    185     char *ptr;
    186     int size;
    187     parse_info.pre_state = S_NULL;
    188     if (parse_info.category != NULL) {
    189 	Xfree(parse_info.category);
    190     }
    191     for (i = 0; i <= parse_info.nest_depth; ++i) {
    192 	if (parse_info.name[i]) {
    193 	    Xfree(parse_info.name[i]);
    194 	}
    195     }
    196     if (parse_info.value) {
    197 	if (*parse_info.value) {
    198 	    Xfree(*parse_info.value);
    199 	}
    200 	Xfree(parse_info.value);
    201     }
    202     ptr = parse_info.buf;
    203     size = parse_info.bufMaxSize;
    204     bzero(&parse_info, sizeof(DBParseInfo));
    205     parse_info.buf = ptr;
    206     parse_info.bufMaxSize = size;
    207 }
    208 
    209 static Bool
    210 realloc_parse_info(
    211     int len)
    212 {
    213     char *p;
    214     int newsize = BUFSIZE * ((parse_info.bufsize + len)/BUFSIZE + 1);
    215 
    216     p = Xrealloc(parse_info.buf, newsize);
    217     if (p == NULL)
    218         return False;
    219     parse_info.bufMaxSize = newsize;
    220     parse_info.buf = p;
    221 
    222     return True;
    223 }
    224 
    225 /************************************************************************/
    226 
    227 typedef struct _Line {
    228     char *str;
    229     int cursize;
    230     int maxsize;
    231     int seq;
    232 } Line;
    233 
    234 static void
    235 free_line(
    236     Line *line)
    237 {
    238     if (line->str != NULL) {
    239 	Xfree(line->str);
    240     }
    241     bzero(line, sizeof(Line));
    242 }
    243 
    244 static int
    245 realloc_line(
    246     Line *line,
    247     int size)
    248 {
    249     char *str = line->str;
    250 
    251     if (str != NULL) {
    252 	str = Xrealloc(str, size);
    253     } else {
    254 	str = Xmalloc(size);
    255     }
    256     if (str == NULL) {
    257 	/* malloc error */
    258 	if (line->str != NULL) {
    259 	    Xfree(line->str);
    260 	}
    261 	bzero(line, sizeof(Line));
    262 	return 0;
    263     }
    264     line->str = str;
    265     line->maxsize = size;
    266     return 1;
    267 }
    268 
    269 #define	iswhite(ch)	((ch) == SYM_SPACE   || (ch) == SYM_TAB)
    270 
    271 static void
    272 zap_comment(
    273     char *str,
    274     int *quoted)
    275 {
    276     char *p = str;
    277 #ifdef	never
    278     *quoted = 0;
    279     if (*p == SYM_COMMENT) {
    280 	int len = strlen(str);
    281 	if (p[len - 1] == SYM_NEWLINE || p[len - 1] == SYM_CR) {
    282 	    *p++ = SYM_NEWLINE;
    283 	}
    284 	*p = '\0';
    285     }
    286 #else
    287     while (*p) {
    288 	if (*p == SYM_DOUBLE_QUOTE) {
    289 	    if (p == str || p[-1] != SYM_BACKSLASH) {
    290 		/* unescaped double quote changes quoted state. */
    291 		*quoted = *quoted ? 0 : 1;
    292 	    }
    293 	}
    294 	if (*p == SYM_COMMENT && !*quoted) {
    295 	    int pos = p - str;
    296 	    if (pos == 0 ||
    297 	        (iswhite(p[-1]) && (pos == 1 || p[-2] != SYM_BACKSLASH))) {
    298 		int len = (int) strlen(p);
    299 		if (len > 0 && (p[len - 1] == SYM_NEWLINE || p[len-1] == SYM_CR)) {
    300 		    /* newline is the identifier for finding end of value.
    301 		       therefore, it should not be removed. */
    302 		    *p++ = SYM_NEWLINE;
    303 		}
    304 		*p = '\0';
    305 		break;
    306 	    }
    307 	}
    308 	++p;
    309     }
    310 #endif
    311 }
    312 
    313 static int
    314 read_line(
    315     FILE *fd,
    316     Line *line)
    317 {
    318     char buf[BUFSIZE], *p;
    319     int len;
    320     int quoted = 0;	/* quoted by double quote? */
    321     char *str;
    322     int cur;
    323 
    324     str = line->str;
    325     cur = line->cursize = 0;
    326 
    327     while ((p = fgets(buf, BUFSIZE, fd)) != NULL) {
    328 	++line->seq;
    329 	zap_comment(p, &quoted);	/* remove comment line */
    330 	len = (int) strlen(p);
    331 	if (len == 0) {
    332 	    if (cur > 0) {
    333 		break;
    334 	    }
    335 	    continue;
    336 	}
    337 	if (cur + len + 1 > line->maxsize) {
    338 	    /* need to reallocate buffer. */
    339 	    if (! realloc_line(line, line->maxsize + BUFSIZE)) {
    340 		return -1;	/* realloc error. */
    341 	    }
    342 	    str = line->str;
    343 	}
    344 	memcpy(str + cur, p, (size_t) len);
    345 
    346 	cur += len;
    347 	str[cur] = '\0';
    348 	if (!quoted && cur > 1 && str[cur - 2] == SYM_BACKSLASH &&
    349 	    (str[cur - 1] == SYM_NEWLINE || str[cur-1] == SYM_CR)) {
    350 	    /* the line is ended backslash followed by newline.
    351 	       need to concatinate the next line. */
    352 	    cur -= 2;
    353 	    str[cur] = '\0';
    354 	} else if (len < BUFSIZE - 1 || buf[len - 1] == SYM_NEWLINE ||
    355 		   buf[len - 1] == SYM_CR) {
    356 	    /* the line is shorter than BUFSIZE. */
    357 	    break;
    358 	}
    359     }
    360     if (quoted) {
    361 	/* error.  still in quoted state. */
    362 	return -1;
    363     }
    364     return line->cursize = cur;
    365 }
    366 
    367 /************************************************************************/
    368 
    369 static Token
    370 get_token(
    371     const char *str)
    372 {
    373     switch (*str) {
    374     case SYM_NEWLINE:
    375     case SYM_CR:		return T_NEWLINE;
    376     case SYM_COMMENT:		return T_COMMENT;
    377     case SYM_SEMICOLON:		return T_SEMICOLON;
    378     case SYM_DOUBLE_QUOTE:	return T_DOUBLE_QUOTE;
    379     case SYM_LEFT_BRACE:	return T_LEFT_BRACE;
    380     case SYM_RIGHT_BRACE:	return T_RIGHT_BRACE;
    381     case SYM_SPACE:		return T_SPACE;
    382     case SYM_TAB:		return T_TAB;
    383     case SYM_BACKSLASH:
    384 	switch (str[1]) {
    385 	case 'x': return T_NUMERIC_HEX;
    386 	case 'd': return T_NUMERIC_DEC;
    387 	case 'o': return T_NUMERIC_OCT;
    388 	}
    389 	return T_BACKSLASH;
    390     default:
    391 	return T_DEFAULT;
    392     }
    393 }
    394 
    395 static int
    396 get_word(
    397     const char *str,
    398     char *word)
    399 {
    400     const char *p = str;
    401     char *w = word;
    402     Token token;
    403     int token_len;
    404 
    405     while (*p != '\0') {
    406 	token = get_token(p);
    407 	token_len = token_tbl[token].len;
    408 	if (token == T_BACKSLASH) {
    409 	    p += token_len;
    410 	    if (*p == '\0')
    411 		break;
    412 	    token = get_token(p);
    413 	    token_len = token_tbl[token].len;
    414 	} else if (token != T_COMMENT && token != T_DEFAULT) {
    415 	    break;
    416 	}
    417 	strncpy(w, p, (size_t) token_len);
    418 	p += token_len; w += token_len;
    419     }
    420     *w = '\0';
    421     return p - str;	/* return number of scanned chars */
    422 }
    423 
    424 static int
    425 get_quoted_word(
    426     const char *str,
    427     char *word)
    428 {
    429     const char *p = str;
    430     char *w = word;
    431     Token token;
    432     int token_len;
    433 
    434     if (*p == SYM_DOUBLE_QUOTE) {
    435 	++p;
    436     }
    437     while (*p != '\0') {
    438 	token = get_token(p);
    439 	token_len = token_tbl[token].len;
    440 	if (token == T_DOUBLE_QUOTE) {
    441 	    p += token_len;
    442 	    goto found;
    443 	}
    444 	if (token == T_BACKSLASH) {
    445 	    p += token_len;
    446 	    if (*p == '\0') {
    447 		break;
    448 	    }
    449 	    token = get_token(p);
    450 	    token_len = token_tbl[token].len;
    451 	}
    452 	strncpy(w, p, (size_t) token_len);
    453 	p += token_len; w += token_len;
    454     }
    455     /* error. cannot detect next double quote */
    456     return 0;
    457 
    458  found:;
    459     *w = '\0';
    460     return p - str;
    461 }
    462 
    463 /************************************************************************/
    464 
    465 static int
    466 append_value_list (void)
    467 {
    468     char **value_list = parse_info.value;
    469     char *value;
    470     int value_num = parse_info.value_num;
    471     int value_len = parse_info.value_len;
    472     char *str = parse_info.buf;
    473     int len = parse_info.bufsize;
    474     char *p;
    475 
    476     if (len < 1) {
    477 	return 1; /* return with no error */
    478     }
    479 
    480     if (value_list == (char **)NULL) {
    481 	value_list = Xmalloc(sizeof(char *) * 2);
    482 	*value_list = NULL;
    483     } else {
    484 	char **prev_list = value_list;
    485 
    486 	value_list = (char **)
    487 	    Xreallocarray(value_list, value_num + 2, sizeof(char *));
    488 	if (value_list == NULL) {
    489 	    Xfree(prev_list);
    490 	}
    491     }
    492     if (value_list == (char **)NULL)
    493 	goto err2;
    494 
    495     value = *value_list;
    496     if (value == NULL) {
    497 	value = Xmalloc(value_len + len + 1);
    498     } else {
    499 	char *prev_value = value;
    500 
    501 	value = Xrealloc(value, value_len + len + 1);
    502 	if (value == NULL) {
    503 	    Xfree(prev_value);
    504 	}
    505     }
    506     if (value == NULL) {
    507 	goto err1;
    508     }
    509     if (value != *value_list) {
    510 	int i;
    511 	char *old_list;
    512 	old_list = *value_list;
    513 	*value_list = value;
    514 	/* Re-derive pointers from the new realloc() result to avoid undefined
    515 	   behaviour (and crashes on architectures with pointer bounds). */
    516 	for (i = 1; i < value_num; ++i) {
    517 	    value_list[i] = value + (value_list[i] - old_list);
    518 	}
    519     }
    520 
    521     value_list[value_num] = p = &value[value_len];
    522     value_list[value_num + 1] = NULL;
    523     strncpy(p, str, (size_t) len);
    524     p[len] = 0;
    525 
    526     parse_info.value = value_list;
    527     parse_info.value_num = value_num + 1;
    528     parse_info.value_len = value_len + len + 1;
    529     parse_info.bufsize = 0;
    530     return 1;
    531 
    532  err1:
    533     if (value_list) {
    534 	Xfree((char **)value_list);
    535     }
    536     if (value) {
    537 	Xfree(value);
    538     }
    539  err2:
    540     parse_info.value = (char **)NULL;
    541     parse_info.value_num = 0;
    542     parse_info.value_len = 0;
    543     parse_info.bufsize = 0;
    544     return 0;
    545 }
    546 
    547 static int
    548 construct_name(
    549     char *name,
    550     int size)
    551 {
    552     int i;
    553     int len = 0;
    554     char *p = name;
    555 
    556     for (i = 0; i <= parse_info.nest_depth; ++i) {
    557 	len = (int) ((size_t) len + (strlen(parse_info.name[i]) + 1));
    558     }
    559     if (len >= size)
    560 	return 0;
    561 
    562     strcpy(p, parse_info.name[0]);
    563     p += strlen(parse_info.name[0]);
    564     for (i = 1; i <= parse_info.nest_depth; ++i) {
    565 	*p++ = '.';
    566 	strcpy(p, parse_info.name[i]);
    567 	p += strlen(parse_info.name[i]);
    568     }
    569     return *name != '\0';
    570 }
    571 
    572 static int
    573 store_to_database(
    574     Database *db)
    575 {
    576     Database new = (Database)NULL;
    577     char name[BUFSIZE];
    578 
    579     if (parse_info.pre_state == S_VALUE) {
    580 	if (! append_value_list()) {
    581 	    goto err;
    582 	}
    583     }
    584 
    585     if (parse_info.name[parse_info.nest_depth] == NULL) {
    586 	goto err;
    587     }
    588 
    589     new = Xcalloc(1, sizeof(DatabaseRec));
    590     if (new == (Database)NULL) {
    591 	goto err;
    592     }
    593 
    594     new->category = strdup(parse_info.category);
    595     if (new->category == NULL) {
    596 	goto err;
    597     }
    598 
    599     if (! construct_name(name, sizeof(name))) {
    600 	goto err;
    601     }
    602     new->name = strdup(name);
    603     if (new->name == NULL) {
    604 	goto err;
    605     }
    606     new->next = *db;
    607     new->value = parse_info.value;
    608     new->value_num = parse_info.value_num;
    609     *db = new;
    610 
    611     Xfree(parse_info.name[parse_info.nest_depth]);
    612     parse_info.name[parse_info.nest_depth] = NULL;
    613 
    614     parse_info.value = (char **)NULL;
    615     parse_info.value_num = 0;
    616     parse_info.value_len = 0;
    617 
    618     return 1;
    619 
    620  err:
    621     if (new) {
    622 	if (new->category) {
    623 	    Xfree(new->category);
    624 	}
    625 	if (new->name) {
    626 	    Xfree(new->name);
    627 	}
    628 	Xfree(new);
    629     }
    630     if (parse_info.value) {
    631 	if (*parse_info.value) {
    632 	    Xfree(*parse_info.value);
    633 	}
    634 	Xfree((char **)parse_info.value);
    635 	parse_info.value = (char **)NULL;
    636 	parse_info.value_num = 0;
    637 	parse_info.value_len = 0;
    638     }
    639     return 0;
    640 }
    641 
    642 #define END_MARK	"END"
    643 #define	END_MARK_LEN	3 /*strlen(END_MARK)*/
    644 
    645 static int
    646 check_category_end(
    647     const char *str)
    648 {
    649     const char *p;
    650     int len;
    651 
    652     p = str;
    653     if (strncmp(p, END_MARK, END_MARK_LEN)) {
    654 	return 0;
    655     }
    656     p += END_MARK_LEN;
    657 
    658     while (iswhite(*p)) {
    659 	++p;
    660     }
    661     len = (int) strlen(parse_info.category);
    662     if (strncmp(p, parse_info.category, (size_t) len)) {
    663 	return 0;
    664     }
    665     p += len;
    666     return p - str;
    667 }
    668 
    669 /************************************************************************/
    670 
    671 static int
    672 f_newline(
    673     const char *str,
    674     Token token,
    675     Database *db)
    676 {
    677     switch (parse_info.pre_state) {
    678     case S_NULL:
    679     case S_CATEGORY:
    680 	break;
    681     case S_NAME:
    682 	return 0; /* no value */
    683     case S_VALUE:
    684 	if (!store_to_database(db))
    685 	    return 0;
    686 	parse_info.pre_state = S_CATEGORY;
    687 	break;
    688     default:
    689 	return 0;
    690     }
    691     return token_tbl[token].len;
    692 }
    693 
    694 static int
    695 f_comment(
    696     const char *str,
    697     Token token,
    698     Database *db)
    699 {
    700     /* NOTE: comment is already handled in read_line(),
    701        so this function is not necessary. */
    702 
    703     const char *p = str;
    704 
    705     while (*p != SYM_NEWLINE && *p != SYM_CR && *p != '\0') {
    706 	++p;	/* zap to the end of line */
    707     }
    708     return p - str;
    709 }
    710 
    711 static int
    712 f_white(
    713     const char *str,
    714     Token token,
    715     Database *db)
    716 {
    717     const char *p = str;
    718 
    719     while (iswhite(*p)) {
    720 	++p;
    721     }
    722     return p - str;
    723 }
    724 
    725 static int
    726 f_semicolon(
    727     const char *str,
    728     Token token,
    729     Database *db)
    730 {
    731     switch (parse_info.pre_state) {
    732     case S_NULL:
    733     case S_CATEGORY:
    734     case S_NAME:
    735 	return 0;
    736     case S_VALUE:
    737 	if (! append_value_list())
    738 	    return 0;
    739 	parse_info.pre_state = S_VALUE;
    740 	break;
    741     default:
    742 	return 0;
    743     }
    744     return token_tbl[token].len;
    745 }
    746 
    747 static int
    748 f_left_brace(
    749     const char *str,
    750     Token token,
    751     Database *db)
    752 {
    753     switch (parse_info.pre_state) {
    754     case S_NULL:
    755     case S_CATEGORY:
    756     case S_VALUE:
    757 	return 0;
    758     case S_NAME:
    759 	if (parse_info.name[parse_info.nest_depth] == NULL
    760 	    || parse_info.nest_depth + 1 > MAX_NAME_NEST)
    761 	    return 0;
    762 	++parse_info.nest_depth;
    763 	parse_info.pre_state = S_CATEGORY;
    764 	break;
    765     default:
    766 	return 0;
    767     }
    768     return token_tbl[token].len;
    769 }
    770 
    771 static int
    772 f_right_brace(
    773     const char *str,
    774     Token token,
    775     Database *db)
    776 {
    777     if (parse_info.nest_depth < 1)
    778 	return 0;
    779 
    780     switch (parse_info.pre_state) {
    781     case S_NULL:
    782     case S_NAME:
    783 	return 0;
    784     case S_VALUE:
    785 	if (! store_to_database(db))
    786 	    return 0;
    787 	/* fall through - to next case */
    788     case S_CATEGORY:
    789 	if (parse_info.name[parse_info.nest_depth] != NULL) {
    790 	    Xfree(parse_info.name[parse_info.nest_depth]);
    791 	    parse_info.name[parse_info.nest_depth] = NULL;
    792 	}
    793 	--parse_info.nest_depth;
    794 	parse_info.pre_state = S_CATEGORY;
    795 	break;
    796     default:
    797 	return 0;
    798     }
    799     return token_tbl[token].len;
    800 }
    801 
    802 static int
    803 f_double_quote(
    804     const char *str,
    805     Token token,
    806     Database *db)
    807 {
    808     char word[BUFSIZE];
    809     char* wordp;
    810     int len;
    811 
    812     if ((len = (int) strlen (str)) < sizeof word)
    813 	wordp = word;
    814     else
    815 	wordp = Xmalloc (len + 1);
    816     if (wordp == NULL)
    817 	return 0;
    818 
    819     len = 0;
    820     switch (parse_info.pre_state) {
    821     case S_NULL:
    822     case S_CATEGORY:
    823 	goto err;
    824     case S_NAME:
    825     case S_VALUE:
    826 	len = get_quoted_word(str, wordp);
    827 	if (len < 1)
    828 	    goto err;
    829 	if ((parse_info.bufsize + (int)strlen(wordp) + 1)
    830 					>= parse_info.bufMaxSize) {
    831 	    if (realloc_parse_info((int) strlen(wordp)+1) == False) {
    832 		goto err;
    833 	    }
    834 	}
    835 	strcpy(&parse_info.buf[parse_info.bufsize], wordp);
    836 	parse_info.bufsize = (int) ((size_t) parse_info.bufsize + strlen(wordp));
    837 	parse_info.pre_state = S_VALUE;
    838 	break;
    839     default:
    840 	goto err;
    841     }
    842     if (wordp != word)
    843 	Xfree (wordp);
    844     return len;	/* including length of token */
    845 
    846 err:
    847     if (wordp != word)
    848 	Xfree (wordp);
    849     return 0;
    850 }
    851 
    852 static int
    853 f_backslash(
    854     const char *str,
    855     Token token,
    856     Database *db)
    857 {
    858     return f_default(str, token, db);
    859 }
    860 
    861 static int
    862 f_numeric(
    863     const char *str,
    864     Token token,
    865     Database *db)
    866 {
    867     char word[BUFSIZE];
    868     const char *p;
    869     char* wordp;
    870     int len;
    871     int token_len;
    872 
    873     if ((len = (int) strlen (str)) < sizeof word)
    874 	wordp = word;
    875     else
    876 	wordp = Xmalloc (len + 1);
    877     if (wordp == NULL)
    878 	return 0;
    879 
    880     switch (parse_info.pre_state) {
    881     case S_NULL:
    882     case S_CATEGORY:
    883 	goto err;
    884     case S_NAME:
    885     case S_VALUE:
    886 	token_len = token_tbl[token].len;
    887 	p = str + token_len;
    888 	len = get_word(p, wordp);
    889 	if (len < 1)
    890 	    goto err;
    891 	if ((parse_info.bufsize + token_len + (int)strlen(wordp) + 1)
    892 					>= parse_info.bufMaxSize) {
    893 	    if (realloc_parse_info((int)((size_t) token_len + strlen(wordp) + 1)) == False)
    894 		goto err;
    895 	}
    896 	strncpy(&parse_info.buf[parse_info.bufsize], str, (size_t) token_len);
    897 	strcpy(&parse_info.buf[parse_info.bufsize + token_len], wordp);
    898 	parse_info.bufsize = (int) ((size_t) parse_info.bufsize + ((size_t) token_len + strlen(wordp)));
    899 	parse_info.pre_state = S_VALUE;
    900 	break;
    901     default:
    902 	goto err;
    903     }
    904     if (wordp != word)
    905 	Xfree (wordp);
    906     return len + token_len;
    907 
    908 err:
    909     if (wordp != word)
    910 	Xfree (wordp);
    911     return 0;
    912 }
    913 
    914 static int
    915 f_default(
    916     const char *str,
    917     Token token,
    918     Database *db)
    919 {
    920     char word[BUFSIZE], *p;
    921     char* wordp;
    922     int len;
    923 
    924     if ((len = (int) strlen (str)) < sizeof word)
    925 	wordp = word;
    926     else
    927 	wordp = Xmalloc (len + 1);
    928     if (wordp == NULL)
    929 	return 0;
    930 
    931     len = get_word(str, wordp);
    932     if (len < 1)
    933 	goto err;
    934 
    935     switch (parse_info.pre_state) {
    936     case S_NULL:
    937 	if (parse_info.category != NULL)
    938 	    goto err;
    939 	p = strdup(wordp);
    940 	if (p == NULL)
    941 	    goto err;
    942 	parse_info.category = p;
    943 	parse_info.pre_state = S_CATEGORY;
    944 	break;
    945     case S_CATEGORY:
    946 	if (parse_info.nest_depth == 0) {
    947 	    if (check_category_end(str)) {
    948 		/* end of category is detected.
    949 		   clear context and zap to end of this line */
    950 		clear_parse_info();
    951 		len = (int) strlen(str);
    952 		break;
    953 	    }
    954 	}
    955 	p = strdup(wordp);
    956 	if (p == NULL)
    957 	    goto err;
    958 	if (parse_info.name[parse_info.nest_depth] != NULL) {
    959 	    Xfree(parse_info.name[parse_info.nest_depth]);
    960 	}
    961 	parse_info.name[parse_info.nest_depth] = p;
    962 	parse_info.pre_state = S_NAME;
    963 	break;
    964     case S_NAME:
    965     case S_VALUE:
    966 	if ((parse_info.bufsize + (int)strlen(wordp) + 1)
    967 					>= parse_info.bufMaxSize) {
    968 	    if (realloc_parse_info((int) strlen(wordp) + 1) == False)
    969 		goto err;
    970 	}
    971 	strcpy(&parse_info.buf[parse_info.bufsize], wordp);
    972 	parse_info.bufsize = (int) ((size_t) parse_info.bufsize + strlen(wordp));
    973 	parse_info.pre_state = S_VALUE;
    974 	break;
    975     default:
    976 	goto err;
    977     }
    978     if (wordp != word)
    979 	Xfree (wordp);
    980     return len;
    981 
    982 err:
    983     if (wordp != word)
    984 	Xfree (wordp);
    985     return 0;
    986 }
    987 
    988 /************************************************************************/
    989 
    990 #ifdef DEBUG
    991 static void
    992 PrintDatabase(
    993     Database db)
    994 {
    995     Database p = db;
    996     int i = 0, j;
    997 
    998     printf("***\n*** BEGIN Database\n***\n");
    999     while (p) {
   1000 	printf("%3d: ", i++);
   1001 	printf("%s, %s, ", p->category, p->name);
   1002 	printf("\t[%d: ", p->value_num);
   1003 	for (j = 0; j < p->value_num; ++j) {
   1004 	    printf("%s, ", p->value[j]);
   1005 	}
   1006 	printf("]\n");
   1007 	p = p->next;
   1008     }
   1009     printf("***\n*** END   Database\n***\n");
   1010 }
   1011 #endif
   1012 
   1013 static void
   1014 DestroyDatabase(
   1015     Database db)
   1016 {
   1017     Database p = db;
   1018 
   1019     while (p) {
   1020 	if (p->category != NULL) {
   1021 	    Xfree(p->category);
   1022 	}
   1023 	if (p->name != NULL) {
   1024 	    Xfree(p->name);
   1025 	}
   1026 	if (p->value != (char **)NULL) {
   1027 	    if (*p->value != NULL) {
   1028 		Xfree(*p->value);
   1029 	    }
   1030 	    Xfree(p->value);
   1031 	}
   1032 	db = p->next;
   1033 	Xfree(p);
   1034 	p = db;
   1035     }
   1036 }
   1037 
   1038 static int
   1039 CountDatabase(
   1040     Database db)
   1041 {
   1042     Database p = db;
   1043     int cnt = 0;
   1044 
   1045     while (p) {
   1046 	++cnt;
   1047 	p = p->next;
   1048     }
   1049     return cnt;
   1050 }
   1051 
   1052 static Database
   1053 CreateDatabase(
   1054     char *dbfile)
   1055 {
   1056     Database db = (Database)NULL;
   1057     FILE *fd;
   1058     Line line;
   1059     char *p;
   1060     Token token;
   1061     int len;
   1062     int error = 0;
   1063 
   1064     fd = _XFopenFile(dbfile, "r");
   1065     if (fd == (FILE *)NULL)
   1066 	return NULL;
   1067 
   1068     bzero(&line, sizeof(Line));
   1069     init_parse_info();
   1070 
   1071     do {
   1072 	int rc = read_line(fd, &line);
   1073 	if (rc < 0) {
   1074 	    error = 1;
   1075 	    break;
   1076 	} else if (rc == 0) {
   1077 	    break;
   1078 	}
   1079 	p = line.str;
   1080 	while (*p) {
   1081             int (*parse_proc)(const char *str, Token token, Database *db) = NULL;
   1082 
   1083 	    token = get_token(p);
   1084 
   1085             switch (token_tbl[token].token) {
   1086             case T_NEWLINE:
   1087                 parse_proc = f_newline;
   1088                 break;
   1089             case T_COMMENT:
   1090                 parse_proc = f_comment;
   1091                 break;
   1092             case T_SEMICOLON:
   1093                 parse_proc = f_semicolon;
   1094                 break;
   1095             case T_DOUBLE_QUOTE:
   1096                 parse_proc = f_double_quote;
   1097                 break;
   1098             case T_LEFT_BRACE:
   1099                 parse_proc = f_left_brace;
   1100                 break;
   1101             case T_RIGHT_BRACE:
   1102                 parse_proc = f_right_brace;
   1103                 break;
   1104             case T_SPACE:
   1105             case T_TAB:
   1106                 parse_proc = f_white;
   1107                 break;
   1108             case T_BACKSLASH:
   1109                 parse_proc = f_backslash;
   1110                 break;
   1111             case T_NUMERIC_HEX:
   1112             case T_NUMERIC_DEC:
   1113             case T_NUMERIC_OCT:
   1114                 parse_proc = f_numeric;
   1115                 break;
   1116             case T_DEFAULT:
   1117                 parse_proc = f_default;
   1118                 break;
   1119             }
   1120 
   1121             len = parse_proc(p, token, &db);
   1122 
   1123 	    if (len < 1) {
   1124 		error = 1;
   1125 		break;
   1126 	    }
   1127 	    p += len;
   1128 	}
   1129     } while (!error);
   1130 
   1131     if (parse_info.pre_state != S_NULL) {
   1132 	clear_parse_info();
   1133 	error = 1;
   1134     }
   1135     if (error) {
   1136 #ifdef	DEBUG
   1137 	fprintf(stderr, "database format error at line %d.\n", line.seq);
   1138 #endif
   1139 	DestroyDatabase(db);
   1140 	db = (Database)NULL;
   1141     }
   1142 
   1143     fclose(fd);
   1144     free_line(&line);
   1145 
   1146 #ifdef	DEBUG
   1147     PrintDatabase(db);
   1148 #endif
   1149 
   1150     return db;
   1151 }
   1152 
   1153 /************************************************************************/
   1154 
   1155 #ifndef	NOT_X_ENV
   1156 
   1157 /* locale framework functions */
   1158 
   1159 typedef struct _XlcDatabaseRec {
   1160     XrmQuark category_q;
   1161     XrmQuark name_q;
   1162     Database db;
   1163     struct _XlcDatabaseRec *next;
   1164 } XlcDatabaseRec, *XlcDatabase;
   1165 
   1166 typedef	struct _XlcDatabaseListRec {
   1167     XrmQuark name_q;
   1168     XlcDatabase lc_db;
   1169     Database database;
   1170     int ref_count;
   1171     struct _XlcDatabaseListRec *next;
   1172 } XlcDatabaseListRec, *XlcDatabaseList;
   1173 
   1174 /* database cache list (per file) */
   1175 static XlcDatabaseList _db_list = (XlcDatabaseList)NULL;
   1176 
   1177 /************************************************************************/
   1178 /*	_XlcGetResource(lcd, category, class, value, count)		*/
   1179 /*----------------------------------------------------------------------*/
   1180 /*	This function retrieves XLocale database information.		*/
   1181 /************************************************************************/
   1182 void
   1183 _XlcGetResource(
   1184     XLCd lcd,
   1185     const char *category,
   1186     const char *class,
   1187     char ***value,
   1188     int *count)
   1189 {
   1190     XLCdPublicMethodsPart *methods = XLC_PUBLIC_METHODS(lcd);
   1191 
   1192     (*methods->get_resource)(lcd, category, class, value, count);
   1193     return;
   1194 }
   1195 
   1196 /************************************************************************/
   1197 /*	_XlcGetLocaleDataBase(lcd, category, class, value, count)	*/
   1198 /*----------------------------------------------------------------------*/
   1199 /*	This function retrieves XLocale database information.		*/
   1200 /************************************************************************/
   1201 void
   1202 _XlcGetLocaleDataBase(
   1203     XLCd lcd,
   1204     const char *category,
   1205     const char *name,
   1206     char ***value,
   1207     int *count)
   1208 {
   1209     XlcDatabase lc_db = (XlcDatabase)XLC_PUBLIC(lcd, xlocale_db);
   1210     XrmQuark category_q, name_q;
   1211 
   1212     category_q = XrmStringToQuark(category);
   1213     name_q = XrmStringToQuark(name);
   1214     for (; lc_db->db; ++lc_db) {
   1215 	if (category_q == lc_db->category_q && name_q == lc_db->name_q) {
   1216 	    *value = lc_db->db->value;
   1217 	    *count = lc_db->db->value_num;
   1218 	    return;
   1219 	}
   1220     }
   1221     *value = (char **)NULL;
   1222     *count = 0;
   1223 }
   1224 
   1225 /************************************************************************/
   1226 /*	_XlcDestroyLocaleDataBase(lcd)					*/
   1227 /*----------------------------------------------------------------------*/
   1228 /*	This function destroy the XLocale Database that bound to the 	*/
   1229 /*	specified lcd.  If the XLocale Database is referred from some 	*/
   1230 /*	other lcd, this function just decreases reference count of 	*/
   1231 /*	the database.  If no locale refers the database, this function	*/
   1232 /*	remove it from the cache list and free work area.		*/
   1233 /************************************************************************/
   1234 void
   1235 _XlcDestroyLocaleDataBase(
   1236     XLCd lcd)
   1237 {
   1238     XlcDatabase lc_db = (XlcDatabase)XLC_PUBLIC(lcd, xlocale_db);
   1239     XlcDatabaseList p, prev;
   1240 
   1241     for (p = _db_list, prev = (XlcDatabaseList)NULL; p;
   1242 	 prev = p, p = p->next) {
   1243 	if (p->lc_db == lc_db) {
   1244 	    if ((-- p->ref_count) < 1) {
   1245 		if (p->lc_db != (XlcDatabase)NULL) {
   1246 		    Xfree(p->lc_db);
   1247 		}
   1248 		DestroyDatabase(p->database);
   1249 		if (prev == (XlcDatabaseList)NULL) {
   1250 		    _db_list = p->next;
   1251 		} else {
   1252 		    prev->next = p->next;
   1253 		}
   1254 		Xfree((char*)p);
   1255 	    }
   1256 	    break;
   1257 	}
   1258     }
   1259     XLC_PUBLIC(lcd, xlocale_db) = (XPointer)NULL;
   1260 }
   1261 
   1262 /************************************************************************/
   1263 /*	_XlcCreateLocaleDataBase(lcd)					*/
   1264 /*----------------------------------------------------------------------*/
   1265 /*	This function create an XLocale database which correspond to	*/
   1266 /*	the specified XLCd.						*/
   1267 /************************************************************************/
   1268 XPointer
   1269 _XlcCreateLocaleDataBase(
   1270     XLCd lcd)
   1271 {
   1272     XlcDatabaseList list, new;
   1273     Database p, database = (Database)NULL;
   1274     XlcDatabase lc_db = (XlcDatabase)NULL;
   1275     XrmQuark name_q;
   1276     char *name;
   1277     int i, n;
   1278 
   1279     name = _XlcFileName(lcd, "locale");
   1280     if (name == NULL)
   1281 	return (XPointer)NULL;
   1282 
   1283     name_q = XrmStringToQuark(name);
   1284     for (list = _db_list; list; list = list->next) {
   1285 	if (name_q == list->name_q) {
   1286 	    list->ref_count++;
   1287 	    Xfree (name);
   1288 	    return XLC_PUBLIC(lcd, xlocale_db) = (XPointer)list->lc_db;
   1289 	}
   1290     }
   1291 
   1292     database = CreateDatabase(name);
   1293     if (database == (Database)NULL) {
   1294 	Xfree (name);
   1295 	return (XPointer)NULL;
   1296     }
   1297     n = CountDatabase(database);
   1298     lc_db = Xcalloc(n + 1, sizeof(XlcDatabaseRec));
   1299     if (lc_db == (XlcDatabase)NULL)
   1300 	goto err;
   1301     for (p = database, i = 0; p && i < n; p = p->next, ++i) {
   1302 	lc_db[i].category_q = XrmStringToQuark(p->category);
   1303 	lc_db[i].name_q = XrmStringToQuark(p->name);
   1304 	lc_db[i].db = p;
   1305     }
   1306 
   1307     new = Xmalloc(sizeof(XlcDatabaseListRec));
   1308     if (new == (XlcDatabaseList)NULL) {
   1309 	goto err;
   1310     }
   1311     new->name_q = name_q;
   1312     new->lc_db = lc_db;
   1313     new->database = database;
   1314     new->ref_count = 1;
   1315     new->next = _db_list;
   1316     _db_list = new;
   1317 
   1318     Xfree (name);
   1319     return XLC_PUBLIC(lcd, xlocale_db) = (XPointer)lc_db;
   1320 
   1321  err:
   1322     DestroyDatabase(database);
   1323     if (lc_db != (XlcDatabase)NULL) {
   1324 	Xfree(lc_db);
   1325     }
   1326     Xfree (name);
   1327     return (XPointer)NULL;
   1328 }
   1329 
   1330 #endif	/* NOT_X_ENV */
   1331