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