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