1 /********************************************
2 error.c
3 copyright 2008-2014,2016 Thomas E. Dickey
4 copyright 1991-1994,1995 Michael D. Brennan
5 
6 This is a source file for mawk, an implementation of
7 the AWK programming language.
8 
9 Mawk is distributed without warranty under the terms of
10 the GNU General Public License, version 2, 1991.
11 ********************************************/
12 
13 /*
14  * $MawkId: error.c,v 1.23 2016/09/29 23:00:43 tom Exp $
15  */
16 
17 #include <mawk.h>
18 #include <scan.h>
19 #include <bi_vars.h>
20 
21 /* for run time error messages only */
22 unsigned rt_nr, rt_fnr;
23 /* *INDENT-OFF* */
24 static const struct token_str {
25     short token;
26     const char *str;
27 } token_str[] = {
28     { EOF,                "end of file" },
29     { NL,                 "end of line" },
30     { SEMI_COLON,         ";" },
31     { LBRACE,             "{" },
32     { RBRACE,             "}" },
33     { SC_FAKE_SEMI_COLON, "}" },
34     { LPAREN,             "(" },
35     { RPAREN,             ")" },
36     { LBOX,               "[" },
37     { RBOX,               "]" },
38     { QMARK,              "?" },
39     { COLON,              ":" },
40     { OR,                 "||" },
41     { AND,                "&&" },
42     { ASSIGN,             "=" },
43     { ADD_ASG,            "+=" },
44     { SUB_ASG,            "-=" },
45     { MUL_ASG,            "*=" },
46     { DIV_ASG,            "/=" },
47     { MOD_ASG,            "%=" },
48     { POW_ASG,            "^=" },
49     { EQ,                 "==" },
50     { NEQ,                "!=" },
51     { LT,                 "<" },
52     { LTE,                "<=" },
53     { GT,                 ">" },
54     { GTE,                ">=" },
55     { MATCH,              string_buff },
56     { PLUS,               "+" },
57     { MINUS,              "-" },
58     { MUL,                "*" },
59     { DIV,                "/" },
60     { MOD,                "%" },
61     { POW,                "^" },
62     { NOT,                "!" },
63     { COMMA,              "," },
64     { INC_or_DEC,         string_buff },
65     { DOUBLE,             string_buff },
66     { STRING_,            string_buff },
67     { ID,                 string_buff },
68     { FUNCT_ID,           string_buff },
69     { BUILTIN,            string_buff },
70     { IO_OUT,             string_buff },
71     { IO_IN,              "<" },
72     { PIPE,               "|" },
73     { DOLLAR,             "$" },
74     { FIELD,              "$" },
75     { 0,                  (char *) 0 }
76 };
77 /* *INDENT-ON* */
78 
79 /* if paren_cnt >0 and we see one of these, we are missing a ')' */
80 static const int missing_rparen[] =
81 {
82     EOF, NL, SEMI_COLON, SC_FAKE_SEMI_COLON, RBRACE, 0
83 };
84 
85 /* ditto for '}' */
86 static const int missing_rbrace[] =
87 {
88     EOF, BEGIN, END, 0
89 };
90 
91 static void
missing(int c,const char * n,unsigned ln)92 missing(int c, const char *n, unsigned ln)
93 {
94     const char *s0, *s1;
95 
96     if (pfile_name) {
97 	s0 = pfile_name;
98 	s1 = ": ";
99     } else
100 	s0 = s1 = "";
101 
102     errmsg(0, "%s%sline %u: missing %c near %s", s0, s1, ln, c, n);
103 }
104 
105 /* we won't use s as input
106    (yacc and bison force this).
107    We will use s for storage to keep lint or the compiler
108    off our back.
109 */
110 void
yyerror(const char * s GCC_UNUSED)111 yyerror(const char *s GCC_UNUSED)
112 {
113     const char *ss = 0;
114     const struct token_str *p;
115     const int *ip;
116 
117     for (p = token_str; p->token; p++)
118 	if (current_token == p->token) {
119 	    ss = p->str;
120 	    break;
121 	}
122 
123     if (!ss)			/* search the keywords */
124 	ss = find_kw_str(current_token);
125 
126     if (ss) {
127 	if (paren_cnt)
128 	    for (ip = missing_rparen; *ip; ip++)
129 		if (*ip == current_token) {
130 		    missing(')', ss, token_lineno);
131 		    paren_cnt = 0;
132 		    goto done;
133 		}
134 
135 	if (brace_cnt)
136 	    for (ip = missing_rbrace; *ip; ip++)
137 		if (*ip == current_token) {
138 		    missing('}', ss, token_lineno);
139 		    brace_cnt = 0;
140 		    goto done;
141 		}
142 
143 	compile_error("syntax error at or near %s", ss);
144 
145     } else			/* special cases */
146 	switch (current_token) {
147 	case UNEXPECTED:
148 	    unexpected_char();
149 	    goto done;
150 
151 	case BAD_DECIMAL:
152 	    compile_error(
153 			     "syntax error in decimal constant %s",
154 			     string_buff);
155 	    break;
156 
157 	case RE:
158 	    compile_error(
159 			     "syntax error at or near /%s/",
160 			     string_buff);
161 	    break;
162 
163 	default:
164 	    compile_error("syntax error");
165 	    break;
166 	}
167     return;
168 
169   done:
170     if (++compile_error_count == MAX_COMPILE_ERRORS)
171 	mawk_exit(2);
172 }
173 
174 /* generic error message with a hook into the system error
175    messages if errnum > 0 */
176 
177 void
errmsg(int errnum,const char * format,...)178 errmsg(int errnum, const char *format,...)
179 {
180     va_list args;
181 
182     fprintf(stderr, "%s: ", progname);
183 
184 #if OPT_TRACE > 0
185     va_start(args, format);
186     Trace("\n?? errmsg \n");
187     TraceVA(format, args);
188     Trace("\n");
189     va_end(args);
190 #endif
191 
192     va_start(args, format);
193     vfprintf(stderr, format, args);
194     va_end(args);
195 
196     if (errnum > 0)
197 	fprintf(stderr, " (%s)", strerror(errnum));
198 
199     fprintf(stderr, "\n");
200 }
201 
202 void
compile_error(const char * format,...)203 compile_error(const char *format,...)
204 {
205     va_list args;
206     const char *s0, *s1;
207 
208     /* with multiple program files put program name in
209        error message */
210     if (pfile_name) {
211 	s0 = pfile_name;
212 	s1 = ": ";
213     } else {
214 	s0 = s1 = "";
215     }
216 
217 #ifdef DEBUG
218     fflush(stdout);
219 #endif
220     fprintf(stderr, "%s: %s%sline %u: ", progname, s0, s1, token_lineno);
221     va_start(args, format);
222     vfprintf(stderr, format, args);
223     va_end(args);
224     fprintf(stderr, "\n");
225     if (++compile_error_count == MAX_COMPILE_ERRORS)
226 	mawk_exit(2);
227 }
228 
229 void
bozo(const char * s)230 bozo(const char *s)
231 {
232     errmsg(0, "bozo: %s", s);
233     mawk_exit(3);
234 }
235 
236 void
overflow(const char * s,unsigned size)237 overflow(const char *s, unsigned size)
238 {
239     errmsg(0, "program limit exceeded: %s size=%u", s, size);
240     mawk_exit(2);
241 }
242 
243 /* print as much as we know about where a rt error occurred */
244 
245 static void
rt_where(void)246 rt_where(void)
247 {
248     if (FILENAME->type != C_STRING)
249 	cast1_to_s(FILENAME);
250 
251     fprintf(stderr, "\tFILENAME=\"%s\" FNR=%u NR=%u\n",
252 	    string(FILENAME)->str, rt_fnr, rt_nr);
253 }
254 
255 void
rt_error(const char * format,...)256 rt_error(const char *format,...)
257 {
258     va_list args;
259 
260     fprintf(stderr, "%s: run time error: ", progname);
261     va_start(args, format);
262     vfprintf(stderr, format, args);
263     va_end(args);
264     putc('\n', stderr);
265     rt_where();
266     mawk_exit(2);
267 }
268 
269 /* run time */
270 void
rt_overflow(const char * s,unsigned size)271 rt_overflow(const char *s, unsigned size)
272 {
273     errmsg(0, "program limit exceeded: %s size=%u", s, size);
274     rt_where();
275     mawk_exit(2);
276 }
277 
278 void
unexpected_char(void)279 unexpected_char(void)
280 {
281     int c = yylval.ival;
282 
283     fprintf(stderr, "%s: %u: ", progname, token_lineno);
284     if (c > ' ' && c < 127)
285 	fprintf(stderr, "unexpected character '%c'\n", c);
286     else
287 	fprintf(stderr, "unexpected character 0x%02x\n", c);
288 }
289 
290 const char *
type_to_str(int type)291 type_to_str(int type)
292 {
293     const char *retval = 0;
294 
295     switch (type) {
296     case ST_NONE:
297 	retval = "untyped variable";
298 	break;
299     case ST_VAR:
300 	retval = "variable";
301 	break;
302     case ST_KEYWORD:
303 	retval = "keyword";
304 	break;
305     case ST_BUILTIN:
306 	retval = "builtin";
307 	break;
308     case ST_ARRAY:
309 	retval = "array";
310 	break;
311     case ST_FIELD:
312 	retval = "field";
313 	break;
314     case ST_NR:
315 	retval = "NR";
316 	break;
317     case ST_ENV:
318 	retval = "ENVIRON";
319 	break;
320     case ST_FUNCT:
321 	retval = "function";
322 	break;
323     case ST_LENGTH:
324 	retval = "length";
325 	break;
326     case ST_LOCAL_VAR:
327 	retval = "local variable";
328 	break;
329     case ST_LOCAL_NONE:
330 	retval = "local untyped variable";
331 	break;
332     case ST_LOCAL_ARRAY:
333 	retval = "local array";
334 	break;
335     default:
336 	bozo("type_to_str");
337     }
338     return retval;
339 }
340 
341 /* emit an error message about a type clash */
342 void
type_error(SYMTAB * p)343 type_error(SYMTAB * p)
344 {
345     compile_error("illegal reference to %s %s",
346 		  type_to_str(p->type), p->name);
347 }
348