lcDB.c revision 1ab64890
1/* $Xorg: lcDB.c,v 1.6 2000/08/17 19:45:17 cpqbld Exp $ */
2/*
3 *
4 * Copyright IBM Corporation 1993
5 *
6 * All Rights Reserved
7 *
8 * License to use, copy, modify, and distribute this software and its
9 * documentation for any purpose and without fee is hereby granted,
10 * provided that the above copyright notice appear in all copies and that
11 * both that copyright notice and this permission notice appear in
12 * supporting documentation, and that the name of IBM not be
13 * used in advertising or publicity pertaining to distribution of the
14 * software without specific, written prior permission.
15 *
16 * IBM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
17 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS, AND
18 * NONINFRINGEMENT OF THIRD PARTY RIGHTS, IN NO EVENT SHALL
19 * IBM BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
20 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
21 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
22 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
23 * SOFTWARE.
24 *
25*/
26/*
27 *  (c) Copyright 1995 FUJITSU LIMITED
28 *  This is source code modified by FUJITSU LIMITED under the Joint
29 *  Development Agreement for the CDE/Motif PST.
30 */
31/* $XFree86: xc/lib/X11/lcDB.c,v 3.15tsi Exp $ */
32
33
34
35#ifndef	NOT_X_ENV
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40#include <X11/Xlib.h>
41#include <X11/Xresource.h>
42#include "Xlibint.h"
43#include "XlcPubI.h"
44
45#else	/* NOT_X_ENV */
46
47#define	Xmalloc	malloc
48#define	Xrealloc	realloc
49#define	Xfree	free
50
51#endif	/* NOT_X_ENV */
52
53/* specifying NOT_X_ENV allows users to just use
54   the database parsing routine. */
55/* For UDC/VW */
56#ifndef	BUFSIZE
57#define	BUFSIZE	2048
58#endif
59
60#ifdef COMMENT
61#ifdef  BUFSIZE
62#undef BUFSIZE
63#endif
64#define BUFSIZE 6144 /* 2048*3 */
65#endif
66
67#include <stdio.h>
68
69typedef struct _DatabaseRec {
70    char *category;
71    char *name;
72    char **value;
73    int value_num;
74    struct _DatabaseRec *next;
75} DatabaseRec, *Database;
76
77typedef enum {
78    S_NULL,	/* outside category */
79    S_CATEGORY,	/* inside category */
80    S_NAME,	/* has name, expecting values */
81    S_VALUE
82} ParseState;
83
84typedef enum {
85    T_NEWLINE,
86    T_COMMENT,
87    T_SEMICOLON,
88    T_DOUBLE_QUOTE,
89    T_LEFT_BRACE,
90    T_RIGHT_BRACE,
91    T_SPACE,
92    T_TAB,
93    T_BACKSLASH,
94    T_NUMERIC_HEX,
95    T_NUMERIC_DEC,
96    T_NUMERIC_OCT,
97    T_DEFAULT
98} Token;
99
100typedef struct {
101    Token token;	/* token id */
102    int len;		/* length of token sequence */
103} TokenTable;
104
105static int f_newline (const char *str, Token token, Database *db);
106static int f_comment (const char *str, Token token, Database *db);
107static int f_semicolon (const char *str, Token token, Database *db);
108static int f_double_quote (const char *str, Token token, Database *db);
109static int f_left_brace (const char *str, Token token, Database *db);
110static int f_right_brace (const char *str, Token token, Database *db);
111static int f_white (const char *str, Token token, Database *db);
112static int f_backslash (const char *str, Token token, Database *db);
113static int f_numeric (const char *str, Token token, Database *db);
114static int f_default (const char *str, Token token, Database *db);
115
116static const TokenTable token_tbl[] = {
117    { T_NEWLINE,      1 },
118    { T_COMMENT,      1 },
119    { T_SEMICOLON,    1 },
120    { T_DOUBLE_QUOTE, 1 },
121    { T_LEFT_BRACE,   1 },
122    { T_RIGHT_BRACE,  1 },
123    { T_SPACE,        1 },
124    { T_TAB,          1 },
125    { T_BACKSLASH,    1 },
126    { T_NUMERIC_HEX,  2 },
127    { T_NUMERIC_DEC,  2 },
128    { T_NUMERIC_OCT,  2 },
129    { T_DEFAULT,      1 } /* any character */
130};
131
132#define	SYM_CR          '\r'
133#define	SYM_NEWLINE	'\n'
134#define	SYM_COMMENT	'#'
135#define	SYM_SEMICOLON	';'
136#define	SYM_DOUBLE_QUOTE	'"'
137#define	SYM_LEFT_BRACE	'{'
138#define	SYM_RIGHT_BRACE	'}'
139#define	SYM_SPACE	' '
140#define	SYM_TAB		'\t'
141#define	SYM_BACKSLASH	'\\'
142
143/************************************************************************/
144
145#define MAX_NAME_NEST	64
146
147typedef struct {
148    ParseState pre_state;
149    char *category;
150    char *name[MAX_NAME_NEST];
151    int nest_depth;
152    char **value;
153    int value_len;
154    int value_num;
155    int bufsize;        /* bufMaxSize >= bufsize >= 0 */
156    int bufMaxSize;     /* default : BUFSIZE */
157    char *buf;
158} DBParseInfo;
159
160static DBParseInfo parse_info;
161
162static void
163init_parse_info (void)
164{
165    static int allocated /* = 0 */;
166    char *ptr;
167    int size;
168    if (!allocated) {
169	bzero(&parse_info, sizeof(DBParseInfo));
170	parse_info.buf = (char *)Xmalloc(BUFSIZE);
171	parse_info.bufMaxSize = BUFSIZE;
172	allocated = 1;
173	return;
174    }
175    ptr = parse_info.buf;
176    size = parse_info.bufMaxSize;
177    bzero(&parse_info, sizeof(DBParseInfo));
178    parse_info.buf = ptr;
179    parse_info.bufMaxSize = size;
180}
181
182static void
183clear_parse_info (void)
184{
185    int i;
186    char *ptr;
187    int size;
188    parse_info.pre_state = S_NULL;
189    if (parse_info.category != NULL) {
190	Xfree(parse_info.category);
191    }
192    for (i = 0; i <= parse_info.nest_depth; ++i) {
193	if (parse_info.name[i]) {
194	    Xfree(parse_info.name[i]);
195	}
196    }
197    if (parse_info.value) {
198	if (*parse_info.value) {
199	    Xfree(*parse_info.value);
200	}
201	Xfree((char *)parse_info.value);
202    }
203    ptr = parse_info.buf;
204    size = parse_info.bufMaxSize;
205    bzero(&parse_info, sizeof(DBParseInfo));
206    parse_info.buf = ptr;
207    parse_info.bufMaxSize = size;
208}
209
210static Bool
211realloc_parse_info(
212    int len)
213{
214    char *p;
215
216    parse_info.bufMaxSize = BUFSIZE * ((parse_info.bufsize + len)/BUFSIZE + 1);
217    p = (char *)Xrealloc(parse_info.buf, parse_info.bufMaxSize);
218    if (p == NULL)
219        return False;
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 = (char *)Xrealloc(str, size);
253    } else {
254	str = (char *)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 = 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 = 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, 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, 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, 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 = (char **)Xmalloc(sizeof(char *) * 2);
491	*value_list = NULL;
492    } else {
493	char **prev_list = value_list;
494
495	value_list = (char **)
496	    Xrealloc(value_list, sizeof(char *) * (value_num + 2));
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 = (char *)Xmalloc(value_len + len + 1);
507    } else {
508	char *prev_value = value;
509
510	value = (char *)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, 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 += 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 = (Database)Xmalloc(sizeof(DatabaseRec));
597    if (new == (Database)NULL) {
598	goto err;
599    }
600    bzero(new, sizeof(DatabaseRec));
601
602    new->category = (char *)Xmalloc(strlen(parse_info.category) + 1);
603    if (new->category == NULL) {
604	goto err;
605    }
606    strcpy(new->category, parse_info.category);
607
608    if (! construct_name(name, sizeof(name))) {
609	goto err;
610    }
611    new->name = (char *)Xmalloc(strlen(name) + 1);
612    if (new->name == NULL) {
613	goto err;
614    }
615    strcpy(new->name, name);
616    new->next = *db;
617    new->value = parse_info.value;
618    new->value_num = parse_info.value_num;
619    *db = new;
620
621    Xfree(parse_info.name[parse_info.nest_depth]);
622    parse_info.name[parse_info.nest_depth] = NULL;
623
624    parse_info.value = (char **)NULL;
625    parse_info.value_num = 0;
626    parse_info.value_len = 0;
627
628    return 1;
629
630 err:
631    if (new) {
632	if (new->category) {
633	    Xfree(new->category);
634	}
635	if (new->name) {
636	    Xfree(new->name);
637	}
638	Xfree(new);
639    }
640    if (parse_info.value) {
641	if (*parse_info.value) {
642	    Xfree(*parse_info.value);
643	}
644	Xfree((char **)parse_info.value);
645	parse_info.value = (char **)NULL;
646	parse_info.value_num = 0;
647	parse_info.value_len = 0;
648    }
649    return 0;
650}
651
652#define END_MARK	"END"
653#define	END_MARK_LEN	3 /*strlen(END_MARK)*/
654
655static int
656check_category_end(
657    const char *str)
658{
659    const char *p;
660    int len;
661
662    p = str;
663    if (strncmp(p, END_MARK, END_MARK_LEN)) {
664	return 0;
665    }
666    p += END_MARK_LEN;
667
668    while (iswhite(*p)) {
669	++p;
670    }
671    len = strlen(parse_info.category);
672    if (strncmp(p, parse_info.category, len)) {
673	return 0;
674    }
675    p += len;
676    return p - str;
677}
678
679/************************************************************************/
680
681static int
682f_newline(
683    const char *str,
684    Token token,
685    Database *db)
686{
687    switch (parse_info.pre_state) {
688    case S_NULL:
689    case S_CATEGORY:
690	break;
691    case S_NAME:
692	return 0; /* no value */
693    case S_VALUE:
694	if (!store_to_database(db))
695	    return 0;
696	parse_info.pre_state = S_CATEGORY;
697	break;
698    default:
699	return 0;
700    }
701    return token_tbl[token].len;
702}
703
704static int
705f_comment(
706    const char *str,
707    Token token,
708    Database *db)
709{
710    /* NOTE: comment is already handled in read_line(),
711       so this function is not necessary. */
712
713    const char *p = str;
714
715    while (*p != SYM_NEWLINE && *p != SYM_CR && *p != '\0') {
716	++p;	/* zap to the end of line */
717    }
718    return p - str;
719}
720
721static int
722f_white(
723    const char *str,
724    Token token,
725    Database *db)
726{
727    const char *p = str;
728
729    while (iswhite(*p)) {
730	++p;
731    }
732    return p - str;
733}
734
735static int
736f_semicolon(
737    const char *str,
738    Token token,
739    Database *db)
740{
741    switch (parse_info.pre_state) {
742    case S_NULL:
743    case S_CATEGORY:
744    case S_NAME:
745	return 0;
746    case S_VALUE:
747	if (! append_value_list())
748	    return 0;
749	parse_info.pre_state = S_VALUE;
750	break;
751    default:
752	return 0;
753    }
754    return token_tbl[token].len;
755}
756
757static int
758f_left_brace(
759    const char *str,
760    Token token,
761    Database *db)
762{
763    switch (parse_info.pre_state) {
764    case S_NULL:
765    case S_CATEGORY:
766    case S_VALUE:
767	return 0;
768    case S_NAME:
769	if (parse_info.name[parse_info.nest_depth] == NULL
770	    || parse_info.nest_depth + 1 > MAX_NAME_NEST)
771	    return 0;
772	++parse_info.nest_depth;
773	parse_info.pre_state = S_CATEGORY;
774	break;
775    default:
776	return 0;
777    }
778    return token_tbl[token].len;
779}
780
781static int
782f_right_brace(
783    const char *str,
784    Token token,
785    Database *db)
786{
787    if (parse_info.nest_depth < 1)
788	return 0;
789
790    switch (parse_info.pre_state) {
791    case S_NULL:
792    case S_NAME:
793	return 0;
794    case S_VALUE:
795	if (! store_to_database(db))
796	    return 0;
797	/* fall into next case */
798    case S_CATEGORY:
799	if (parse_info.name[parse_info.nest_depth] != NULL) {
800	    Xfree(parse_info.name[parse_info.nest_depth]);
801	    parse_info.name[parse_info.nest_depth] = NULL;
802	}
803	--parse_info.nest_depth;
804	parse_info.pre_state = S_CATEGORY;
805	break;
806    default:
807	return 0;
808    }
809    return token_tbl[token].len;
810}
811
812static int
813f_double_quote(
814    const char *str,
815    Token token,
816    Database *db)
817{
818    char word[BUFSIZE];
819    char* wordp;
820    int len;
821
822    if ((len = strlen (str)) < sizeof word)
823	wordp = word;
824    else
825	wordp = Xmalloc (len + 1);
826    if (wordp == NULL)
827	return 0;
828
829    len = 0;
830    switch (parse_info.pre_state) {
831    case S_NULL:
832    case S_CATEGORY:
833	goto err;
834    case S_NAME:
835    case S_VALUE:
836	len = get_quoted_word(str, wordp);
837	if (len < 1)
838	    goto err;
839	if ((parse_info.bufsize + (int)strlen(wordp) + 1)
840					>= parse_info.bufMaxSize) {
841	    if (realloc_parse_info(strlen(wordp)+1) == False) {
842		goto err;
843	    }
844	}
845	strcpy(&parse_info.buf[parse_info.bufsize], wordp);
846	parse_info.bufsize += strlen(wordp);
847	parse_info.pre_state = S_VALUE;
848	break;
849    default:
850	goto err;
851    }
852    if (wordp != word)
853	Xfree (wordp);
854    return len;	/* including length of token */
855
856err:
857    if (wordp != word)
858	Xfree (wordp);
859    return 0;
860}
861
862static int
863f_backslash(
864    const char *str,
865    Token token,
866    Database *db)
867{
868    return f_default(str, token, db);
869}
870
871static int
872f_numeric(
873    const char *str,
874    Token token,
875    Database *db)
876{
877    char word[BUFSIZE];
878    const char *p;
879    char* wordp;
880    int len;
881    int token_len;
882
883    if ((len = strlen (str)) < sizeof word)
884	wordp = word;
885    else
886	wordp = Xmalloc (len + 1);
887    if (wordp == NULL)
888	return 0;
889
890    switch (parse_info.pre_state) {
891    case S_NULL:
892    case S_CATEGORY:
893	goto err;
894    case S_NAME:
895    case S_VALUE:
896	token_len = token_tbl[token].len;
897	p = str + token_len;
898	len = get_word(p, wordp);
899	if (len < 1)
900	    goto err;
901	if ((parse_info.bufsize + token_len + (int)strlen(wordp) + 1)
902					>= parse_info.bufMaxSize) {
903	    if (realloc_parse_info(token_len + strlen(wordp) + 1) == False)
904		goto err;
905	}
906	strncpy(&parse_info.buf[parse_info.bufsize], str, token_len);
907	strcpy(&parse_info.buf[parse_info.bufsize + token_len], wordp);
908	parse_info.bufsize += token_len + strlen(wordp);
909	parse_info.pre_state = S_VALUE;
910	break;
911    default:
912	goto err;
913    }
914    if (wordp != word)
915	Xfree (wordp);
916    return len + token_len;
917
918err:
919    if (wordp != word)
920	Xfree (wordp);
921    return 0;
922}
923
924static int
925f_default(
926    const char *str,
927    Token token,
928    Database *db)
929{
930    char word[BUFSIZE], *p;
931    char* wordp;
932    int len;
933
934    if ((len = strlen (str)) < sizeof word)
935	wordp = word;
936    else
937	wordp = Xmalloc (len + 1);
938    if (wordp == NULL)
939	return 0;
940
941    len = get_word(str, wordp);
942    if (len < 1)
943	goto err;
944
945    switch (parse_info.pre_state) {
946    case S_NULL:
947	if (parse_info.category != NULL)
948	    goto err;
949	p = (char *)Xmalloc(strlen(wordp) + 1);
950	if (p == NULL)
951	    goto err;
952	strcpy(p, wordp);
953	parse_info.category = p;
954	parse_info.pre_state = S_CATEGORY;
955	break;
956    case S_CATEGORY:
957	if (parse_info.nest_depth == 0) {
958	    if (check_category_end(str)) {
959		/* end of category is detected.
960		   clear context and zap to end of this line */
961		clear_parse_info();
962		len = strlen(str);
963		break;
964	    }
965	}
966	p = (char *)Xmalloc(strlen(wordp) + 1);
967	if (p == NULL)
968	    goto err;
969	strcpy(p, wordp);
970	if (parse_info.name[parse_info.nest_depth] != NULL) {
971	    Xfree(parse_info.name[parse_info.nest_depth]);
972	}
973	parse_info.name[parse_info.nest_depth] = p;
974	parse_info.pre_state = S_NAME;
975	break;
976    case S_NAME:
977    case S_VALUE:
978	if ((parse_info.bufsize + (int)strlen(wordp) + 1)
979					>= parse_info.bufMaxSize) {
980	    if (realloc_parse_info(strlen(wordp) + 1) == False)
981		goto err;
982	}
983	strcpy(&parse_info.buf[parse_info.bufsize], wordp);
984	parse_info.bufsize += strlen(wordp);
985	parse_info.pre_state = S_VALUE;
986	break;
987    default:
988	goto err;
989    }
990    if (wordp != word)
991	Xfree (wordp);
992    return len;
993
994err:
995    if (wordp != word)
996	Xfree (wordp);
997    return 0;
998}
999
1000/************************************************************************/
1001
1002#ifdef DEBUG
1003static void
1004PrintDatabase(
1005    Database db)
1006{
1007    Database p = db;
1008    int i = 0, j;
1009
1010    printf("***\n*** BEGIN Database\n***\n");
1011    while (p) {
1012	printf("%3d: ", i++);
1013	printf("%s, %s, ", p->category, p->name);
1014	printf("\t[%d: ", p->value_num);
1015	for (j = 0; j < p->value_num; ++j) {
1016	    printf("%s, ", p->value[j]);
1017	}
1018	printf("]\n");
1019	p = p->next;
1020    }
1021    printf("***\n*** END   Database\n***\n");
1022}
1023#endif
1024
1025static void
1026DestroyDatabase(
1027    Database db)
1028{
1029    Database p = db;
1030
1031    while (p) {
1032	if (p->category != NULL) {
1033	    Xfree(p->category);
1034	}
1035	if (p->name != NULL) {
1036	    Xfree(p->name);
1037	}
1038	if (p->value != (char **)NULL) {
1039	    if (*p->value != NULL) {
1040		Xfree(*p->value);
1041	    }
1042	    Xfree((char *)p->value);
1043	}
1044	db = p->next;
1045	Xfree((char *)p);
1046	p = db;
1047    }
1048}
1049
1050static int
1051CountDatabase(
1052    Database db)
1053{
1054    Database p = db;
1055    int cnt = 0;
1056
1057    while (p) {
1058	++cnt;
1059	p = p->next;
1060    }
1061    return cnt;
1062}
1063
1064static Database
1065CreateDatabase(
1066    char *dbfile)
1067{
1068    Database db = (Database)NULL;
1069    FILE *fd;
1070    Line line;
1071    char *p;
1072    Token token;
1073    int len;
1074    int error = 0;
1075
1076    fd = _XFopenFile(dbfile, "r");
1077    if (fd == (FILE *)NULL)
1078	return NULL;
1079
1080    bzero(&line, sizeof(Line));
1081    init_parse_info();
1082
1083    do {
1084	int rc = read_line(fd, &line);
1085	if (rc < 0) {
1086	    error = 1;
1087	    break;
1088	} else if (rc == 0) {
1089	    break;
1090	}
1091	p = line.str;
1092	while (*p) {
1093            int (*parse_proc)(const char *str, Token token, Database *db) = NULL;
1094
1095	    token = get_token(p);
1096
1097            switch (token_tbl[token].token) {
1098            case T_NEWLINE:
1099                parse_proc = f_newline;
1100                break;
1101            case T_COMMENT:
1102                parse_proc = f_comment;
1103                break;
1104            case T_SEMICOLON:
1105                parse_proc = f_semicolon;
1106                break;
1107            case T_DOUBLE_QUOTE:
1108                parse_proc = f_double_quote;
1109                break;
1110            case T_LEFT_BRACE:
1111                parse_proc = f_left_brace;
1112                break;
1113            case T_RIGHT_BRACE:
1114                parse_proc = f_right_brace;
1115                break;
1116            case T_SPACE:
1117            case T_TAB:
1118                parse_proc = f_white;
1119                break;
1120            case T_BACKSLASH:
1121                parse_proc = f_backslash;
1122                break;
1123            case T_NUMERIC_HEX:
1124            case T_NUMERIC_DEC:
1125            case T_NUMERIC_OCT:
1126                parse_proc = f_numeric;
1127                break;
1128            case T_DEFAULT:
1129                parse_proc = f_default;
1130                break;
1131            }
1132
1133            len = parse_proc(p, token, &db);
1134
1135	    if (len < 1) {
1136		error = 1;
1137		break;
1138	    }
1139	    p += len;
1140	}
1141    } while (!error);
1142
1143    if (parse_info.pre_state != S_NULL) {
1144	clear_parse_info();
1145	error = 1;
1146    }
1147    if (error) {
1148#ifdef	DEBUG
1149	fprintf(stderr, "database format error at line %d.\n", line.seq);
1150#endif
1151	DestroyDatabase(db);
1152	db = (Database)NULL;
1153    }
1154
1155    fclose(fd);
1156    free_line(&line);
1157
1158#ifdef	DEBUG
1159    PrintDatabase(db);
1160#endif
1161
1162    return db;
1163}
1164
1165/************************************************************************/
1166
1167#ifndef	NOT_X_ENV
1168
1169/* locale framework functions */
1170
1171typedef struct _XlcDatabaseRec {
1172    XrmQuark category_q;
1173    XrmQuark name_q;
1174    Database db;
1175    struct _XlcDatabaseRec *next;
1176} XlcDatabaseRec, *XlcDatabase;
1177
1178typedef	struct _XlcDatabaseListRec {
1179    XrmQuark name_q;
1180    XlcDatabase lc_db;
1181    Database database;
1182    int ref_count;
1183    struct _XlcDatabaseListRec *next;
1184} XlcDatabaseListRec, *XlcDatabaseList;
1185
1186/* database cache list (per file) */
1187static XlcDatabaseList _db_list = (XlcDatabaseList)NULL;
1188
1189/************************************************************************/
1190/*	_XlcGetResource(lcd, category, class, value, count)		*/
1191/*----------------------------------------------------------------------*/
1192/*	This function retrieves XLocale database information.		*/
1193/************************************************************************/
1194void
1195_XlcGetResource(
1196    XLCd lcd,
1197    const char *category,
1198    const char *class,
1199    char ***value,
1200    int *count)
1201{
1202    XLCdPublicMethodsPart *methods = XLC_PUBLIC_METHODS(lcd);
1203
1204    (*methods->get_resource)(lcd, category, class, value, count);
1205    return;
1206}
1207
1208/************************************************************************/
1209/*	_XlcGetLocaleDataBase(lcd, category, class, value, count)	*/
1210/*----------------------------------------------------------------------*/
1211/*	This function retrieves XLocale database information.		*/
1212/************************************************************************/
1213void
1214_XlcGetLocaleDataBase(
1215    XLCd lcd,
1216    const char *category,
1217    const char *name,
1218    char ***value,
1219    int *count)
1220{
1221    XlcDatabase lc_db = (XlcDatabase)XLC_PUBLIC(lcd, xlocale_db);
1222    XrmQuark category_q, name_q;
1223
1224    category_q = XrmStringToQuark(category);
1225    name_q = XrmStringToQuark(name);
1226    for (; lc_db->db; ++lc_db) {
1227	if (category_q == lc_db->category_q && name_q == lc_db->name_q) {
1228	    *value = lc_db->db->value;
1229	    *count = lc_db->db->value_num;
1230	    return;
1231	}
1232    }
1233    *value = (char **)NULL;
1234    *count = 0;
1235}
1236
1237/************************************************************************/
1238/*	_XlcDestroyLocaleDataBase(lcd)					*/
1239/*----------------------------------------------------------------------*/
1240/*	This function destroy the XLocale Database that bound to the 	*/
1241/*	specified lcd.  If the XLocale Database is refered from some 	*/
1242/*	other lcd, this function just decreases reference count of 	*/
1243/*	the database.  If no locale refers the database, this function	*/
1244/*	remove it from the cache list and free work area.		*/
1245/************************************************************************/
1246void
1247_XlcDestroyLocaleDataBase(
1248    XLCd lcd)
1249{
1250    XlcDatabase lc_db = (XlcDatabase)XLC_PUBLIC(lcd, xlocale_db);
1251    XlcDatabaseList p, prev;
1252
1253    for (p = _db_list, prev = (XlcDatabaseList)NULL; p;
1254	 prev = p, p = p->next) {
1255	if (p->lc_db == lc_db) {
1256	    if ((-- p->ref_count) < 1) {
1257		if (p->lc_db != (XlcDatabase)NULL) {
1258		    Xfree((char *)p->lc_db);
1259		}
1260		DestroyDatabase(p->database);
1261		if (prev == (XlcDatabaseList)NULL) {
1262		    _db_list = p->next;
1263		} else {
1264		    prev->next = p->next;
1265		}
1266		Xfree((char*)p);
1267	    }
1268	    break;
1269	}
1270    }
1271    XLC_PUBLIC(lcd, xlocale_db) = (XPointer)NULL;
1272}
1273
1274/************************************************************************/
1275/*	_XlcCreateLocaleDataBase(lcd)					*/
1276/*----------------------------------------------------------------------*/
1277/*	This function create an XLocale database which correspond to	*/
1278/*	the specified XLCd.						*/
1279/************************************************************************/
1280XPointer
1281_XlcCreateLocaleDataBase(
1282    XLCd lcd)
1283{
1284    XlcDatabaseList list, new;
1285    Database p, database = (Database)NULL;
1286    XlcDatabase lc_db = (XlcDatabase)NULL;
1287    XrmQuark name_q;
1288    char *name;
1289    int i, n;
1290
1291    name = _XlcFileName(lcd, "locale");
1292    if (name == NULL)
1293	return (XPointer)NULL;
1294
1295#ifndef __UNIXOS2__
1296    name_q = XrmStringToQuark(name);
1297#else
1298    name_q = XrmStringToQuark((char*)__XOS2RedirRoot(name));
1299#endif
1300    for (list = _db_list; list; list = list->next) {
1301	if (name_q == list->name_q) {
1302	    list->ref_count++;
1303	    Xfree (name);
1304	    return XLC_PUBLIC(lcd, xlocale_db) = (XPointer)list->lc_db;
1305	}
1306    }
1307
1308    database = CreateDatabase(name);
1309    if (database == (Database)NULL) {
1310	Xfree (name);
1311	return (XPointer)NULL;
1312    }
1313    n = CountDatabase(database);
1314    lc_db = (XlcDatabase)Xmalloc(sizeof(XlcDatabaseRec) * (n + 1));
1315    if (lc_db == (XlcDatabase)NULL)
1316	goto err;
1317    bzero(lc_db, sizeof(XlcDatabaseRec) * (n + 1));
1318    for (p = database, i = 0; p && i < n; p = p->next, ++i) {
1319	lc_db[i].category_q = XrmStringToQuark(p->category);
1320	lc_db[i].name_q = XrmStringToQuark(p->name);
1321	lc_db[i].db = p;
1322    }
1323
1324    new = (XlcDatabaseList)Xmalloc(sizeof(XlcDatabaseListRec));
1325    if (new == (XlcDatabaseList)NULL) {
1326	goto err;
1327    }
1328    new->name_q = name_q;
1329    new->lc_db = lc_db;
1330    new->database = database;
1331    new->ref_count = 1;
1332    new->next = _db_list;
1333    _db_list = new;
1334
1335    Xfree (name);
1336    return XLC_PUBLIC(lcd, xlocale_db) = (XPointer)lc_db;
1337
1338 err:
1339    DestroyDatabase(database);
1340    if (lc_db != (XlcDatabase)NULL) {
1341	Xfree((char *)lc_db);
1342    }
1343    Xfree (name);
1344    return (XPointer)NULL;
1345}
1346
1347#endif	/* NOT_X_ENV */
1348