ltlcross: Add a --color option.

* src/bin/ltlcross.cc: Add a --color option.
* NEWS: Mention it.
This commit is contained in:
Alexandre Duret-Lutz 2013-05-17 14:04:09 +02:00
parent 35129e5a5a
commit 5b0bf8ef09
2 changed files with 88 additions and 19 deletions

3
NEWS
View file

@ -3,6 +3,9 @@ New in spot 1.1.4a (not relased)
* All the parsers implemented in Spot now use the same type * All the parsers implemented in Spot now use the same type
to store locations. to store locations.
* ltlcross has a new option --color to color its output. It is enabled
by default when the output is a terminal.
* Cleanup of exported symbols * Cleanup of exported symbols
All symbols in the library now have hidden visibility on ELF systems. All symbols in the library now have hidden visibility on ELF systems.

View file

@ -32,6 +32,7 @@
#include <sys/wait.h> #include <sys/wait.h>
#include "error.h" #include "error.h"
#include "gethrxtime.h" #include "gethrxtime.h"
#include "argmatch.h"
#include "common_setup.hh" #include "common_setup.hh"
#include "common_cout.hh" #include "common_cout.hh"
@ -85,6 +86,7 @@ Exit status:\n\
#define OPT_STOP_ERR 7 #define OPT_STOP_ERR 7
#define OPT_SEED 8 #define OPT_SEED 8
#define OPT_PRODUCTS 9 #define OPT_PRODUCTS 9
#define OPT_COLOR 10
static const argp_option options[] = static const argp_option options[] =
{ {
@ -138,6 +140,10 @@ static const argp_option options[] =
"output statistics as CSV in FILENAME or on standard output", 0 }, "output statistics as CSV in FILENAME or on standard output", 0 },
/**************************************************/ /**************************************************/
{ 0, 0, 0, 0, "Miscellaneous options:", -1 }, { 0, 0, 0, 0, "Miscellaneous options:", -1 },
{ "color", OPT_COLOR, "WHEN", OPTION_ARG_OPTIONAL,
"colorize output; WHEN can be 'never', 'always' (the default if "
"--color is used without argument), or "
"'auto' (the default if --color is not used)", 0 },
{ 0, 0, 0, 0, 0, 0 } { 0, 0, 0, 0, 0, 0 }
}; };
@ -148,6 +154,28 @@ const struct argp_child children[] =
{ 0, 0, 0, 0 } { 0, 0, 0, 0 }
}; };
enum color_type { color_never, color_always, color_if_tty };
static char const *const color_args[] =
{
"always", "yes", "force",
"never", "no", "none",
"auto", "tty", "if-tty", 0
};
static color_type const color_types[] =
{
color_always, color_always, color_always,
color_never, color_never, color_never,
color_if_tty, color_if_tty, color_if_tty
};
ARGMATCH_VERIFY(color_args, color_types);
color_type color_opt = color_if_tty;
const char* bright_red = "\033[01;31m";
const char* bright_white = "\033[01;37m";
const char* reset_color = "\033[m";
unsigned states = 200; unsigned states = 200;
float density = 0.1; float density = 0.1;
unsigned timeout = 0; unsigned timeout = 0;
@ -167,9 +195,19 @@ static std::ostream&
global_error() global_error()
{ {
global_error_flag = true; global_error_flag = true;
if (color_opt)
std::cerr << bright_red;
return std::cerr; return std::cerr;
} }
static void
end_error()
{
if (color_opt)
std::cerr << reset_color;
}
struct statistics struct statistics
{ {
statistics() statistics()
@ -334,6 +372,14 @@ parse_opt(int key, char* arg, struct argp_state*)
<< "on your platform" << std::endl; << "on your platform" << std::endl;
#endif #endif
break; break;
case OPT_COLOR:
{
if (arg)
color_opt = XARGMATCH("--color", arg, color_args, color_types);
else
color_opt = color_always;
break;
}
case OPT_CSV: case OPT_CSV:
want_stats = true; want_stats = true;
csv_output = arg ? arg : "-"; csv_output = arg ? arg : "-";
@ -658,11 +704,13 @@ namespace
{ {
global_error() << "error: execution terminated by signal " global_error() << "error: execution terminated by signal "
<< WTERMSIG(es) << ".\n"; << WTERMSIG(es) << ".\n";
end_error();
} }
else if (WIFEXITED(es) && WEXITSTATUS(es) != 0) else if (WIFEXITED(es) && WEXITSTATUS(es) != 0)
{ {
global_error() << "error: execution returned exit code " global_error() << "error: execution returned exit code "
<< WEXITSTATUS(es) << ".\n"; << WEXITSTATUS(es) << ".\n";
end_error();
} }
else else
{ {
@ -677,6 +725,7 @@ namespace
std::ostream& err = global_error(); std::ostream& err = global_error();
err << "error: failed to parse the produced neverclaim.\n"; err << "error: failed to parse the produced neverclaim.\n";
spot::format_neverclaim_parse_errors(err, output, pel); spot::format_neverclaim_parse_errors(err, output, pel);
end_error();
delete res; delete res;
res = 0; res = 0;
} }
@ -690,15 +739,18 @@ namespace
{ {
global_error() << "Cannot open " << output.val() global_error() << "Cannot open " << output.val()
<< std::endl; << std::endl;
global_error_flag = true; end_error();
} }
else else
{ {
res = spot::lbtt_parse(f, error, &dict); res = spot::lbtt_parse(f, error, &dict);
if (!res) if (!res)
global_error() << ("error: failed to parse output in " {
"LBTT format: ") global_error() << ("error: failed to parse output in "
<< error << std::endl; "LBTT format: ")
<< error << std::endl;
end_error();
}
} }
break; break;
} }
@ -808,6 +860,7 @@ namespace
err << "state-space #" << p << "/" << products << "\n"; err << "state-space #" << p << "/" << products << "\n";
else else
err << "the state-space\n"; err << "the state-space\n";
end_error();
} }
} }
@ -905,7 +958,11 @@ namespace
std::cerr << linenum << ":"; std::cerr << linenum << ":";
if (filename || linenum) if (filename || linenum)
std::cerr << " "; std::cerr << " ";
if (color_opt)
std::cerr << bright_white;
std::cerr << fstr << "\n"; std::cerr << fstr << "\n";
if (color_opt)
std::cerr << reset_color;
// Make sure we do not translate the same formula twice. // Make sure we do not translate the same formula twice.
if (!allow_dups) if (!allow_dups)
@ -987,8 +1044,11 @@ namespace
spot::tgba_product* prod = spot::tgba_product* prod =
new spot::tgba_product(pos[i], neg[j]); new spot::tgba_product(pos[i], neg[j]);
if (!is_empty(prod)) if (!is_empty(prod))
global_error() << "error: P" << i << "*N" << j {
<< " is nonempty\n"; global_error() << "error: P" << i << "*N" << j
<< " is nonempty\n";
end_error();
}
delete prod; delete prod;
} }
} }
@ -1065,13 +1125,15 @@ namespace
if (pos_map[i] && neg_map[i] && if (pos_map[i] && neg_map[i] &&
!(consistency_check(pos_map[i], neg_map[i], statespace))) !(consistency_check(pos_map[i], neg_map[i], statespace)))
{ {
global_error() << "error: inconsistency between P" << i std::ostream& err = global_error();
<< " and N" << i; err << "error: inconsistency between P" << i
<< " and N" << i;
if (products > 1) if (products > 1)
global_error() << " for state-space #" << p err << " for state-space #" << p
<< "/" << products << "\n"; << "/" << products << "\n";
else else
global_error() << "\n"; err << "\n";
end_error();
} }
} }
@ -1230,6 +1292,9 @@ main(int argc, char** argv)
error(2, 0, "No translator to run? Run '%s --help' for usage.", error(2, 0, "No translator to run? Run '%s --help' for usage.",
program_name); program_name);
if (color_opt == color_if_tty)
color_opt = isatty(STDERR_FILENO) ? color_always : color_never;
setup_sig_handler(); setup_sig_handler();
processor p; processor p;
@ -1244,16 +1309,17 @@ main(int argc, char** argv)
{ {
if (global_error_flag) if (global_error_flag)
{ {
std::cerr std::ostream& err = global_error();
<< ("error: some error was detected during the above runs,\n" err << ("error: some error was detected during the above runs,\n"
" please search for 'error:' messages in the above " " please search for 'error:' messages in the above "
"trace.") "trace.")
<< std::endl; << std::endl;
if (timeout_count == 1) if (timeout_count == 1)
std::cerr << "Additionally, 1 timeout occurred." << std::endl; err << "Additionally, 1 timeout occurred." << std::endl;
else if (timeout_count > 1) else if (timeout_count > 1)
std::cerr << "Additionally, " err << "Additionally, "
<< timeout_count << " timeouts occurred." << std::endl; << timeout_count << " timeouts occurred." << std::endl;
end_error();
} }
else if (timeout_count == 0) else if (timeout_count == 0)
std::cerr << "No problem detected." << std::endl; std::cerr << "No problem detected." << std::endl;