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