1 /*
2 * cook - file construction tool
3 * Copyright (C) 1994, 1997-1999, 2001, 2006, 2007 Peter Miller;
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see
18 * <http://www.gnu.org/licenses/>.
19 */
20
21 #include <common/ac/ctype.h>
22 #include <common/ac/errno.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25
26 #include <common/error_intl.h>
27 #include <common/input/file_text.h>
28 #include <common/input/null.h>
29 #include <common/str.h>
30 #include <cook/fingerprint/lex.h>
31 #include <cook/fingerprint/gram.gen.h> /* after str.h */
32
33 static input_ty *fp;
34 static int nerr;
35 static long linum;
36
37
38 /*
39 * NAME
40 * fingerprint_lex_open
41 *
42 * SYNOPSIS
43 * void fingerprint_lex_open(string_ty *filename);
44 *
45 * DESCRIPTION
46 * The fingerprint_lex_open function is used to commence lexical
47 * analysis of the given file. If an error occurs, it will be
48 * reported and the function will NOT return.
49 */
50
51 void
fingerprint_lex_open(string_ty * fn)52 fingerprint_lex_open(string_ty *fn)
53 {
54 struct stat st;
55
56 /* don't use os_exists, you get a loop */
57 if (stat(fn->str_text, &st) < 0 && (errno == ENOENT || errno == ENOTDIR))
58 fp = input_null();
59 else
60 fp = input_file_text_open(fn);
61 linum = 1;
62 nerr = 0;
63 }
64
65
66 /*
67 * NAME
68 * fingerprint_lex_close
69 *
70 * SYNOPSIS
71 * void fingerprint_lex_close(void);
72 *
73 * DESCRIPTION
74 * The fingerprint_lex_close function is used to complete lexical
75 * analysis of a file and release resources consumed in doing so.
76 * If an erro roccurred during the parse, the total will be reported
77 * and this function will NOT return.
78 */
79
80 void
fingerprint_lex_close(void)81 fingerprint_lex_close(void)
82 {
83 if (nerr)
84 {
85 sub_context_ty *scp;
86
87 scp = sub_context_new();
88 sub_var_set_string(scp, "File_Name", input_filename(fp));
89 sub_var_set_long(scp, "Number", nerr);
90 sub_var_optional(scp, "Number");
91 fatal_intl(scp, i18n("$filename: found $number fatal errors"));
92 /* NOTREACHED */
93 }
94 input_delete(fp);
95 nerr = 0;
96 fp = 0;
97 linum = 0;
98 }
99
100
101 /*
102 * NAME
103 * lex_getc
104 *
105 * SYNOPSIS
106 * int lex_getc(void);
107 *
108 * DESCRIPTION
109 * The lex_getc function is used to fetch another character from
110 * the input stream. If tere are errors reading the file, they
111 * will be reported and this function will NOT return.
112 *
113 * RETURNS
114 * int; zero or positive numbers for input characters, or INPUT_EOF
115 * on end of file.
116 */
117
118 static int
lex_getc(void)119 lex_getc(void)
120 {
121 int c;
122
123 c = input_getc(fp);
124 if (c == '\n')
125 linum++;
126 return c;
127 }
128
129
130 /*
131 * NAME
132 * lex_getc_undo
133 *
134 * SYNOPSIS
135 * void lex_getc_undo(int);
136 *
137 * DESCRIPTION
138 * The lex_getc_undo function is used to return the given character
139 * to the input stream. The push back stack is arbitrarily deep.
140 */
141
142 static void
lex_getc_undo(int c)143 lex_getc_undo(int c)
144 {
145 switch (c)
146 {
147 case INPUT_EOF:
148 break;
149
150 case '\n':
151 --linum;
152 /* fall through... */
153
154 default:
155 input_ungetc(fp, c);
156 break;
157 }
158 }
159
160
161 /*
162 * NAME
163 * fingerprint_error
164 *
165 * SYNOPSIS
166 * void fingerprint_error(sub_context_ty *cntxt, char *text);
167 *
168 * DESCRIPTION
169 * The fingerprint_error function is used to report errors
170 * while parsing fingerprint files. The substitution context
171 * given (if any) will be used. The error message text will be
172 * internationalized and substituted before being reported.
173 *
174 * The function does return, however the fingerprint_lex_close
175 * function will, when eventually called, not return.
176 */
177
178 static void
fingerprint_error(sub_context_ty * scp,char * s)179 fingerprint_error(sub_context_ty *scp, char *s)
180 {
181 string_ty *buffer;
182 int len;
183 int need_to_delete;
184
185 if (scp)
186 need_to_delete = 0;
187 else
188 {
189 scp = sub_context_new();
190 need_to_delete = 1;
191 }
192
193 buffer = subst_intl(scp, s);
194 len = buffer->str_length;
195 while (len > 0 && isspace(buffer->str_text[len - 1]))
196 --len;
197
198 /* re-use substitution context */
199 sub_var_set_string(scp, "File_Name", input_filename(fp));
200 sub_var_set_long(scp, "Number", linum);
201 sub_var_set(scp, "MeSsaGe", "%.*s", len, buffer->str_text);
202 error_intl(scp, i18n("$filename: $number: $message"));
203 str_free(buffer);
204 if (++nerr >= 20)
205 {
206 /* re-use substitution context */
207 sub_var_set_string(scp, "File_Name", input_filename(fp));
208 fatal_intl(scp, i18n("$filename: too many fatal errors"));
209 }
210
211 if (need_to_delete)
212 sub_context_delete(scp);
213 }
214
215
216 /*
217 * NAME
218 * fingerprint_gram_lex
219 *
220 * SYNOPSIS
221 * int fingerprint_gram_lex(void);
222 *
223 * DESCRIPTION
224 * The fingerprint_gram_lex function is called by the yacc-generated
225 * grammar to partition the input into discrete tokens. The return
226 * values are defined by yacc.
227 *
228 * SIDE EFFECTS
229 * Tokens which need more to describe them than just the return
230 * value have that additional information stored into the
231 * fingerprint_gram_lval variable, provided to us by yacc.
232 */
233
234 int
fingerprint_gram_lex(void)235 fingerprint_gram_lex(void)
236 {
237 int c;
238 char buffer[2000];
239 char *cp;
240 long n;
241
242 if (!fp)
243 return 0;
244 for (;;)
245 {
246 c = lex_getc();
247 switch (c)
248 {
249 case INPUT_EOF:
250 return 0;
251
252 case '"':
253 cp = buffer;
254 for (;;)
255 {
256 c = lex_getc();
257 if (c == INPUT_EOF || c == '\n')
258 {
259 unterm:
260 fingerprint_gram_error(i18n("unterminated string"));
261 break;
262 }
263 if (c == '"')
264 break;
265 if (c == '\\')
266 {
267 c = lex_getc();
268 if (c == INPUT_EOF || c == '\n')
269 goto unterm;
270 if (c != '"' && c != '\\')
271 {
272 sub_context_ty *scp;
273
274 scp = sub_context_new();
275 sub_var_set(scp, "Name", "\\%c", c);
276 fingerprint_error(scp, i18n("unknown '$name' escape"));
277 sub_context_delete(scp);
278 }
279 }
280 if (cp < ENDOF(buffer))
281 *cp++ = c;
282 }
283 fingerprint_gram_lval.lv_string = str_n_from_c(buffer, cp - buffer);
284 return STRING;
285
286 case '0':
287 case '1':
288 case '2':
289 case '3':
290 case '4':
291 case '5':
292 case '6':
293 case '7':
294 case '8':
295 case '9':
296 n = 0;
297 for (;;)
298 {
299 n = n * 10 + c - '0';
300 c = lex_getc();
301 switch (c)
302 {
303 case '0':
304 case '1':
305 case '2':
306 case '3':
307 case '4':
308 case '5':
309 case '6':
310 case '7':
311 case '8':
312 case '9':
313 continue;
314
315 default:
316 break;
317 }
318 break;
319 }
320 lex_getc_undo(c);
321 fingerprint_gram_lval.lv_number = n;
322 return NUMBER;
323
324 case ' ':
325 case '\t':
326 case '\n':
327 break;
328
329 case '=':
330 return EQ;
331
332 case '{':
333 return LB;
334
335 case '}':
336 return RB;
337
338 default:
339 return JUNK;
340 }
341 }
342 }
343
344
345 /*
346 * NAME
347 * fingerprint_gram_error
348 *
349 * SYNOPSIS
350 * void fingerprint_gram_error(char *text);
351 *
352 * DESCRIPTION
353 * The fingerprint_gram_error function is called by yacc to report
354 * parse errors.
355 */
356
357 void
fingerprint_gram_error(char * s)358 fingerprint_gram_error(char *s)
359 {
360 fingerprint_error(0, s);
361 }
362