/* * Simple 'C' cross reference listing generator * * This program generates a line-numbered listing of a 'C' program, with * A cross reference table of all global symbols showing their definition, * and any references to them. * * The program does minimal parsing of the 'C' code, and as such has the * following limitations: * * - The first time a symbol is encountered outside of a function definition * is considered to be its definition. This is usually the case, however for * symbols #defined in external header files etc, which are used in the * definition of other global entities, the first use is considered the * definition. * * - Local symbols having names which are identical to a global symbol are * reported as references. * * - Symbols not defined in the file which don't have an explict "extern" * declaration are not reported. To track library functions etc. which * fall into this catagory, place an "extern int func()" declaration at * the start of the file. * * Copyright 1993-1995 Dave Dunfield * All rights reserved. * * Permission granted for personal (non-commercial) use only. * * Compile command: cc ccref -fop */ #include #define SYMBOLS 500 /* Maximum number of symbols */ #define SYMBOL_POOL 5000 /* String space for symbol storage */ #define REFERENCES 10000 /* Maximum number of references */ /* Symbol table and associated management variables */ char symbol_pool[SYMBOL_POOL], *symbol_names[SYMBOLS]; int references[REFERENCES], ref_list[SYMBOLS], pool_top = 0, name_top = 0, ref_top = 0; /* Command line switch variables */ char list = -1, filename[66]; int page_width = 79, page_length = 60, tab_size = 4; /* Misc. housekeeping variables */ char in_string = 0, in_comment = 0, *optr, pass = 0; int bracket = 0, line_number, page_number = 0, pcount = 9999; /* Table of MICRO-C reserved words */ char *reserved_words[] = { "int", "unsigned", "char", "static", "extern", "register", "struct", "union", "if", "else", "while", "do", "for", "switch", "case", "default", "return", "break", "continue", "goto", "sizeof", "asm", "#define", "#ifdef", "#ifndef", "#else", "#endif", "#undef", "#forget", 0 }; /* end of table */ /* * Main program */ main(int argc, char *argv[]) { int i, j, k, l; char buffer[200], symbol[50], *sptr, c, last_def; FILE *fp; if(argc < 2) abort("\nUse: CCREF [-list p=page_length t=tab_size w=page_width]\n\nCopyright 1993-1995 Dave Dunfield\nAll rights reserved.\n"); sptr = argv[1]; i = j = 0; do { if((filename[i++] = c = toupper(*sptr++)) == '.') j = -1; } while(c); if(!j) { filename[i-1] = '.'; filename[i] = 'C'; filename[i+1] = 0; } fp = fopen(filename, "rvq"); for(i=2; i < argc; ++i) { sptr = argv[i]; switch((toupper(*sptr++) << 8) | toupper(*sptr++)) { case 'W=' : /* Specify page width */ page_width = atoi(sptr); break; case 'T=' : /* Specify tab size */ tab_size = atoi(sptr); break; case 'P=' : /* Specify page length */ page_length = atoi(sptr); break; case '-L' : /* Inhibit line numbered listing */ list = 0; break; default: printf("Invalid argument: %s\n", argv[i]); exit(-1); } } again: line_number = 0; while(fgets(optr = buffer, sizeof(buffer)-1, fp)) { ++line_number; if(list && !pass) { title("Program Listing"); printf("%5u ", line_number); i = 0; while(c = *optr++) { if(c == '\t') { /* tab */ if((i + tab_size) > (page_width - 6)) { putc('\n', stdout); title("Program Listing"); printf(" "); i = 0; } do putc(' ', stdout); while(++i % tab_size); } else { if(++i > (page_width - 6)) { putc('\n', stdout); title("Program Listing"); printf(" "); i = 0; } putc(c, stdout); } } putc('\n', stdout); optr = buffer; } if(strbeg(buffer, "#include")) continue; do { if(in_string) switch(c = *optr) { default: if(c == in_string) in_string = 0; continue;; case '\\' : ++optr; continue; } if(in_comment) switch(c = *optr) { case '/' : if(*(optr+1) == '*') { ++optr; ++in_comment; } continue; case '*' : if(*(optr+1) == '/') { ++optr; --in_comment; } default: continue; } switch(c = *optr) { case '{' : ++bracket; continue; case '}' : --bracket; continue; case '/' : if(*(optr+1) == '*') { in_comment = 1; ++optr; } continue; case '(' : if(last_def == 1) { in_string = '{'; ++bracket; } continue; case '\'' : case '\"' : in_string = c; continue; case ' ' : case '\t' : continue; } last_def = 0; if(issymbol(c)) { sptr = symbol; while(issymbol(*optr) || isdigit(*optr)) *sptr++ = *optr++; *sptr = 0; --optr; for(i=0; sptr = reserved_words[i]; ++i) if(!strcmp(symbol, sptr)) goto skip_symbol; if(bracket) { if(pass) reference(symbol); } else { if(!pass) define(symbol); last_def = 1; } skip_symbol: } } while(*optr++); } if(!pass) { pass = 1; rewind(fp); ref_list[name_top] = ref_top; goto again; } fclose(fp); pcount = 9999; title("Cross Reference"); printf(" Symbol Def References\n"); title("Cross Reference"); printf("--------------- ----- "); for(l=22; l < page_width; ++l) putc('-', stdout); putc('\n', stdout); for(;;) { i = 0; while(!symbol_names[i]) if(++i >= name_top) return; for(j=i+1; j < name_top; ++j) if((sptr = symbol_names[j]) && (strcmp(sptr, symbol_names[i]) < 0)) i = j; title("Cross Reference"); printf("%-15s %-5u", symbol_names[i], references[j = ref_list[i]]); k = ref_list[i+1]-1; l = 0; while(j < k) { if((l += 6) > (page_width-21)) { l = 6; printf("\n%-21s", ""); } printf(" %5u", references[++j]); } putc('\n', stdout); symbol_names[i] = 0; } } /* * Test for valid 'C' symbol character. * Also allow '#', so that we can detect '#' directives. This should * not conflict with 'C' source since '#' is otherwise not permitted. */ issymbol(int c) { return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || (c == '_') || (c == '#'); } /* * Lookup a symbol in the symbol table */ lookup(char *symbol) { int i; for(i=0; i < name_top; ++i) if(!strcmp(symbol_names[i], symbol)) return i; return -1; } /* * Log a symbol reference */ reference(char *symbol) { int s, i, j; /* Check that symbol defined */ if((s = lookup(symbol)) == -1) return; /* Check that line not already reported */ j = ref_list[s+1]; for(i=ref_list[s]+1; i < j; ++i) if(references[i] == line_number) return; /* Expand reference list to include new number */ if(ref_top >= REFERENCES) error("Reference table full"); for(i = ref_top++; i > j; --i) references[i] = references[i-1]; /* Adjust other symbols */ for(i=s+1; i <= name_top; ++i) ++ref_list[i]; references[j] = line_number; } /* * Define a new symbol */ define(char *symbol) { if(lookup(symbol) != -1) { reference(symbol); return; } if(name_top >= SYMBOLS) error("Symbol table full"); symbol_names[name_top] = &symbol_pool[pool_top]; ref_list[name_top++] = ref_top; do { if(pool_top >= SYMBOL_POOL) error("Symbol name space exausted"); symbol_pool[pool_top++] = *symbol; } while(*symbol++); references[ref_top++] = line_number; } /* * Write title for page */ title(char *string) { if(++pcount >= page_length) { if(page_number) putc('\f', stdout); printf("%-30s %-30s Page: %u\n\n", filename, string, ++page_number); pcount = 2; } } /* * Report an error in the file */ error(char *string) { fprintf(stdout, "Error in line %u : %s\n", line_number, string); exit(-1); }