/* This file is part of GNU cflow Copyright (C) 1997, 2005, 2007, 2009-2011, 2014 Sergey Poznyakoff GNU cflow is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GNU cflow is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include unsigned char *level_mark; /* Tree level information. level_mark[i] contains 1 if there are more * leafs on the level `i', otherwise it contains 0 */ int level_mark_size = 0; /* Actual size of level_mark */ int level_mark_incr = 128; /* level_mark is expanded by this number of bytes */ int out_line = 1; /* Current output line number */ FILE *outfile; /* Output file */ static void set_level_mark(int lev, int mark) { if (lev >= level_mark_size) { level_mark_size += level_mark_incr; level_mark = xrealloc(level_mark, level_mark_size); } level_mark[lev] = mark; } /* Print current tree level */ void print_level(int lev, int last) { int i; if (print_line_numbers) fprintf(outfile, "%5d ", out_line); if (print_levels) fprintf(outfile, "{%4d} ", lev); fprintf(outfile, "%s", level_begin); for (i = 0; i < lev; i++) fprintf(outfile, "%s", level_indent[ level_mark[i] ]); fprintf(outfile, "%s", level_end[last]); } /* Low level output functions */ struct output_driver { char *name; int (*handler) (cflow_output_command cmd, FILE *outfile, int line, void *data, void *handler_data); void *handler_data; }; static int driver_index; static int driver_max=0; struct output_driver output_driver[MAX_OUTPUT_DRIVERS]; int register_output(const char *name, int (*handler) (cflow_output_command cmd, FILE *outfile, int line, void *data, void *handler_data), void *handler_data) { if (driver_max == MAX_OUTPUT_DRIVERS-1) abort (); output_driver[driver_max].name = strdup(name); output_driver[driver_max].handler = handler; output_driver[driver_max].handler_data = handler_data; return driver_max++; } int select_output_driver(const char *name) { int i; for (i = 0; i < driver_max; i++) if (strcmp(output_driver[i].name, name) == 0) { driver_index = i; return 0; } return -1; } void output_init() { output_driver[driver_index].handler(cflow_output_init, NULL, 0, NULL, output_driver[driver_index].handler_data); } void newline() { output_driver[driver_index].handler(cflow_output_newline, outfile, out_line, NULL, output_driver[driver_index].handler_data); out_line++; } static void begin() { output_driver[driver_index].handler(cflow_output_begin, outfile, out_line, NULL, output_driver[driver_index].handler_data); } static void end() { output_driver[driver_index].handler(cflow_output_end, outfile, out_line, NULL, output_driver[driver_index].handler_data); } static void separator() { output_driver[driver_index].handler(cflow_output_separator, outfile, out_line, NULL, output_driver[driver_index].handler_data); } #if 0 static void print_text(char *buf) { output_driver[driver_index].handler(cflow_output_text, outfile, out_line, buf, output_driver[driver_index].handler_data); } #endif static int print_symbol (int direct, int level, int last, Symbol *sym) { struct output_symbol output_symbol; output_symbol.direct = direct; output_symbol.level = level; output_symbol.last = last; output_symbol.sym = sym; return output_driver[driver_index].handler(cflow_output_symbol, outfile, out_line, &output_symbol, output_driver[driver_index].handler_data); } static int compare(const void *ap, const void *bp) { Symbol * const *a = ap; Symbol * const *b = bp; return strcmp((*a)->name, (*b)->name); } static int is_var(Symbol *symp) { if (include_symbol(symp)) { if (symp->type == SymIdentifier) return symp->storage == ExternStorage || symp->storage == StaticStorage; else return 1; } return 0; } int symbol_is_function(Symbol *symp) { return symp->type == SymIdentifier && symp->arity >= 0; } static void clear_active(Symbol *sym) { sym->active = 0; } /* Cross-reference output */ void print_refs(char *name, struct linked_list *reflist) { Ref *refptr; struct linked_list_entry *p; for (p = linked_list_head(reflist); p; p = p->next) { refptr = (Ref*)p->data; fprintf(outfile, "%s %s:%d\n", name, refptr->source, refptr->line); } } static void print_function(Symbol *symp) { if (symp->source) { fprintf(outfile, "%s * %s:%d %s\n", symp->name, symp->source, symp->def_line, symp->decl); } print_refs(symp->name, symp->ref_line); } static void print_type(Symbol *symp) { if (symp->source) fprintf(outfile, "%s t %s:%d\n", symp->name, symp->source, symp->def_line); } void xref_output() { Symbol **symbols, *symp; size_t i, num; num = collect_symbols(&symbols, is_var, 0); qsort(symbols, num, sizeof(*symbols), compare); /* produce xref output */ for (i = 0; i < num; i++) { symp = symbols[i]; switch (symp->type) { case SymIdentifier: print_function(symp); break; case SymToken: print_type(symp); break; case SymUndefined: break; } } free(symbols); } /* Tree output */ static void set_active(Symbol *sym) { sym->active = out_line; } static int is_printable(struct linked_list_entry *p) { return p != NULL && include_symbol((Symbol*)p->data); } static int is_last(struct linked_list_entry *p) { while ((p = p->next)) if (is_printable(p)) return 0; return 1; } /* Produce direct call tree output */ static void direct_tree(int lev, int last, Symbol *sym) { struct linked_list_entry *p; int rc; if (sym->type == SymUndefined || (max_depth && lev >= max_depth) || !include_symbol(sym)) return; rc = print_symbol(1, lev, last, sym); newline(); if (rc || sym->active) return; set_active(sym); for (p = linked_list_head(sym->callee); p; p = p->next) { set_level_mark(lev+1, !is_last(p)); direct_tree(lev+1, is_last(p), (Symbol*)p->data); } clear_active(sym); } /* Produce reverse call tree output */ static void inverted_tree(int lev, int last, Symbol *sym) { struct linked_list_entry *p; int rc; if (sym->type == SymUndefined || (max_depth && lev >= max_depth) || !include_symbol(sym)) return; rc = print_symbol(0, lev, last, sym); newline(); if (rc || sym->active) return; set_active(sym); for (p = linked_list_head(sym->caller); p; p = p->next) { set_level_mark(lev+1, !is_last(p)); inverted_tree(lev+1, is_last(p), (Symbol*)p->data); } clear_active(sym); } static void tree_output() { Symbol **symbols, *main_sym; size_t i, num; cflow_depmap_t depmap; /* Collect functions and assign them ordinal numbers */ num = collect_functions(&symbols); for (i = 0; i < num; i++) symbols[i]->ord = i; /* Create a dependency matrix */ depmap = depmap_alloc(num); for (i = 0; i < num; i++) { if (symbols[i]->callee) { struct linked_list_entry *p; for (p = linked_list_head(symbols[i]->callee); p; p = p->next) { Symbol *s = (Symbol*) p->data; if (symbol_is_function(s)) depmap_set(depmap, i, ((Symbol*)p->data)->ord); } } } depmap_tc(depmap); /* Mark recursive calls */ for (i = 0; i < num; i++) if (depmap_isset(depmap, i, i)) symbols[i]->recursive = 1; free(depmap); free(symbols); /* Collect and sort all symbols */ num = collect_symbols(&symbols, is_var, 0); qsort(symbols, num, sizeof(*symbols), compare); /* Produce output */ begin(); if (reverse_tree) { for (i = 0; i < num; i++) { inverted_tree(0, 0, symbols[i]); separator(); } } else { main_sym = lookup(start_name); if (main_sym) { direct_tree(0, 0, main_sym); separator(); } else { for (i = 0; i < num; i++) { if (symbols[i]->callee == NULL) continue; direct_tree(0, 0, symbols[i]); separator(); } } } end(); free(symbols); } void output() { if (strcmp(outname, "-") == 0) { outfile = stdout; } else { outfile = fopen(outname, "w"); if (!outfile) error(2, errno, _("cannot open file `%s'"), outname); } set_level_mark(0, 0); if (print_option & PRINT_XREF) { xref_output(); } if (print_option & PRINT_TREE) { tree_output(); } fclose(outfile); }