lcDB.c revision 6cc2b21f
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 = Xcalloc(1, sizeof(DatabaseRec));
595    if (new == (Database)NULL) {
596	goto err;
597    }
598
599    new->category = strdup(parse_info.category);
600    if (new->category == NULL) {
601	goto err;
602    }
603
604    if (! construct_name(name, sizeof(name))) {
605	goto err;
606    }
607    new->name = strdup(name);
608    if (new->name == NULL) {
609	goto err;
610    }
611    new->next = *db;
612    new->value = parse_info.value;
613    new->value_num = parse_info.value_num;
614    *db = new;
615
616    Xfree(parse_info.name[parse_info.nest_depth]);
617    parse_info.name[parse_info.nest_depth] = NULL;
618
619    parse_info.value = (char **)NULL;
620    parse_info.value_num = 0;
621    parse_info.value_len = 0;
622
623    return 1;
624
625 err:
626    if (new) {
627	if (new->category) {
628	    Xfree(new->category);
629	}
630	if (new->name) {
631	    Xfree(new->name);
632	}
633	Xfree(new);
634    }
635    if (parse_info.value) {
636	if (*parse_info.value) {
637	    Xfree(*parse_info.value);
638	}
639	Xfree((char **)parse_info.value);
640	parse_info.value = (char **)NULL;
641	parse_info.value_num = 0;
642	parse_info.value_len = 0;
643    }
644    return 0;
645}
646
647#define END_MARK	"END"
648#define	END_MARK_LEN	3 /*strlen(END_MARK)*/
649
650static int
651check_category_end(
652    const char *str)
653{
654    const char *p;
655    int len;
656
657    p = str;
658    if (strncmp(p, END_MARK, END_MARK_LEN)) {
659	return 0;
660    }
661    p += END_MARK_LEN;
662
663    while (iswhite(*p)) {
664	++p;
665    }
666    len = strlen(parse_info.category);
667    if (strncmp(p, parse_info.category, len)) {
668	return 0;
669    }
670    p += len;
671    return p - str;
672}
673
674/************************************************************************/
675
676static int
677f_newline(
678    const char *str,
679    Token token,
680    Database *db)
681{
682    switch (parse_info.pre_state) {
683    case S_NULL:
684    case S_CATEGORY:
685	break;
686    case S_NAME:
687	return 0; /* no value */
688    case S_VALUE:
689	if (!store_to_database(db))
690	    return 0;
691	parse_info.pre_state = S_CATEGORY;
692	break;
693    default:
694	return 0;
695    }
696    return token_tbl[token].len;
697}
698
699static int
700f_comment(
701    const char *str,
702    Token token,
703    Database *db)
704{
705    /* NOTE: comment is already handled in read_line(),
706       so this function is not necessary. */
707
708    const char *p = str;
709
710    while (*p != SYM_NEWLINE && *p != SYM_CR && *p != '\0') {
711	++p;	/* zap to the end of line */
712    }
713    return p - str;
714}
715
716static int
717f_white(
718    const char *str,
719    Token token,
720    Database *db)
721{
722    const char *p = str;
723
724    while (iswhite(*p)) {
725	++p;
726    }
727    return p - str;
728}
729
730static int
731f_semicolon(
732    const char *str,
733    Token token,
734    Database *db)
735{
736    switch (parse_info.pre_state) {
737    case S_NULL:
738    case S_CATEGORY:
739    case S_NAME:
740	return 0;
741    case S_VALUE:
742	if (! append_value_list())
743	    return 0;
744	parse_info.pre_state = S_VALUE;
745	break;
746    default:
747	return 0;
748    }
749    return token_tbl[token].len;
750}
751
752static int
753f_left_brace(
754    const char *str,
755    Token token,
756    Database *db)
757{
758    switch (parse_info.pre_state) {
759    case S_NULL:
760    case S_CATEGORY:
761    case S_VALUE:
762	return 0;
763    case S_NAME:
764	if (parse_info.name[parse_info.nest_depth] == NULL
765	    || parse_info.nest_depth + 1 > MAX_NAME_NEST)
766	    return 0;
767	++parse_info.nest_depth;
768	parse_info.pre_state = S_CATEGORY;
769	break;
770    default:
771	return 0;
772    }
773    return token_tbl[token].len;
774}
775
776static int
777f_right_brace(
778    const char *str,
779    Token token,
780    Database *db)
781{
782    if (parse_info.nest_depth < 1)
783	return 0;
784
785    switch (parse_info.pre_state) {
786    case S_NULL:
787    case S_NAME:
788	return 0;
789    case S_VALUE:
790	if (! store_to_database(db))
791	    return 0;
792	/* fall into next case */
793    case S_CATEGORY:
794	if (parse_info.name[parse_info.nest_depth] != NULL) {
795	    Xfree(parse_info.name[parse_info.nest_depth]);
796	    parse_info.name[parse_info.nest_depth] = NULL;
797	}
798	--parse_info.nest_depth;
799	parse_info.pre_state = S_CATEGORY;
800	break;
801    default:
802	return 0;
803    }
804    return token_tbl[token].len;
805}
806
807static int
808f_double_quote(
809    const char *str,
810    Token token,
811    Database *db)
812{
813    char word[BUFSIZE];
814    char* wordp;
815    int len;
816
817    if ((len = strlen (str)) < sizeof word)
818	wordp = word;
819    else
820	wordp = Xmalloc (len + 1);
821    if (wordp == NULL)
822	return 0;
823
824    len = 0;
825    switch (parse_info.pre_state) {
826    case S_NULL:
827    case S_CATEGORY:
828	goto err;
829    case S_NAME:
830    case S_VALUE:
831	len = get_quoted_word(str, wordp);
832	if (len < 1)
833	    goto err;
834	if ((parse_info.bufsize + (int)strlen(wordp) + 1)
835					>= parse_info.bufMaxSize) {
836	    if (realloc_parse_info(strlen(wordp)+1) == False) {
837		goto err;
838	    }
839	}
840	strcpy(&parse_info.buf[parse_info.bufsize], wordp);
841	parse_info.bufsize += strlen(wordp);
842	parse_info.pre_state = S_VALUE;
843	break;
844    default:
845	goto err;
846    }
847    if (wordp != word)
848	Xfree (wordp);
849    return len;	/* including length of token */
850
851err:
852    if (wordp != word)
853	Xfree (wordp);
854    return 0;
855}
856
857static int
858f_backslash(
859    const char *str,
860    Token token,
861    Database *db)
862{
863    return f_default(str, token, db);
864}
865
866static int
867f_numeric(
868    const char *str,
869    Token token,
870    Database *db)
871{
872    char word[BUFSIZE];
873    const char *p;
874    char* wordp;
875    int len;
876    int token_len;
877
878    if ((len = strlen (str)) < sizeof word)
879	wordp = word;
880    else
881	wordp = Xmalloc (len + 1);
882    if (wordp == NULL)
883	return 0;
884
885    switch (parse_info.pre_state) {
886    case S_NULL:
887    case S_CATEGORY:
888	goto err;
889    case S_NAME:
890    case S_VALUE:
891	token_len = token_tbl[token].len;
892	p = str + token_len;
893	len = get_word(p, wordp);
894	if (len < 1)
895	    goto err;
896	if ((parse_info.bufsize + token_len + (int)strlen(wordp) + 1)
897					>= parse_info.bufMaxSize) {
898	    if (realloc_parse_info(token_len + strlen(wordp) + 1) == False)
899		goto err;
900	}
901	strncpy(&parse_info.buf[parse_info.bufsize], str, token_len);
902	strcpy(&parse_info.buf[parse_info.bufsize + token_len], wordp);
903	parse_info.bufsize += token_len + strlen(wordp);
904	parse_info.pre_state = S_VALUE;
905	break;
906    default:
907	goto err;
908    }
909    if (wordp != word)
910	Xfree (wordp);
911    return len + token_len;
912
913err:
914    if (wordp != word)
915	Xfree (wordp);
916    return 0;
917}
918
919static int
920f_default(
921    const char *str,
922    Token token,
923    Database *db)
924{
925    char word[BUFSIZE], *p;
926    char* wordp;
927    int len;
928
929    if ((len = strlen (str)) < sizeof word)
930	wordp = word;
931    else
932	wordp = Xmalloc (len + 1);
933    if (wordp == NULL)
934	return 0;
935
936    len = get_word(str, wordp);
937    if (len < 1)
938	goto err;
939
940    switch (parse_info.pre_state) {
941    case S_NULL:
942	if (parse_info.category != NULL)
943	    goto err;
944	p = strdup(wordp);
945	if (p == NULL)
946	    goto err;
947	parse_info.category = p;
948	parse_info.pre_state = S_CATEGORY;
949	break;
950    case S_CATEGORY:
951	if (parse_info.nest_depth == 0) {
952	    if (check_category_end(str)) {
953		/* end of category is detected.
954		   clear context and zap to end of this line */
955		clear_parse_info();
956		len = strlen(str);
957		break;
958	    }
959	}
960	p = strdup(wordp);
961	if (p == NULL)
962	    goto err;
963	if (parse_info.name[parse_info.nest_depth] != NULL) {
964	    Xfree(parse_info.name[parse_info.nest_depth]);
965	}
966	parse_info.name[parse_info.nest_depth] = p;
967	parse_info.pre_state = S_NAME;
968	break;
969    case S_NAME:
970    case S_VALUE:
971	if ((parse_info.bufsize + (int)strlen(wordp) + 1)
972					>= parse_info.bufMaxSize) {
973	    if (realloc_parse_info(strlen(wordp) + 1) == False)
974		goto err;
975	}
976	strcpy(&parse_info.buf[parse_info.bufsize], wordp);
977	parse_info.bufsize += strlen(wordp);
978	parse_info.pre_state = S_VALUE;
979	break;
980    default:
981	goto err;
982    }
983    if (wordp != word)
984	Xfree (wordp);
985    return len;
986
987err:
988    if (wordp != word)
989	Xfree (wordp);
990    return 0;
991}
992
993/************************************************************************/
994
995#ifdef DEBUG
996static void
997PrintDatabase(
998    Database db)
999{
1000    Database p = db;
1001    int i = 0, j;
1002
1003    printf("***\n*** BEGIN Database\n***\n");
1004    while (p) {
1005	printf("%3d: ", i++);
1006	printf("%s, %s, ", p->category, p->name);
1007	printf("\t[%d: ", p->value_num);
1008	for (j = 0; j < p->value_num; ++j) {
1009	    printf("%s, ", p->value[j]);
1010	}
1011	printf("]\n");
1012	p = p->next;
1013    }
1014    printf("***\n*** END   Database\n***\n");
1015}
1016#endif
1017
1018static void
1019DestroyDatabase(
1020    Database db)
1021{
1022    Database p = db;
1023
1024    while (p) {
1025	if (p->category != NULL) {
1026	    Xfree(p->category);
1027	}
1028	if (p->name != NULL) {
1029	    Xfree(p->name);
1030	}
1031	if (p->value != (char **)NULL) {
1032	    if (*p->value != NULL) {
1033		Xfree(*p->value);
1034	    }
1035	    Xfree((char *)p->value);
1036	}
1037	db = p->next;
1038	Xfree((char *)p);
1039	p = db;
1040    }
1041}
1042
1043static int
1044CountDatabase(
1045    Database db)
1046{
1047    Database p = db;
1048    int cnt = 0;
1049
1050    while (p) {
1051	++cnt;
1052	p = p->next;
1053    }
1054    return cnt;
1055}
1056
1057static Database
1058CreateDatabase(
1059    char *dbfile)
1060{
1061    Database db = (Database)NULL;
1062    FILE *fd;
1063    Line line;
1064    char *p;
1065    Token token;
1066    int len;
1067    int error = 0;
1068
1069    fd = _XFopenFile(dbfile, "r");
1070    if (fd == (FILE *)NULL)
1071	return NULL;
1072
1073    bzero(&line, sizeof(Line));
1074    init_parse_info();
1075
1076    do {
1077	int rc = read_line(fd, &line);
1078	if (rc < 0) {
1079	    error = 1;
1080	    break;
1081	} else if (rc == 0) {
1082	    break;
1083	}
1084	p = line.str;
1085	while (*p) {
1086            int (*parse_proc)(const char *str, Token token, Database *db) = NULL;
1087
1088	    token = get_token(p);
1089
1090            switch (token_tbl[token].token) {
1091            case T_NEWLINE:
1092                parse_proc = f_newline;
1093                break;
1094            case T_COMMENT:
1095                parse_proc = f_comment;
1096                break;
1097            case T_SEMICOLON:
1098                parse_proc = f_semicolon;
1099                break;
1100            case T_DOUBLE_QUOTE:
1101                parse_proc = f_double_quote;
1102                break;
1103            case T_LEFT_BRACE:
1104                parse_proc = f_left_brace;
1105                break;
1106            case T_RIGHT_BRACE:
1107                parse_proc = f_right_brace;
1108                break;
1109            case T_SPACE:
1110            case T_TAB:
1111                parse_proc = f_white;
1112                break;
1113            case T_BACKSLASH:
1114                parse_proc = f_backslash;
1115                break;
1116            case T_NUMERIC_HEX:
1117            case T_NUMERIC_DEC:
1118            case T_NUMERIC_OCT:
1119                parse_proc = f_numeric;
1120                break;
1121            case T_DEFAULT:
1122                parse_proc = f_default;
1123                break;
1124            }
1125
1126            len = parse_proc(p, token, &db);
1127
1128	    if (len < 1) {
1129		error = 1;
1130		break;
1131	    }
1132	    p += len;
1133	}
1134    } while (!error);
1135
1136    if (parse_info.pre_state != S_NULL) {
1137	clear_parse_info();
1138	error = 1;
1139    }
1140    if (error) {
1141#ifdef	DEBUG
1142	fprintf(stderr, "database format error at line %d.\n", line.seq);
1143#endif
1144	DestroyDatabase(db);
1145	db = (Database)NULL;
1146    }
1147
1148    fclose(fd);
1149    free_line(&line);
1150
1151#ifdef	DEBUG
1152    PrintDatabase(db);
1153#endif
1154
1155    return db;
1156}
1157
1158/************************************************************************/
1159
1160#ifndef	NOT_X_ENV
1161
1162/* locale framework functions */
1163
1164typedef struct _XlcDatabaseRec {
1165    XrmQuark category_q;
1166    XrmQuark name_q;
1167    Database db;
1168    struct _XlcDatabaseRec *next;
1169} XlcDatabaseRec, *XlcDatabase;
1170
1171typedef	struct _XlcDatabaseListRec {
1172    XrmQuark name_q;
1173    XlcDatabase lc_db;
1174    Database database;
1175    int ref_count;
1176    struct _XlcDatabaseListRec *next;
1177} XlcDatabaseListRec, *XlcDatabaseList;
1178
1179/* database cache list (per file) */
1180static XlcDatabaseList _db_list = (XlcDatabaseList)NULL;
1181
1182/************************************************************************/
1183/*	_XlcGetResource(lcd, category, class, value, count)		*/
1184/*----------------------------------------------------------------------*/
1185/*	This function retrieves XLocale database information.		*/
1186/************************************************************************/
1187void
1188_XlcGetResource(
1189    XLCd lcd,
1190    const char *category,
1191    const char *class,
1192    char ***value,
1193    int *count)
1194{
1195    XLCdPublicMethodsPart *methods = XLC_PUBLIC_METHODS(lcd);
1196
1197    (*methods->get_resource)(lcd, category, class, value, count);
1198    return;
1199}
1200
1201/************************************************************************/
1202/*	_XlcGetLocaleDataBase(lcd, category, class, value, count)	*/
1203/*----------------------------------------------------------------------*/
1204/*	This function retrieves XLocale database information.		*/
1205/************************************************************************/
1206void
1207_XlcGetLocaleDataBase(
1208    XLCd lcd,
1209    const char *category,
1210    const char *name,
1211    char ***value,
1212    int *count)
1213{
1214    XlcDatabase lc_db = (XlcDatabase)XLC_PUBLIC(lcd, xlocale_db);
1215    XrmQuark category_q, name_q;
1216
1217    category_q = XrmStringToQuark(category);
1218    name_q = XrmStringToQuark(name);
1219    for (; lc_db->db; ++lc_db) {
1220	if (category_q == lc_db->category_q && name_q == lc_db->name_q) {
1221	    *value = lc_db->db->value;
1222	    *count = lc_db->db->value_num;
1223	    return;
1224	}
1225    }
1226    *value = (char **)NULL;
1227    *count = 0;
1228}
1229
1230/************************************************************************/
1231/*	_XlcDestroyLocaleDataBase(lcd)					*/
1232/*----------------------------------------------------------------------*/
1233/*	This function destroy the XLocale Database that bound to the 	*/
1234/*	specified lcd.  If the XLocale Database is refered from some 	*/
1235/*	other lcd, this function just decreases reference count of 	*/
1236/*	the database.  If no locale refers the database, this function	*/
1237/*	remove it from the cache list and free work area.		*/
1238/************************************************************************/
1239void
1240_XlcDestroyLocaleDataBase(
1241    XLCd lcd)
1242{
1243    XlcDatabase lc_db = (XlcDatabase)XLC_PUBLIC(lcd, xlocale_db);
1244    XlcDatabaseList p, prev;
1245
1246    for (p = _db_list, prev = (XlcDatabaseList)NULL; p;
1247	 prev = p, p = p->next) {
1248	if (p->lc_db == lc_db) {
1249	    if ((-- p->ref_count) < 1) {
1250		if (p->lc_db != (XlcDatabase)NULL) {
1251		    Xfree((char *)p->lc_db);
1252		}
1253		DestroyDatabase(p->database);
1254		if (prev == (XlcDatabaseList)NULL) {
1255		    _db_list = p->next;
1256		} else {
1257		    prev->next = p->next;
1258		}
1259		Xfree((char*)p);
1260	    }
1261	    break;
1262	}
1263    }
1264    XLC_PUBLIC(lcd, xlocale_db) = (XPointer)NULL;
1265}
1266
1267/************************************************************************/
1268/*	_XlcCreateLocaleDataBase(lcd)					*/
1269/*----------------------------------------------------------------------*/
1270/*	This function create an XLocale database which correspond to	*/
1271/*	the specified XLCd.						*/
1272/************************************************************************/
1273XPointer
1274_XlcCreateLocaleDataBase(
1275    XLCd lcd)
1276{
1277    XlcDatabaseList list, new;
1278    Database p, database = (Database)NULL;
1279    XlcDatabase lc_db = (XlcDatabase)NULL;
1280    XrmQuark name_q;
1281    char *name;
1282    int i, n;
1283
1284    name = _XlcFileName(lcd, "locale");
1285    if (name == NULL)
1286	return (XPointer)NULL;
1287
1288#ifndef __UNIXOS2__
1289    name_q = XrmStringToQuark(name);
1290#else
1291    name_q = XrmStringToQuark((char*)__XOS2RedirRoot(name));
1292#endif
1293    for (list = _db_list; list; list = list->next) {
1294	if (name_q == list->name_q) {
1295	    list->ref_count++;
1296	    Xfree (name);
1297	    return XLC_PUBLIC(lcd, xlocale_db) = (XPointer)list->lc_db;
1298	}
1299    }
1300
1301    database = CreateDatabase(name);
1302    if (database == (Database)NULL) {
1303	Xfree (name);
1304	return (XPointer)NULL;
1305    }
1306    n = CountDatabase(database);
1307    lc_db = Xcalloc(n + 1, sizeof(XlcDatabaseRec));
1308    if (lc_db == (XlcDatabase)NULL)
1309	goto err;
1310    for (p = database, i = 0; p && i < n; p = p->next, ++i) {
1311	lc_db[i].category_q = XrmStringToQuark(p->category);
1312	lc_db[i].name_q = XrmStringToQuark(p->name);
1313	lc_db[i].db = p;
1314    }
1315
1316    new = (XlcDatabaseList)Xmalloc(sizeof(XlcDatabaseListRec));
1317    if (new == (XlcDatabaseList)NULL) {
1318	goto err;
1319    }
1320    new->name_q = name_q;
1321    new->lc_db = lc_db;
1322    new->database = database;
1323    new->ref_count = 1;
1324    new->next = _db_list;
1325    _db_list = new;
1326
1327    Xfree (name);
1328    return XLC_PUBLIC(lcd, xlocale_db) = (XPointer)lc_db;
1329
1330 err:
1331    DestroyDatabase(database);
1332    if (lc_db != (XlcDatabase)NULL) {
1333	Xfree((char *)lc_db);
1334    }
1335    Xfree (name);
1336    return (XPointer)NULL;
1337}
1338
1339#endif	/* NOT_X_ENV */
1340