/******************************************** error.c copyright 1991, 1992 Michael D. Brennan This is a source file for mawk, an implementation of the AWK programming language. Mawk is distributed without warranty under the terms of the GNU General Public License, version 2, 1991. ********************************************/ /* $Log: error.c,v $ * Revision 1.6 1995/06/06 00:18:22 mike * change mawk_exit(1) to mawk_exit(2) * * Revision 1.5 1994/12/13 00:26:33 mike * rt_nr and rt_fnr for run-time error messages * * Revision 1.4 1994/09/23 00:20:00 mike * minor bug fix: handle \ in eat_nl() * * Revision 1.3 1993/07/17 13:22:49 mike * indent and general code cleanup * * Revision 1.2 1993/07/04 12:51:44 mike * start on autoconfig changes * * Revision 1.1.1.1 1993/07/03 18:58:11 mike * move source to cvs * * Revision 5.3 1993/01/22 14:55:46 mike * trivial change for unexpected_char() * * Revision 5.2 1992/10/02 23:26:04 mike * using vargs.h * * Revision 5.1 1991/12/05 07:55:48 brennan * 1.1 pre-release * */ #include "mawk.h" #include "scan.h" #include "bi_vars.h" #include "vargs.h" #ifndef EOF #define EOF (-1) #endif static void PROTO( rt_where, (void) ) ; static void PROTO( missing, (int, char *, int) ) ; static char *PROTO( type_to_str, (int) ) ; #ifdef NO_VFPRINTF #define vfprintf simple_vfprintf #endif /* for run time error messages only */ unsigned rt_nr , rt_fnr ; static struct token_str { short token ; char *str ; } token_str[] = { EOF , "end of file" , NL , "end of line", SEMI_COLON , ";" , LBRACE , "{" , RBRACE , "}" , SC_FAKE_SEMI_COLON, "}", LPAREN , "(" , RPAREN , ")" , LBOX , "[", RBOX , "]", QMARK , "?", COLON , ":", OR, "||", AND, "&&", ASSIGN , "=" , ADD_ASG, "+=", SUB_ASG, "-=", MUL_ASG, "*=", DIV_ASG, "/=", MOD_ASG, "%=", POW_ASG, "^=", EQ , "==" , NEQ , "!=", LT, "<" , LTE, "<=" , GT, ">", GTE, ">=" , MATCH, string_buff, PLUS , "+" , MINUS, "-" , MUL , "*" , DIV, "/" , MOD, "%" , POW, "^" , NOT, "!" , COMMA, "," , INC_or_DEC , string_buff , DOUBLE , string_buff , STRING_ , string_buff , ID , string_buff , FUNCT_ID , string_buff , BUILTIN , string_buff , IO_OUT , string_buff , IO_IN, "<" , PIPE, "|" , DOLLAR, "$" , FIELD, "$" , 0, (char *) 0 } ; /* if paren_cnt >0 and we see one of these, we are missing a ')' */ static int missing_rparen[] = { EOF, NL, SEMI_COLON, SC_FAKE_SEMI_COLON, RBRACE, 0 } ; /* ditto for '}' */ static int missing_rbrace[] = { EOF, BEGIN, END , 0 } ; static void missing( c, n , ln) int c ; char *n ; int ln ; { char *s0, *s1 ; if ( pfile_name ) { s0 = pfile_name ; s1 = ": " ; } else s0 = s1 = "" ; errmsg(0, "%s%sline %u: missing %c near %s" ,s0, s1, ln, c, n) ; } void yyerror(s) char *s ; /* we won't use s as input (yacc and bison force this). We will use s for storage to keep lint or the compiler off our back */ { struct token_str *p ; int *ip ; s = (char *) 0 ; for ( p = token_str ; p->token ; p++ ) if ( current_token == p->token ) { s = p->str ; break ; } if ( ! s ) /* search the keywords */ s = find_kw_str(current_token) ; if ( s ) { if ( paren_cnt ) for( ip = missing_rparen ; *ip ; ip++) if ( *ip == current_token ) { missing(')', s, token_lineno) ; paren_cnt = 0 ; goto done ; } if ( brace_cnt ) for( ip = missing_rbrace ; *ip ; ip++) if ( *ip == current_token ) { missing('}', s, token_lineno) ; brace_cnt = 0 ; goto done ; } compile_error("syntax error at or near %s", s) ; } else /* special cases */ switch ( current_token ) { case UNEXPECTED : unexpected_char() ; goto done ; case BAD_DECIMAL : compile_error( "syntax error in decimal constant %s", string_buff ) ; break ; case RE : compile_error( "syntax error at or near /%s/", string_buff ) ; break ; default : compile_error("syntax error") ; break ; } return ; done : if ( ++compile_error_count == MAX_COMPILE_ERRORS ) mawk_exit(2) ; } /* generic error message with a hook into the system error messages if errnum > 0 */ void errmsg VA_ALIST2(int , errnum, char *, format) va_list args ; fprintf(stderr, "%s: " , progname) ; VA_START2(args, int, errnum, char *, format) ; vfprintf(stderr, format, args) ; va_end(args) ; if ( errnum > 0 ) fprintf(stderr, " (%s)" , strerror(errnum) ) ; fprintf( stderr, "\n") ; } void compile_error VA_ALIST(char *, format) va_list args ; char *s0, *s1 ; /* with multiple program files put program name in error message */ if ( pfile_name ) { s0 = pfile_name ; s1 = ": " ; } else { s0 = s1 = "" ; } fprintf(stderr, "%s: %s%sline %u: " , progname, s0, s1,token_lineno) ; VA_START(args, char *, format) ; vfprintf(stderr, format, args) ; va_end(args) ; fprintf(stderr, "\n") ; if ( ++compile_error_count == MAX_COMPILE_ERRORS ) mawk_exit(2) ; } void rt_error VA_ALIST( char *, format) va_list args ; fprintf(stderr, "%s: run time error: " , progname ) ; VA_START(args, char *, format) ; vfprintf(stderr, format, args) ; va_end(args) ; putc('\n',stderr) ; rt_where() ; mawk_exit(2) ; } void bozo(s) char *s ; { errmsg(0, "bozo: %s" , s) ; mawk_exit(3) ; } void overflow(s, size) char *s ; unsigned size ; { errmsg(0 , "program limit exceeded: %s size=%u", s, size) ; mawk_exit(2) ; } /* print as much as we know about where a rt error occured */ static void rt_where() { if ( FILENAME->type != C_STRING ) cast1_to_s(FILENAME) ; fprintf(stderr, "\tFILENAME=\"%s\" FNR=%u NR=%u\n", string(FILENAME)->str, rt_fnr, rt_nr) ; } /* run time */ void rt_overflow(s, size) char *s ; unsigned size ; { errmsg(0 , "program limit exceeded: %s size=%u", s, size) ; rt_where() ; mawk_exit(2) ; } void unexpected_char() { int c = yylval.ival ; fprintf(stderr, "%s: %u: ", progname, token_lineno) ; if ( c > ' ' && c < 127 ) fprintf(stderr, "unexpected character '%c'\n" , c) ; else fprintf(stderr, "unexpected character 0x%02x\n" , c) ; } static char *type_to_str( type ) int type ; { char *retval ; switch( type ) { case ST_VAR : retval = "variable" ; break ; case ST_ARRAY : retval = "array" ; break ; case ST_FUNCT : retval = "function" ; break ; case ST_LOCAL_VAR : retval = "local variable" ; break ; case ST_LOCAL_ARRAY : retval = "local array" ; break ; default : bozo("type_to_str") ; } return retval ; } /* emit an error message about a type clash */ void type_error(p) SYMTAB *p ; { compile_error("illegal reference to %s %s", type_to_str(p->type) , p->name) ; } #ifdef NO_VFPRINTF /* a minimal vfprintf */ int simple_vfprintf( fp, format, argp) FILE *fp ; char *format ; va_list argp ; { char *q , *p, *t ; int l_flag ; char xbuff[64] ; q = format ; xbuff[0] = '%' ; while ( *q != 0 ) { if ( *q != '%' ) { putc(*q, fp) ; q++ ; continue ; } /* mark the start with p */ p = ++q ; t = xbuff + 1 ; if ( *q == '-' ) *t++ = *q++ ; while ( scan_code[*(unsigned char*)q] == SC_DIGIT ) *t++ = *q++ ; if ( *q == '.' ) { *t++ = *q++ ; while ( scan_code[*(unsigned char*)q] == SC_DIGIT ) *t++ = *q++ ; } if ( *q == 'l' ) { l_flag = 1 ; *t++ = *q++ ; } else l_flag = 0 ; *t = *q++ ; t[1] = 0 ; switch( *t ) { case 'c' : case 'd' : case 'o' : case 'x' : case 'u' : if ( l_flag ) fprintf(fp, xbuff, va_arg(argp,long) ) ; else fprintf(fp, xbuff, va_arg(argp, int)) ; break ; case 's' : fprintf(fp, xbuff, va_arg(argp, char*)) ; break ; case 'g' : case 'f' : fprintf(fp, xbuff, va_arg(argp, double)) ; break ; default: putc('%', fp) ; q = p ; break ; } } return 0 ; /* shut up */ } #endif /* USE_SIMPLE_VFPRINTF */