/*-< ANTIC.C >------------------------------------------------------*--------*/ /* Jlint Version 1.11.1 (c) 1998 GARRET * ? */ /* (Java Lint) * /\| */ /* * / \ */ /* Created: 28-Apr-1998 K.A. Knizhnik * / [] \ */ /* Last update: 24-Feb-2000 E.G. Parmelan * GARRET */ /*------------------------------------------------------------------*--------*/ /* AntiC - C/C++/Java syntax verifier * */ /*------------------------------------------------------------------*--------*/ /* Edouard G. Parmelan e-mail address: egp@free.fr */ /*------------------------------------------------------------------*--------*/ #include #include #include #ifdef _WIN32 #include #else #include #endif #ifndef MAX_PATH #define MAX_PATH 1024 #endif #define VERSION "1.11.1" char* file_name; FILE* f; int line; int coln; int force_java; int token_line; int token_coln; int java; int fall_thru_cmt; int notreached_cmt; int tab_size = 8; int relax_else = 0; /* Use only lowercase letters without spaces */ const char *fall_thru_cmt_text[] = { "fallthr", "nobreak" }; /* Use only lowercase letters without spaces */ const char *notreached_cmt_text[] = { "notreach", "unreach", }; #define items(a) (sizeof(a)/sizeof(*(a))) #define nobreak enum { TKN_FOR = 257, TKN_IF, TKN_DO, TKN_WHILE, TKN_ELSE, TKN_BREAK, TKN_CASE, TKN_CATCH, TKN_FINALLY, TKN_TRY, TKN_SWITCH, TKN_STR, TKN_IDENT, TKN_CTX, TKN_ACCESS, TKN_AND_OP, TKN_OR_OP, TKN_BIN_OP, TKN_BIT_OP, TKN_SET_OP, TKN_CMP_OP, TKN_EQU_OP, TKN_INC_OP, TKN_SHIFT_OP, TKN_INCLUDE }; typedef enum { ctx_statement, ctx_typedef, ctx_body, ctx_switch } statement_ctx; int get() { int ch = getc(f); if (ch == '\n') { line += 1; coln = 0; } else if (ch == '\t') { coln += tab_size; coln -= coln % tab_size; } else { coln += 1; } return ch; } void unget(int ch) { ungetc(ch, f); if (ch == '\n') { line -= 1; } else if (ch == '\t') { coln -= tab_size; } else { coln -= 1; } } int n_messages; void message_at(int line, int coln, char* msg) { printf("%s:%d:%d: %s\n", file_name, line, coln, msg); n_messages += 1; } void message(char* msg) { message_at(token_line, token_coln, msg); } int check_special_character() { int ch = get(); switch (ch) { case '\n': if (java) { message("Newline within string\n"); } break; case 'n': case 't': case 'b': case 'r': case 'f': case '\\': case '"': case '\'': break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': ch = get(); if (ch >= '0' && ch <= '9') { if (ch > '7') { message("Octal digit expected"); } ch = get(); if (ch >= '0' && ch <= '9') { if (ch > '7') { message("Octal digit expected"); } ch = get(); if (ch >= '0' && ch <= '9') { message("May be more than three octal digits are specified"); } } } return ch; case 'x': if (java) { message("Hexadecimal constant\n"); } ch = get(); if (!isxdigit(ch)) { message("Hexadecimal digit expected\n"); } else while (isxdigit(ch)) { ch = get(); } return ch; case 'u': if (java) { do { ch = get(); } while (ch == 'u'); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (!isxdigit(ch)) { message("Invalid character constant"); } ch = get(); if (isxdigit(ch)) { message("May be more than four hex digits " "are specified for character constant"); } return ch; } nobreak; default: message("May be incorrect escape sequence"); } return get(); } static void check_fall_thru(int ch) { static unsigned index[items(fall_thru_cmt_text)]; unsigned i; if (ch == ' ' || ch == '-') { return; } if (ch == 0) { fall_thru_cmt = 0; for (i = 0; i < items(fall_thru_cmt_text); i++) index[i] = 0; } else if (!fall_thru_cmt) { for (i = 0; i < items(index); i++) { if (tolower(ch) == fall_thru_cmt_text[i][index[i]]) { if (fall_thru_cmt_text[i][++(index[i])] == '\0') { fall_thru_cmt = 1; return; } } else { index[i] = 0; } } } } static void check_notreached(int ch) { static unsigned index[items(notreached_cmt_text)]; unsigned i; if (ch == ' ' || ch == '-') { return; } if (ch == 0) { notreached_cmt = 0; for (i = 0; i < items(notreached_cmt_text); i++) index[i] = 0; } else if (!notreached_cmt) { for (i = 0; i < items(index); i++) { if (tolower(ch) == notreached_cmt_text[i][index[i]]) { if (notreached_cmt_text[i][++(index[i])] == '\0') { notreached_cmt = 1; return; } } else { index[i] = 0; } } } } int scan_0(int cpp) { int ch; unsigned i; char buf[256]; skip_spaces: do { ch = get(); if ((ch == EOF) || (ch == '\n') || (ch == '#')) return ch; } while (isspace(ch)); token_coln = coln; token_line = line; switch (ch) { case '/': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '*') { int nested = 0; ch = get(); while (1) { if (ch == EOF) { message("Unclosed comments"); return EOF; } else if (ch == '/') { ch = get(); if (ch == '*') { if (!nested) message("Nested comments"); nested = 1; } } else if (ch == '*') { ch = get(); if (ch == '/') { goto skip_spaces; } } else { check_fall_thru(ch); check_notreached(ch); ch = get(); } } } else if (ch == '/') { while ((ch = get()) != '\n' && ch != EOF) { check_fall_thru(ch); check_notreached(ch); } return ch; } unget(ch); return TKN_BIN_OP; case '"': ch = get(); while (ch != '"') { switch (ch) { case '\n': message("Newline within string constant"); ch = get(); continue; case EOF: return EOF; case '\\': if (cpp == 2) { ch = get(); } else { ch = check_special_character(); } continue; case '?': if (!java) { ch = get(); if (ch == '?') { ch = get(); if (ch == '=' || ch == '/' || ch == '\'' || ch == '(' || ch == ')' || ch == '!' || ch == '<' || ch == '>') { message("Trigraph sequence inside string"); } } continue; } nobreak; default: ch = get(); } } return TKN_STR; case '\'': ch = get(); if (ch == '\\') { ch = check_special_character(); } else { ch = get(); } if (ch != '\'') { message("Multibyte character constants are not portable"); do { ch = get(); if (ch == '\\') get(); } while ((ch != '\'') && (ch != EOF)); } return TKN_STR; case ':': if (!java) { ch = get(); if (ch == ':') { return TKN_CTX; } else { unget(ch); return ':'; } } break; case '+': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '+') return TKN_INC_OP; unget(ch); return '+'; case '-': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (!java && ch == '>') { return TKN_ACCESS; } else if (ch == '-') return TKN_INC_OP; unget(ch); return '-'; case '*': ch = get(); if (ch == '=') { return TKN_SET_OP; } unget(ch); return '*'; case '%': ch = get(); if (ch == '=') { return TKN_SET_OP; } unget(ch); return TKN_BIN_OP; case '&': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '&') { return TKN_AND_OP; } unget(ch); return '&'; case '|': ch = get(); if (ch == '=') { return TKN_SET_OP; } else if (ch == '|') { return TKN_OR_OP; } unget(ch); return TKN_BIT_OP; case '^': ch = get(); if (ch == '=') { return TKN_SET_OP; } unget(ch); return TKN_BIT_OP; case '>': ch = get(); if (ch == '>') { ch = get(); if (java && ch == '>') { ch = get(); if (ch == '=') return TKN_SET_OP; } else if (ch == '=') return TKN_SET_OP; unget(ch); return TKN_SHIFT_OP; } else if (ch != '=') unget(ch); return TKN_CMP_OP; case '<': ch = get(); if (ch == '<') { ch = get(); if (ch == '=') return TKN_SET_OP; unget(ch); return TKN_SHIFT_OP; } else if (ch != '=') unget(ch); return TKN_CMP_OP; case '=': ch = get(); if (ch == '=') return TKN_EQU_OP; unget(ch); return '='; case '!': ch = get(); if (ch == '=') return TKN_CMP_OP; unget(ch); return '!'; default: if (isalnum(ch) || ch == '_' || ch == '$') { i = 0; do { if (i < sizeof(buf)) { buf[i++] = ch; } ch = get(); } while (isalnum(ch) || ch == '_' || ch == '$'); unget(ch); if (i < sizeof(buf)) { buf[i] = '\0'; if (strcmp(buf, "if") == 0) return TKN_IF; if (strcmp(buf, "for") == 0) return TKN_FOR; if (strcmp(buf, "while") == 0) return TKN_WHILE; if (strcmp(buf, "do") == 0) return TKN_DO; if (strcmp(buf, "else") == 0) return TKN_ELSE; if (strcmp(buf, "default") == 0) return TKN_CASE; if (strcmp(buf, "case") == 0) return TKN_CASE; if (strcmp(buf, "break") == 0) return TKN_BREAK; if (strcmp(buf, "goto") == 0) return TKN_BREAK; if (strcmp(buf, "throw") == 0) return TKN_BREAK; if (strcmp(buf, "return") == 0) return TKN_BREAK; if (strcmp(buf, "continue") == 0) return TKN_BREAK; if (strcmp(buf, "catch") == 0) return TKN_CATCH; if (strcmp(buf, "try") == 0) return TKN_TRY; if (strcmp(buf, "finally") == 0) return TKN_FINALLY; if (strcmp(buf, "switch") == 0) return TKN_SWITCH; if (!java && strcmp(buf, "nobreak") == 0) return TKN_BREAK; if ((cpp == 1) && strcmp(buf, "include") == 0) return TKN_INCLUDE; } if (isdigit((unsigned char)buf[0]) && buf[i-1] == 'l') { message("May be 'l' is used instead of '1' at the end of " "integer constant"); } return TKN_IDENT; } } return ch; } int scan() { int ch; check_fall_thru(0); check_notreached(0); while (1) { ch = scan_0(0); if (ch == '#') { int cpp = 1; do { ch = scan_0(cpp); if (cpp == 1 && ch == TKN_INCLUDE) cpp = 2; if (ch == '\\') scan_0(cpp); } while ((ch != '\n') && (ch != EOF)); } else if (ch == '\\') { message("Unexpected character '\\'"); } else if (ch != '\n') return ch; } } int parse_binary_operation(int* n_bin_ops); int parse_expression() { int lex, prev_op = 0; while (1) { int n_bin_ops; lex = parse_binary_operation(&n_bin_ops); if (lex == '&') lex = TKN_BIT_OP; if (lex == TKN_CMP_OP || lex == TKN_BIT_OP || lex == TKN_AND_OP || lex == TKN_OR_OP || lex == TKN_SET_OP || lex == '=' || lex == TKN_EQU_OP || lex == ',' || lex == '?' || lex == ':') { if (!java && lex == TKN_CMP_OP && prev_op == TKN_CMP_OP) { prev_op = 0; /* may be template */ continue; } if (((lex == TKN_CMP_OP || lex == TKN_EQU_OP) && prev_op == TKN_BIT_OP) || (lex == TKN_BIT_OP && (n_bin_ops != 0 || prev_op == TKN_AND_OP || prev_op == TKN_OR_OP || prev_op == TKN_CMP_OP || prev_op == TKN_EQU_OP)) || ((lex == TKN_AND_OP || lex == TKN_OR_OP) && (prev_op == TKN_BIT_OP || prev_op == '=' || prev_op == TKN_SET_OP)) || ((lex == '=' || lex == TKN_SET_OP) && (prev_op == TKN_AND_OP || prev_op == TKN_OR_OP))) { message("May be wrong assumption about operators priorities"); } else if (lex == TKN_AND_OP && prev_op == TKN_OR_OP) { message("May be wrong assumption about logical " "operators precedence"); } prev_op = lex; } else return lex; } } void parse_block(int* pass_through, statement_ctx ctx); int parse_term() { int lex; while ((lex = scan()) == '&' || lex == '+' || lex == '-' || lex == '*' || lex == '!' || lex == TKN_INC_OP || lex == TKN_CTX || lex == '~'); while (lex == '.' || lex == TKN_INC_OP || lex == TKN_IDENT || lex == TKN_ACCESS || lex == '(' || lex == '[' || lex == TKN_STR || lex == TKN_CTX) { if (lex == '(') { int lpr_line = token_line; int lpr_coln = token_coln; lex = parse_expression(); if (lex != ')') { message_at(lpr_line, lpr_coln, "Unbalanced '('"); return lex; } } else if (lex == '[') { int lbr_line = token_line; int lbr_coln = token_coln; lex = parse_expression(); if (lex != ']') { message_at(lbr_line, lbr_coln, "Unbalanced '['"); return lex; } } lex = scan(); } return lex; } int parse_switch_expression() { int lex = scan(); int lpr_line = token_line; int lpr_coln = token_coln; if (lex != '(') { message_at(lpr_line, lpr_coln, "'(' expected"); return lex; } lex = parse_expression(); if (lex != ')') { message_at(lpr_line, lpr_coln, "Unbalanced '('"); } return scan(); } int parse_binary_operation(int* n_bin_ops) { int lex; int prev_op = 0; *n_bin_ops = 0; while (1) { lex = parse_term(); if (lex == '*' || lex == '-' || lex == '+') lex = TKN_BIN_OP; if (lex == TKN_SHIFT_OP) { if (prev_op == TKN_BIN_OP) { message("May be wrong assumption about shift operator priority"); } } else if (lex == TKN_BIN_OP) { if (prev_op == TKN_SHIFT_OP) { message("May be wrong assumption about shift operator priority"); } } else if (java && lex == '{') { /* anonymous class */ int pass_through = 1; parse_block(&pass_through, ctx_typedef); } else { return lex; } prev_op = lex; *n_bin_ops += 1; } } void parse_conditional_expression(int term_ch) { int bit_op_line = 0; int bit_op_coln = 0; int n_bin_ops; int lex; int expr_line = 0; int expr_coln = 0; int prev_op = 0; if (term_ch == ')') { if (scan() != '(') { message("'(' expected"); return; } expr_line = token_line; expr_coln = token_coln; } while ((lex = parse_binary_operation(&n_bin_ops)) != term_ch) { if (lex == '&') lex = TKN_BIT_OP; if (lex == EOF) { message_at(expr_line, expr_coln, term_ch == ')' ? "Unbalanced parentheses" : "No ')' after FOR condition"); return; } if (lex == '=') { message("May be '=' used instead of '=='"); } else if (lex == TKN_SET_OP) { message("May be skipped parentheses around assign operator"); } else if (lex == TKN_BIT_OP) { if (n_bin_ops != 0 || prev_op == TKN_AND_OP || prev_op == TKN_OR_OP || prev_op == TKN_EQU_OP || prev_op == TKN_CMP_OP) { message("May be wrong assumption about bit operation priority"); } bit_op_line = token_line; bit_op_coln = token_coln; prev_op = TKN_BIT_OP; continue; } else if (lex == TKN_AND_OP && prev_op == TKN_OR_OP) { message("May be wrong assumption about logical operators precedence"); } else if (lex != '?' && lex != ':' && lex != ',' && lex != TKN_CMP_OP && lex != TKN_EQU_OP && lex != TKN_AND_OP && lex != TKN_OR_OP) { message("Syntax error"); } if (prev_op == TKN_BIT_OP) { message_at(bit_op_line, bit_op_coln, "May be wrong assumption about operators priorities"); } prev_op = lex; } } int parse_statement(int lex, int* stmt_line, int* stmt_coln, int* pass_through, int* entry_point, statement_ctx ctx) { int next_stmt_line, next_stmt_coln; int if_coln; int stmt; int if_pass_through, else_pass_through; int statement_with_label = 0; int n_labels_before; int entries; static int n_labels; int miss_equal; classify_statement: miss_equal = 0; switch (lex) { case '{': parse_block(pass_through, ctx); nobreak; case ';': lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; case TKN_FOR: if ((lex = scan()) != '(') { message("No '(' after FOR"); return lex; } lex = parse_expression(); if (lex != ';') { message("No ';' after FOR initialization part"); return lex; } parse_conditional_expression(';'); lex = parse_expression(); if (lex != ')') { message("No ')' after FOR increment part"); return lex; } goto loop_body; case TKN_WHILE: parse_conditional_expression(')'); loop_body: stmt = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; if_pass_through = 0; n_labels_before = n_labels; if (stmt != '{' && next_stmt_coln <= *stmt_coln) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about loop body (semicolon missed)"); } lex = parse_statement(stmt, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); if (if_pass_through) { *pass_through = 1; } if (!java && n_labels != n_labels_before) { *entry_point = 1; } if (stmt != '{' && lex != '}' && next_stmt_coln > *stmt_coln) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about loop body"); } break; case TKN_SWITCH: stmt = parse_switch_expression(); next_stmt_line = token_line; next_stmt_coln = token_coln; if (stmt != '{') { message_at(next_stmt_line, next_stmt_coln, "Suspicios SWITCH without body"); } if_pass_through = 0; lex = parse_statement(stmt, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_switch); *pass_through = 1; break; case TKN_IF: if_coln = *stmt_coln; parse_conditional_expression(')'); stmt = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; else_pass_through = if_pass_through = *pass_through; lex = parse_statement(stmt, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); if (if_pass_through) { *pass_through = 1; } if (lex == TKN_ELSE) { lex = scan(); if (next_stmt_coln < if_coln) { /* handle IF ... ELSE FOO where FOO is aligned with IF */ if (!relax_else || (token_coln < if_coln)) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about ELSE branch " "association"); } } next_stmt_coln = token_coln; if (lex == TKN_IF && next_stmt_line == token_line) { if (if_coln < token_coln) { next_stmt_coln = if_coln; } } next_stmt_line = token_line; lex = parse_statement(lex, &next_stmt_line, &next_stmt_coln, &else_pass_through, &entries, ctx_statement); if (!if_pass_through && !else_pass_through) { *pass_through = 0; } else if (else_pass_through) { *pass_through = 1; } } else if (stmt == ';' || (stmt != '{' && lex != '}' && next_stmt_coln > if_coln)) { message_at(next_stmt_line, next_stmt_coln, "May be wrong assumption about IF body"); } break; case TKN_TRY: lex = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; if_pass_through = *pass_through; else_pass_through = 0; lex = parse_statement(lex, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); else_pass_through |= if_pass_through; while ((lex == TKN_CATCH) || (lex == TKN_FINALLY)) { /* It is really not condition expression, but list of * catch paramters, but is possible to use this function to skip * catch paramter list. */ if (lex == TKN_CATCH) parse_conditional_expression(')'); if_pass_through = *pass_through; lex = parse_statement(scan(), &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); else_pass_through |= if_pass_through; } *pass_through = else_pass_through; break; case TKN_DO: lex = scan(); next_stmt_line = token_line; next_stmt_coln = token_coln; n_labels_before = n_labels; if_pass_through = 0; lex = parse_statement(lex, &next_stmt_line, &next_stmt_coln, &if_pass_through, &entries, ctx_statement); if (if_pass_through) { *pass_through = 1; } if (!java && n_labels != n_labels_before) { *entry_point = 1; } if (lex != TKN_WHILE) { message_at(next_stmt_line, next_stmt_coln, "No WHILE for DO"); } parse_conditional_expression(')'); lex = scan(); if (lex != ';') { message_at(token_line, token_coln, "No ';' after DO WHILE"); } lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; case TKN_CASE: if (ctx != ctx_switch) { message_at(*stmt_line, *stmt_coln, "Suspicious CASE/DEFAULT"); } else if (!statement_with_label && !fall_thru_cmt && *pass_through) { message_at(*stmt_line, *stmt_coln, "Possible miss of BREAK before CASE/DEFAULT"); } while ((lex = scan()) != ':') { if (lex == EOF) { message_at(*stmt_line, *stmt_coln, "No ':' after CASE"); return EOF; } } statement_with_label = 1; *entry_point = 1; *pass_through = 1; lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; case TKN_BREAK: *pass_through = 0; goto parse_statement; case TKN_CATCH: case TKN_FINALLY: message_at(*stmt_line, *stmt_coln, "Suspicious CATCH/FINALLY"); if (lex == TKN_CATCH) parse_conditional_expression(')'); *pass_through = 1; lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; case TKN_IDENT: lex = scan(); if (lex == ':') { n_labels += 1; statement_with_label = 1; *entry_point = 1; *pass_through = 1; lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; } miss_equal = 1; nobreak; parse_statement: default: /* ctx = ctx_typedef; */ while (lex != ';') { switch (lex) { case EOF: message_at(*stmt_line, *stmt_coln, "Statement not terminated by ';'"); return EOF; case '(': lex = parse_expression(); if (lex != ')') { message("')' expected"); return lex; } ctx = ctx_body; miss_equal = 0; break; case '[': lex = parse_expression(); if (lex != ']') { message("']' expected"); return lex; } break; case ')': message("Unbalanced ')' in statement"); lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; case TKN_EQU_OP: if (miss_equal && ctx != ctx_typedef) message("Possible miss of '='"); miss_equal = 0; break; case TKN_SET_OP: case '=': lex = parse_expression(); miss_equal = 0; continue; case '}': *stmt_line = token_line; *stmt_coln = token_coln; return lex; case '{': if_pass_through = 1; parse_block(&if_pass_through, ctx); lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; case TKN_FOR: case TKN_IF: case TKN_DO: case TKN_WHILE: case TKN_CASE: case TKN_TRY: case TKN_SWITCH: message_at(*stmt_line, *stmt_coln, "Possible miss of ';'"); *stmt_line = token_line; *stmt_coln = token_coln; goto classify_statement; } lex = scan(); } lex = scan(); *stmt_line = token_line; *stmt_coln = token_coln; return lex; } *stmt_line = next_stmt_line; *stmt_coln = next_stmt_coln; return lex; } void parse_block(int* pass_through, statement_ctx ctx) { int block_line = token_line; int block_coln = token_coln; int lex = scan(); int stmt_line = token_line; int stmt_coln = token_coln; int reachable = 1; int unreachable = 0; int unreachable_line; int unreachable_coln; int entry_point; while (1) { switch (lex) { case EOF: message_at(block_line, block_coln, "Unbalanced brackets"); return; case '}': if (notreached_cmt) { *pass_through = 1; } return; case TKN_ELSE: message_at(stmt_line, stmt_coln, "ELSE without IF"); lex = scan(); continue; case TKN_FOR: case TKN_IF: case TKN_DO: case TKN_WHILE: case TKN_BREAK: case TKN_CASE: case TKN_CATCH: case TKN_FINALLY: case TKN_TRY: case TKN_SWITCH: if (ctx == ctx_typedef) ctx = ctx_statement; nobreak; default: if (lex == TKN_CASE) reachable = 1; else reachable = *pass_through; unreachable_line = stmt_line; unreachable_coln = stmt_coln; entry_point = 0; lex = parse_statement(lex, &stmt_line, &stmt_coln, pass_through, &entry_point, ctx); if (ctx != ctx_typedef && unreachable && !entry_point) { message_at(unreachable_line, unreachable_coln, "Unreachable statement"); } if (reachable && !*pass_through && !notreached_cmt) { unreachable = 1; } else { unreachable = 0; } } } } void parse_file() { int lex = scan(); while (lex != EOF) { if (lex == '{') { int pass_through = 1; parse_block(&pass_through, ctx_typedef); } else if (lex == '=') { lex = parse_expression(); continue; } lex = scan(); } } int has_suffix(char* str, char* suffix) { int suffix_len = strlen(suffix); int str_len = strlen(str); if (suffix_len > str_len) { return 0; } str += str_len - suffix_len; while (--suffix_len >= 0) { if (tolower(*(unsigned char*)str) != *suffix) { return 0; } suffix += 1; str += 1; } return 1; } void load_file(char* name, int recursive) { #ifdef _WIN32 HANDLE dir; char dir_path[MAX_PATH]; WIN32_FIND_DATA file_data; if (recursive != 0) { sprintf(dir_path, "%s\\*", name); if ((dir=FindFirstFile(dir_path, &file_data)) != INVALID_HANDLE_VALUE) { name = dir_path; } } else { if (strcmp(name, "..") == 0 || strcmp(name, ".") == 0) { load_file(name, 1); return; } if ((dir = FindFirstFile(name, &file_data)) == INVALID_HANDLE_VALUE) { fprintf(stderr, "Failed to locate file '%s'\n", name); return; } } if (dir != INVALID_HANDLE_VALUE) { do { if (!recursive || *file_data.cFileName != '.') { char file_path[MAX_PATH]; char* file_dir = strrchr(name, '\\'); char* file_name_with_path; if (file_dir != NULL) { int dir_len = file_dir - name + 1; memcpy(file_path, name, dir_len); strcpy(file_path+dir_len, file_data.cFileName); file_name_with_path = file_path; } else { file_name_with_path = file_data.cFileName; } load_file(file_name_with_path, recursive+1); } } while (FindNextFile(dir, &file_data)); CloseHandle(dir); return; } #else DIR* dir = opendir(name); if (dir != NULL) { struct dirent* entry; while ((entry = readdir(dir)) != NULL) { if (*entry->d_name != '.') { char full_name[MAX_PATH]; sprintf(full_name, "%s/%s", name, entry->d_name); load_file(full_name, 2); } } closedir(dir); return; } #endif if (recursive < 2 || has_suffix(name, ".java") || has_suffix(name, ".c") || has_suffix(name, ".cpp") || has_suffix(name, ".cxx") || has_suffix(name, ".cc") || has_suffix(name, ".h") || has_suffix(name, ".hpp")) { if ((f = fopen(name, "r")) != NULL) { file_name = name; line = 1; coln = 0; java = force_java || has_suffix(name, ".java"); parse_file(); fclose(f); } else { fprintf(stderr, "Failed to open file '%s'\n", name); } } } void usage() { fprintf(stderr, "Usage: antic [-version] [-java] [-tab ] [-relax-else] file|dir {file|dir...}\n"); } int main(int argc, char* argv[]) { int i; if (argc == 1) { usage(); return 0; } for (i = 1; i < argc; i++) { if (*argv[i] == '-') { if (strcmp(argv[i], "-java") == 0) { force_java = 1; } else if (strcmp(argv[i], "-tab") == 0 && i+1 < argc) { if (sscanf(argv[++i], "%d", &tab_size) != 1) { tab_size = 8; fprintf(stderr, "Bad value '%s' for TAB size parameter\n", argv[i]); } } else if (strcmp(argv[i], "-relax-else") == 0) { relax_else = 1; } else if (strcmp(argv[i], "-version") == 0) { fprintf(stderr, "AntiC - C/C++/Java syntax verifier" ", version %s (" __DATE__ ")\n", VERSION); return 0; } else { fprintf(stderr, "Unrecognized option %s\n", argv[i]); usage(); } } else { load_file(argv[i], 0); } } printf("Verification completed: %d reported messages\n", n_messages); return 0; }