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