1 /* Path-style configuration file parser for Grecs.
2    Copyright (C) 2011-2016 Sergey Poznyakoff
3 
4    Grecs is free software; you can redistribute it and/or modify it
5    under the terms of the GNU General Public License as published by the
6    Free Software Foundation; either version 3 of the License, or (at your
7    option) any later version.
8 
9    Grecs is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License along
15    with Grecs. If not, see <http://www.gnu.org/licenses/>. */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <stdlib.h>
21 #include <string.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <grecs.h>
25 
26 static int
next_char(FILE * infile)27 next_char(FILE *infile)
28 {
29 	int c = fgetc(infile);
30 	if (c == '\n')
31 		grecs_locus_point_advance_line(grecs_current_locus_point);
32 	else {
33 		grecs_current_locus_point.col++;
34 		if (c == '\\') {
35 			int nc = fgetc(infile);
36 			if (nc == '\n') {
37 				grecs_locus_point_advance_line(grecs_current_locus_point);
38 				c = fgetc(infile);
39 				grecs_current_locus_point.col++;
40 			} else
41 				ungetc(nc, infile);
42 		}
43 	}
44 	return c;
45 }
46 
47 struct grecs_node *
grecs_path_parser(const char * name,int traceflags)48 grecs_path_parser(const char *name, int traceflags)
49 {
50 	struct grecs_node *root, *subtree = NULL, *node;
51 	FILE *infile;
52 	struct grecs_txtacc *acc = NULL;
53 	char *kw, *val;
54 	grecs_locus_t kwloc, valloc, rootloc;
55 	int inquote;
56 	int lookahead;
57 	int err = 0;
58 	unsigned prev_col;
59 
60 	infile = fopen(name, "r");
61 	if (!infile) {
62 		grecs_error(NULL, errno, _("cannot open `%s'"), name);
63 		return NULL;
64 	}
65 	grecs_current_locus_point.file = grecs_install_text(name);
66 	grecs_current_locus_point.line = 1;
67 	grecs_current_locus_point.col = 0;
68 	rootloc.beg = grecs_current_locus_point;
69 	rootloc.beg.col++;
70 
71 	acc = grecs_txtacc_create();
72 
73 	while ((lookahead = next_char(infile)) > 0) {
74 		while (1) {
75 			while (lookahead == ' ' || lookahead == '\t')
76 				lookahead = next_char(infile);
77 
78 			if (lookahead == '#') {
79 				while ((lookahead = next_char(infile)) &&
80 				       lookahead != '\n')
81 					;
82 				continue;
83 			}
84 			break;
85 		}
86 
87 		if (lookahead <= 0)
88 			break;
89 
90 		kwloc.beg = grecs_current_locus_point;
91 
92 		inquote = 0;
93 		for (; lookahead > 0 && lookahead != ':';
94 		     lookahead = next_char(infile)) {
95 			if (inquote) {
96 				if (inquote == '"' && lookahead == '\\') {
97 					lookahead = next_char(infile);
98 					if (lookahead <= 0)
99 						break;
100 				} else if (lookahead == inquote)
101 					inquote = 0;
102 			} else if (lookahead == '\'' || lookahead == '"')
103 				inquote = lookahead;
104 			grecs_txtacc_grow_char(acc, lookahead);
105 		}
106 
107 		if (lookahead <= 0) {
108 			grecs_error(&kwloc, 0, _("unexpected end of file"));
109 			err = 1;
110 			break;
111 		}
112 
113 		grecs_txtacc_grow_char(acc, 0);
114 		kw = grecs_txtacc_finish(acc, 0);
115 
116 		kwloc.end = grecs_current_locus_point;
117 		kwloc.end.col--;
118 
119 		while ((lookahead = next_char(infile)) > 0 &&
120 		       (lookahead == ' ' || lookahead == '\t'));
121 
122 		if (lookahead <= 0) {
123 			grecs_error(&kwloc, 0, _("unexpected end of file"));
124 			err = 1;
125 			break;
126 		}
127 
128 		valloc.beg = grecs_current_locus_point;
129 		do {
130 			grecs_txtacc_grow_char(acc, lookahead);
131 			prev_col = grecs_current_locus_point.col;
132 		} while ((lookahead = next_char(infile)) > 0 &&
133 			 lookahead != '\n');
134 		valloc.end = grecs_current_locus_point;
135 		valloc.end.line--;
136 		valloc.end.col = prev_col;
137 
138 		grecs_txtacc_grow_char(acc, 0);
139 		val = grecs_txtacc_finish(acc, 0);
140 
141 		node = grecs_node_from_path_locus(kw, val, &kwloc, &valloc);
142 		if (!node) {
143 			grecs_error(&kwloc, 0, _("parse error"));
144 			err = 1;
145 			break;
146 		}
147 		node->locus.end = valloc.end;
148 		node->idloc = kwloc;
149 
150 		if (!subtree)
151 			subtree = node;
152 		else
153 			grecs_node_bind(subtree, node, 0);
154 		grecs_txtacc_free_string(acc, kw);
155 		grecs_txtacc_free_string(acc, val);
156 	}
157 
158 	fclose(infile);
159 	grecs_txtacc_free(acc);
160 
161 	if (err) {
162 		grecs_tree_free(subtree);
163 		root = NULL;
164 	} else {
165 		rootloc.end = grecs_current_locus_point;
166 		root = grecs_node_create(grecs_node_root, &rootloc);
167 		root->v.texttab = grecs_text_table();
168 		grecs_node_bind(root, subtree, 1);
169 		grecs_tree_reduce(root, NULL, 0);
170 	}
171 
172 	return root;
173 }
174 
175