Logo Search packages:      
Sourcecode: jlint version File versions  Download package

antic.c

/*-< 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 <stdio.h>
#include <string.h>
#include <ctype.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dirent.h>
#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 <TAB SIZE>] [-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;
}     

Generated by  Doxygen 1.6.0   Back to index