diff options
Diffstat (limited to 'src/main.c')
-rw-r--r-- | src/main.c | 242 |
1 files changed, 143 insertions, 99 deletions
@@ -1,5 +1,5 @@ /* This file is part of GNU cflow - Copyright (C) 1997-2019 Sergey Poznyakoff + Copyright (C) 1997-2022 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 @@ -22,18 +22,14 @@ #include <parser.h> #include <version-etc.h> -const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">"; -static char doc[] = N_("generate a program flowgraph") -"\v" -N_("* The effect of each option marked with an asterisk is reversed if the option's long name is prefixed with `no-'. For example, --no-cpp cancels --cpp."); +static char doc[] = N_("generate a program flowgraph"); const char *program_authors[] = { "Sergey Poznyakoff", NULL }; enum option_code { - OPT_DEFINES = 256, - OPT_LEVEL_INDENT, + OPT_LEVEL_INDENT = 256, OPT_DEBUG, OPT_PREPROCESS, OPT_NO_PREPROCESS, @@ -51,7 +47,8 @@ enum option_code { OPT_OMIT_ARGUMENTS, OPT_NO_OMIT_ARGUMENTS, OPT_OMIT_SYMBOL_NAMES, - OPT_NO_OMIT_SYMBOL_NAMES + OPT_NO_OMIT_SYMBOL_NAMES, + OPT_TARGET, }; static struct argp_option options[] = { @@ -59,24 +56,21 @@ static struct argp_option options[] = { { NULL, 0, NULL, 0, N_("General options:"), GROUP_ID }, { "depth", 'd', N_("NUMBER"), 0, - N_("Set the depth at which the flowgraph is cut off"), GROUP_ID+1 }, + N_("set the depth at which the flowgraph is cut off"), GROUP_ID+1 }, { "include", 'i', N_("CLASSES"), 0, - N_("Include specified classes of symbols (see below). Prepend CLASSES with ^ or - to exclude them from the output"), GROUP_ID+1 }, + N_("include specified classes of symbols (see below); prepend CLASSES with ^ or - to exclude them from the output"), GROUP_ID+1 }, { "format", 'f', N_("NAME"), 0, - N_("Use given output format NAME. Valid names are `gnu' (default) and `posix'"), + N_("use given output format NAME; valid names are `gnu' (default), `posix', and `dot'"), GROUP_ID+1 }, { "reverse", 'r', NULL, 0, - N_("* Print reverse call tree"), GROUP_ID+1 }, + N_("print reverse call tree"), GROUP_ID+1 }, { "xref", 'x', NULL, 0, - N_("Produce cross-reference listing only"), GROUP_ID+1 }, - { "print", 'P', N_("OPT"), OPTION_HIDDEN, - N_("Set printing option to OPT. Valid OPT values are: xref (or cross-ref), tree. Any unambiguous abbreviation of the above is also accepted"), - GROUP_ID+1 }, + N_("produce cross-reference listing only"), GROUP_ID+1 }, { "output", 'o', N_("FILE"), 0, - N_("Set output file name (default -, meaning stdout)"), + N_("set output file name (default -, meaning stdout)"), GROUP_ID+1 }, - { NULL, 0, NULL, 0, N_("Symbols classes for --include argument"), GROUP_ID+2 }, + { NULL, 0, NULL, 0, N_("Symbol classes for --include argument"), GROUP_ID+2 }, {" x", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, N_("all data symbols, both external and static"), GROUP_ID+3 }, {" _", 0, NULL, OPTION_DOC|OPTION_NO_TRANS, @@ -92,81 +86,90 @@ static struct argp_option options[] = { { NULL, 0, NULL, 0, N_("Parser control:"), GROUP_ID }, { "use-indentation", 'S', NULL, 0, - N_("* Rely on indentation"), GROUP_ID+1 }, - { "no-use-indentation", OPT_NO_USE_INDENTATION, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("rely on indentation"), GROUP_ID+1 }, + { "no-use-indentation", OPT_NO_USE_INDENTATION, NULL, 0, + N_("don't rely on indentation (default)"), GROUP_ID+1 }, { "ansi", 'a', NULL, 0, - N_("* Accept only sources in ANSI C"), GROUP_ID+1 }, - { "no-ansi", OPT_NO_ANSI, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("accept only sources in ANSI C"), GROUP_ID+1 }, + { "no-ansi", OPT_NO_ANSI, NULL, 0, + N_("accept both ANSI and K&R C (default)"), GROUP_ID+1 }, { "pushdown", 'p', N_("NUMBER"), 0, - N_("Set initial token stack size to NUMBER"), GROUP_ID+1 }, + N_("set initial token stack size to NUMBER"), GROUP_ID+1 }, { "symbol", 's', N_("SYMBOL:[=]TYPE"), 0, - N_("Register SYMBOL with given TYPE, or define an alias (if := is used). Valid types are: keyword (or kw), modifier, qualifier, identifier, type, wrapper. Any unambiguous abbreviation of the above is also accepted"), GROUP_ID+1 }, - { "main", 'm', N_("NAME"), 0, - N_("Assume main function to be called NAME"), GROUP_ID+1 }, - { "no-main", OPT_NO_MAIN, NULL, 0, - N_("There's no main function; print graphs for all functions in the program") }, + /* TRANSLATORS: Don't translate type names. */ + N_("register SYMBOL with given TYPE, or define an alias (if := is used); valid types are: keyword (or kw), modifier, qualifier, identifier, type, wrapper, or any unambiguous abbreviation thereof"), GROUP_ID+1 }, { "define", 'D', N_("NAME[=DEFN]"), 0, - N_("Predefine NAME as a macro"), GROUP_ID+1 }, + N_("predefine NAME as a macro"), GROUP_ID+1 }, { "undefine", 'U', N_("NAME"), 0, - N_("Cancel any previous definition of NAME"), GROUP_ID+1 }, + N_("cancel any previous definition of NAME"), GROUP_ID+1 }, { "include-dir", 'I', N_("DIR"), 0, - N_("Add the directory DIR to the list of directories to be searched for header files."), GROUP_ID+1 }, + N_("add the directory DIR to the list of directories to be searched for header files"), GROUP_ID+1 }, { "preprocess", OPT_PREPROCESS, N_("COMMAND"), OPTION_ARG_OPTIONAL, - N_("* Run the specified preprocessor command"), GROUP_ID+1 }, + N_("run the specified preprocessor command"), GROUP_ID+1 }, { "cpp", 0, NULL, OPTION_ALIAS, NULL, GROUP_ID+1 }, - { "no-preprocess", OPT_NO_PREPROCESS, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, - { "no-cpp", 0, NULL, OPTION_ALIAS|OPTION_HIDDEN, NULL, GROUP_ID+1 }, + { "no-preprocess", OPT_NO_PREPROCESS, NULL, 0, + N_("disable preprocessor"), GROUP_ID+1 }, + { "no-cpp", 0, NULL, OPTION_ALIAS, NULL, GROUP_ID+1 }, #undef GROUP_ID #define GROUP_ID 20 { NULL, 0, NULL, 0, N_("Output control:"), GROUP_ID }, { "all", 'A', NULL, 0, - N_("Show all functions, not only those reachable from main"), + N_("show all functions, not only those reachable from main"), GROUP_ID+1 }, { "number", 'n', NULL, 0, - N_("* Print line numbers"), GROUP_ID+1 }, - { "no-number", OPT_NO_NUMBER, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("print line numbers"), GROUP_ID+1 }, + { "no-number", OPT_NO_NUMBER, NULL, 0, + N_("don't print line numbers (default)"), GROUP_ID+1 }, { "print-level", 'l', NULL, 0, - N_("* Print nesting level along with the call tree"), GROUP_ID+1 }, - { "no-print-level", OPT_NO_PRINT_LEVEL, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("print nesting level along with the call tree"), GROUP_ID+1 }, + { "no-print-level", OPT_NO_PRINT_LEVEL, NULL, 0, + N_("don't print nesting levels (default)"), GROUP_ID+1 }, { "level-indent", OPT_LEVEL_INDENT, "ELEMENT", 0, - N_("Control graph appearance"), GROUP_ID+1 }, + N_("control graph appearance; see [1] for details"), GROUP_ID+1 },//FIXME { "tree", 'T', NULL, 0, - N_("* Draw ASCII art tree"), GROUP_ID+1 }, - { "no-tree", OPT_NO_TREE, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("draw ASCII art tree"), GROUP_ID+1 }, + { "no-tree", OPT_NO_TREE, NULL, 0, + N_("don't draw ASCII art tree (default)"), GROUP_ID+1 }, { "brief", 'b', NULL, 0, - N_("* Brief output"), GROUP_ID+1 }, - { "no-brief", OPT_NO_BRIEF, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("brief output"), GROUP_ID+1 }, + { "no-brief", OPT_NO_BRIEF, NULL, 0, + N_("full output (default)"), GROUP_ID+1 }, { "emacs", OPT_EMACS, NULL, 0, - N_("* Additionally format output for use with GNU Emacs"), GROUP_ID+1 }, - { "no-emacs", OPT_NO_EMACS, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("additionally format output for use with GNU Emacs"), GROUP_ID+1 }, + { "no-emacs", OPT_NO_EMACS, NULL, 0, + N_("don't format output for Emacs (default)"), GROUP_ID+1 }, { "omit-arguments", OPT_OMIT_ARGUMENTS, NULL, 0, - N_("* Do not print argument lists in function declarations"), GROUP_ID+1 }, - { "no-ignore-arguments", OPT_NO_OMIT_ARGUMENTS, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("don't print argument lists in function declarations"), GROUP_ID+1 }, + { "no-omit-arguments", OPT_NO_OMIT_ARGUMENTS, NULL, 0, + N_("print argument lists in function declarations (default)"), GROUP_ID+1 }, { "omit-symbol-names", OPT_OMIT_SYMBOL_NAMES, NULL, 0, - N_("* Do not print symbol names in declaration strings"), GROUP_ID+1 }, - { "no-omit-symbol-names", OPT_NO_OMIT_SYMBOL_NAMES, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("don't print symbol names in declaration strings"), GROUP_ID+1 }, + { "no-omit-symbol-names", OPT_NO_OMIT_SYMBOL_NAMES, NULL, 0, + N_("print symbol names in declaration strings (default)"), GROUP_ID+1 }, + { "main", 'm', N_("NAME"), 0, + N_("start graph at this function; multiple options are allowed"), + GROUP_ID+1 }, + { "start", 0, NULL, OPTION_ALIAS, + NULL, + GROUP_ID+1 }, + { "no-main", OPT_NO_MAIN, NULL, 0, + N_("there's no main function; print graphs for all functions in the program") }, + { "target", OPT_TARGET, N_("NAME"), 0, + N_("show only graphs leading from start symbols to this function; multiple options are allowed"), + GROUP_ID+1 }, #undef GROUP_ID #define GROUP_ID 30 { NULL, 0, NULL, 0, N_("Informational options:"), GROUP_ID }, { "verbose", 'v', NULL, 0, - N_("* Verbose error diagnostics"), GROUP_ID+1 }, - { "no-verbose", OPT_NO_VERBOSE, NULL, OPTION_HIDDEN, - "", GROUP_ID+1 }, + N_("verbose error diagnostics"), GROUP_ID+1 }, + { "no-verbose", OPT_NO_VERBOSE, NULL, 0, + N_("disable verbose diagnostics (default)"), GROUP_ID+1 }, { "debug", OPT_DEBUG, "NUMBER", OPTION_ARG_OPTIONAL, - N_("Set debugging level"), GROUP_ID+1 }, + N_("set debugging level"), GROUP_ID+1 }, #undef GROUP_ID + {"help", '?', 0, OPTION_HIDDEN, N_("give this help list"), -1}, { 0, } }; @@ -186,7 +189,6 @@ int use_indentation; /* Rely on indentation, * is necessarily surrounded by the curly braces * in the first column */ -int record_defines; /* Record macro definitions */ int strict_ansi; /* Assume sources to be written in ANSI C */ int print_line_numbers; /* Print line numbers */ int print_levels; /* Print level number near every branch */ @@ -198,6 +200,8 @@ int emacs_option; /* Format and check for use with Emacs cflow-mode */ int omit_arguments_option; /* Omit arguments from function declaration string */ int omit_symbol_names_option; /* Omit symbol name from symbol declaration string */ +static int no_main_option; /* Disable start symbols */ + #define SM_FUNCTIONS 0x0001 #define SM_DATA 0x0002 #define SM_STATIC 0x0004 @@ -212,7 +216,9 @@ int omit_symbol_names_option; /* Omit symbol name from symbol declaration string (c)=='u' ? SM_UNDEFINED : 0) #define SYMBOL_INCLUDE(c) (symbol_map |= CHAR_TO_SM(c)) #define SYMBOL_EXCLUDE(c) (symbol_map &= ~CHAR_TO_SM(c)) -int symbol_map; /* A bitmap of symbols included in the graph. */ +static int symbol_map; /* A bitmap of symbols included in the graph. */ + +int output_visible; char *level_indent[] = { NULL, NULL }; char *level_end[] = { "", "" }; @@ -220,7 +226,6 @@ char *level_begin = ""; int preprocess_option = 0; /* Do they want to preprocess sources? */ -char *start_name = "main"; /* Name of start symbol */ int all_functions; struct linked_list *arglist; /* List of command line arguments */ @@ -235,6 +240,7 @@ find_option_type(struct option_type *optype, const char *str, int len) len = strlen(str); for ( ; optype->str; optype++) { if (len >= optype->min_match && + len <= strlen(optype->str) && memcmp(str, optype->str, len) == 0) { return optype->type; } @@ -270,10 +276,24 @@ symbol_override(const char *str) error(EX_USAGE, 0, _("%s: no symbol type supplied"), str); else { name = strndup(str, ptr - str); + if (!name) + xalloc_die(); if (ptr[1] == '=') { - Symbol *alias = lookup(ptr+2); - if (!alias) { - alias = install(xstrdup(ptr+2), INSTALL_OVERWRITE); + Symbol *alias; + + ptr += 2; + if (strcmp(name, ptr) == 0) { + error(EX_USAGE, 0, _("cyclic alias: %s -> %s"), name, ptr); + } + + alias = lookup(ptr); + if (alias) { + if (strcmp(alias->name, name) == 0) { + error(EX_USAGE, 0, _("cyclic alias: %s -> %s -> %s"), + name, ptr, alias->name); + } + } else { + alias = install(xstrdup(ptr), INSTALL_OVERWRITE); alias->type = SymToken; alias->token_type = 0; alias->source = NULL; @@ -298,27 +318,6 @@ symbol_override(const char *str) } } -/* Args for --print option */ -static struct option_type print_optype[] = { - { "xref", 1, PRINT_XREF }, - { "cross-ref", 1, PRINT_XREF }, - { "tree", 1, PRINT_TREE }, - { 0 }, -}; - -static void -set_print_option(char *str) -{ - int opt; - - opt = find_option_type(print_optype, str, 0); - if (opt == 0) { - error(EX_USAGE, 0, _("unknown print option: %s"), str); - return; - } - print_option |= opt; -} - /* Convert first COUNT bytes of the string pointed to by STR_PTR * to integer using BASE. Move STR_PTR to the point where the * conversion stopped. @@ -511,6 +510,43 @@ add_preproc_option(int key, const char *arg) preprocess_option = 1; } +#define end_of_options(opt) \ + (!(opt)->key && !(opt)->name && !(opt)->doc && !(opt)->group) + +/* Override default argp's --help behavior */ +static void +cflow_help(struct argp_state *state) +{ + struct argp_option *opt; + + /* + * Unshadow the -? option. + * + * By default argp shadows (i.e. removes from the help listing), short + * options that appear more than once. The -? option added in the + * options array above falls into that category. To avoid shadowing it, + * find the option structure and replace its key with 0. + */ + for (opt = (struct argp_option *) state->root_argp->children[0].argp->options; + !end_of_options(opt); opt++) { + if (opt->key == '?') { + opt->key = 0; + break; + } + } + + /* Print the help screen without bug-reporting address. */ + argp_state_help (state, stdout, + ARGP_HELP_STD_HELP & ~(ARGP_HELP_BUG_ADDR|ARGP_HELP_EXIT_OK)); + + printf ("\n%s\n", _("References:")); + printf (" [1] https://www.gnu.org/software/cflow/manual/html_section/ASCII-Tree.html\n"); + + /* Emit bug-reporting address and exit. */ + emit_bug_reporting_address (); + exit (0); +} + static error_t parse_opt (int key, char *arg, struct argp_state *state) { @@ -529,9 +565,6 @@ parse_opt (int key, char *arg, struct argp_state *state) case OPT_DEBUG: debug = arg ? atoi(arg) : 1; break; - case 'P': - set_print_option(arg); - break; case 'S': use_indentation = 1; break; @@ -561,9 +594,6 @@ parse_opt (int key, char *arg, struct argp_state *state) if (max_depth < 0) max_depth = 0; break; - case OPT_DEFINES: /* FIXME: Not used. */ - record_defines = 1; - break; case OPT_EMACS: emacs_option = 1; break; @@ -622,10 +652,11 @@ parse_opt (int key, char *arg, struct argp_state *state) print_levels = 0; break; case 'm': - start_name = strdup(arg); + install_starter(arg); break; case OPT_NO_MAIN: - start_name = NULL; + clear_starters(); + no_main_option = 1; break; case 'n': print_line_numbers = 1; @@ -675,6 +706,12 @@ parse_opt (int key, char *arg, struct argp_state *state) case 'U': add_preproc_option(key, arg); break; + case OPT_TARGET: + install_target(arg); + break; + case '?': + cflow_help(state); + break; default: return ARGP_ERR_UNKNOWN; } @@ -704,6 +741,9 @@ include_symbol(Symbol *sym) if (!sym) return 0; + + if (sym->visible != output_visible) + return 0; if (sym->type == SymIdentifier) { if (sym->name[0] == '_' && !(symbol_map & SM_UNDERSCORE)) @@ -772,6 +812,7 @@ main(int argc, char **argv) register_output("gnu", gnu_output_handler, NULL); register_output("posix", posix_output_handler, NULL); + register_output("dot", dot_output_handler, NULL); symbol_map = SM_FUNCTIONS|SM_STATIC|SM_UNDEFINED; @@ -788,6 +829,9 @@ main(int argc, char **argv) if (argp_parse(&argp, argc, argv, ARGP_IN_ORDER, &index, NULL)) exit(EX_USAGE); + if (!no_main_option) + set_default_starter(); + if (print_option == 0) print_option = PRINT_TREE; |