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