blob: dc4abda8d135220ff0c145520e4c86ae3021f552 [file] [log] [blame]
/* Main entry point for dwarflint, a pedantic checker for DWARF files.
Copyright (C) 2008,2009,2010,2011 Red Hat, Inc.
This file is part of elfutils.
This file 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.
elfutils 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 <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <libintl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <iostream>
#include <sstream>
#include "dwarflint.hh"
#include "readctx.hh"
#include "checks.hh"
#include "option.hh"
#include "messages.hh"
/* Messages that are accepted (and made into warning). */
struct message_criteria warning_criteria;
/* Accepted (warning) messages, that are turned into errors. */
struct message_criteria error_criteria;
struct check_option_t
: public global_opt<option_common>
{
struct initial_checkrules
: public checkrules
{
initial_checkrules ()
{
push_back (checkrule_internal ("@all", checkrule::request));
push_back (checkrule_internal ("@nodefault", checkrule::forbid));
}
} rules;
check_option_t ()
: global_opt<option_common> ("Only run selected checks.",
"[+-][@]name,...", "check", 0)
{}
error_t parse_opt (char *arg, __attribute__ ((unused)) argp_state *state)
{
static bool first = true;
std::stringstream ss (arg);
std::string item;
while (std::getline (ss, item, ','))
{
if (item.empty ())
continue;
enum
{
forbid,
request,
replace
} act;
// If the first rule has no operator, we assume the user
// wants to replace the implicit set of checks.
if (first)
{
act = replace;
first = false;
}
else
// Otherwise the rules are implicitly requesting, even
// without the '+' operator.
act = request;
bool minus = item[0] == '-';
bool plus = item[0] == '+';
if (plus || minus)
item = item.substr (1);
if (plus)
act = request;
if (minus)
act = forbid;
if (act == replace)
{
rules.clear ();
act = request;
}
checkrule::action_t action
= act == request ? checkrule::request : checkrule::forbid;
rules.push_back (checkrule (item, action));
}
return 0;
}
} check_option;
global_opt<void_option>
be_quiet ("Do not print anything if successful",
"quiet", 'q');
global_opt<string_option>
opt_list_checks ("List all the available checks.",
"full", "list-checks", 0,
OPTION_ARG_OPTIONAL);
// xxx The following three should go away when we introduce the
// message filtering. Or should be preserved, but in a way that makes
// more sense, right now they are simply a misnomer.
global_opt<void_option>
ignore_bloat ("Ignore messages related to bloat.", "ignore-bloat");
global_opt<void_option>
be_strict ("Be somewhat stricter.", "strict");
global_opt<void_option>
be_tolerant ("Be somewhat more tolerant.", "tolerant");
int
main (int argc, char *argv[])
{
/* Set locale. */
setlocale (LC_ALL, "");
/* Initialize the message catalog. */
textdomain (PACKAGE_TARNAME);
/* Parse and process arguments. */
argppp argp (global_opts (),
dwarflint::main_registrar ()->get_descriptors ());
int remaining;
argp.parse (argc, argv, 0, &remaining);
if (opt_list_checks.seen ())
{
dwarflint::list_checks ();
std::exit (0);
}
else if (remaining == argc)
{
fputs (gettext ("Missing file name.\n"), stderr);
argp.help (stderr, ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR,
program_invocation_short_name);
std::exit (1);
}
/* Initialize warning & error criteria. */
warning_criteria |= message_term (mc_none, mc_none);
error_criteria |= message_term (mc_impact_4, mc_none);
error_criteria |= message_term (mc_error, mc_none);
/* Configure warning & error criteria according to configuration. */
if (ignore_bloat)
warning_criteria &= message_term (mc_none, mc_acc_bloat);
if (!be_strict)
{
warning_criteria &= message_term (mc_none, mc_strings);
warning_criteria.and_not (mc_line | mc_acc_bloat);
warning_criteria &= message_term (mc_none, mc_pubtypes);
}
if (be_tolerant)
{
warning_criteria &= message_term (mc_none, mc_loc);
warning_criteria &= message_term (mc_none, mc_ranges);
}
if (false) // for debugging
{
std::cout << "warning criteria: " << warning_criteria << std::endl;
std::cout << "error criteria: " << error_criteria << std::endl;
}
/* Before we start tell the ELF library which version we are using. */
elf_version (EV_CURRENT);
/* Now process all the files given at the command line. */
bool only_one = remaining + 1 == argc;
bool one_passed = false;
do
{
try
{
char const *fname = argv[remaining];
if (!only_one)
std::cout << std::endl << fname << ":" << std::endl;
wr_reset_counters ();
dwarflint lint (fname, check_option.rules);
one_passed = true;
if (error_count == 0 && !be_quiet)
puts (gettext ("No errors"));
}
catch (std::runtime_error &e)
{
wr_error () << e.what () << std::endl;
continue;
}
}
while (++remaining < argc);
if (one_passed)
for (checkrules::const_iterator it = check_option.rules.begin ();
it != check_option.rules.end (); ++it)
if (!it->used ())
std::cerr << "warning: the rule `" << it->name ()
<< "' never matched." << std::endl;
return error_count != 0;
}