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