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