diff options
Diffstat (limited to 'src/cli')
-rw-r--r-- | src/cli/Makefile.am | 49 | ||||
-rw-r--r-- | src/cli/abrt-cli.1 | 58 | ||||
-rw-r--r-- | src/cli/abrt-cli.txt | 75 | ||||
-rw-r--r-- | src/cli/abrt-handle-crashdump.c | 99 | ||||
-rw-r--r-- | src/cli/cli.c | 299 | ||||
-rw-r--r-- | src/cli/report.c | 82 |
6 files changed, 405 insertions, 257 deletions
diff --git a/src/cli/Makefile.am b/src/cli/Makefile.am index d6cd5ef2..bc02236e 100644 --- a/src/cli/Makefile.am +++ b/src/cli/Makefile.am @@ -1,4 +1,8 @@ -bin_PROGRAMS = abrt-cli +-include ../../config.mak + +bin_PROGRAMS = \ + abrt-handle-crashdump \ + abrt-cli abrt_cli_SOURCES = \ cli.c \ @@ -19,10 +23,49 @@ abrt_cli_LDADD = \ ../lib/libabrt_dbus.la \ $(GLIB_LIBS) -man_MANS = abrt-cli.1 -EXTRA_DIST = $(man_MANS) +abrt_handle_crashdump_SOURCES = \ + abrt-handle-crashdump.c +abrt_handle_crashdump_CPPFLAGS = \ + -I$(srcdir)/../include/report -I$(srcdir)/../include \ + -I$(srcdir)/../lib \ + -DBIN_DIR=\"$(bindir)\" \ + -DVAR_RUN=\"$(VAR_RUN)\" \ + -DCONF_DIR=\"$(CONF_DIR)\" \ + -DLOCALSTATEDIR='"$(localstatedir)"' \ + -DDEBUG_DUMPS_DIR=\"$(DEBUG_DUMPS_DIR)\" \ + -DDEBUG_INFO_DIR=\"$(DEBUG_INFO_DIR)\" \ + -DPLUGINS_LIB_DIR=\"$(PLUGINS_LIB_DIR)\" \ + -DPLUGINS_CONF_DIR=\"$(PLUGINS_CONF_DIR)\" \ + -DLIBEXEC_DIR=\"$(LIBEXEC_DIR)\" \ + $(GLIB_CFLAGS) \ + -D_GNU_SOURCE \ + -Wall -Wwrite-strings -Werror +abrt_handle_crashdump_LDADD = \ + ../lib/libreport.la + +MAN_TXT = \ + abrt-cli.txt + +# Manual pages are generated from .txt via Docbook +man1_MANS = ${MAN_TXT:%.txt=%.1} + +%.1 %.5 %.7: %.xml + $(XMLTO_SILENT) xmlto man $< 2>&1 | sed '/Note/d' + +%.xml: %.txt ../../asciidoc.conf + $(ASCIIDOC_SILENT) asciidoc --backend=docbook --doctype=manpage --conf-file ../../asciidoc.conf -aabrt_version=$(PACKAGE_VERSION) -o $@ $< + +CLEANFILES = $(man1_MANS) + +PYTHON_FILES = \ + abrt-action-install-debuginfo.py \ + abrt-action-list-dsos.py \ + abrt-action-analyze-core.py completiondir = $(sysconfdir)/bash_completion.d dist_completion_DATA = abrt-cli.bash DEFS = -DLOCALEDIR=\"$(localedir)\" @DEFS@ + +EXTRA_DIST = $(MAN_TXT) + diff --git a/src/cli/abrt-cli.1 b/src/cli/abrt-cli.1 deleted file mode 100644 index 9e0a630e..00000000 --- a/src/cli/abrt-cli.1 +++ /dev/null @@ -1,58 +0,0 @@ -.TH abrt\-cli "1" "12 Oct 2009" "" -.SH NAME -abrt\-cli \- a command line interface to abrt -.SH SYNOPSIS -.B abrt\-cli -[option] -.SH DESCRIPTION -.I abrt\-cli -is a command line tool that manages application crashes catched by -.I abrtd -daemon. It enables access to problem data, and allows to report -problems depending on active abrt plugins. -.SH OPTIONS -.B Basic startup options -.IP "\-V, \-\-version" -Displays version of abrt\-cli. -.IP "\-?, \-\-help" -Print a help message describing all of abrt-cli’s command-line options. - -.PP -.B Crash action options -.IP "\-l, \-\-list" -Prints list of crashes which are not reported yet. -.IP "\-r, \-\-report \fIDUMPDIR\fR" -Creates a crash report and then the text editor is invoked on that -report. When you are done with editing the report just exit the editor -and then you will be asked if you want to send the report. -.IP "\-d, \-\-delete \fIDUMPDIR\fR" -Removes data about particular crash. -.IP "\-i, \-\-info \fIDUMPDIR\fR" -Prints detailed information about particular crash. - -.PP -.B Listing options -.IP "\-f, \-\-full" -List all crashes, including already reported. - -.PP -.B Report options -.IP "\-y, \-\-always" -Creates and sends the crash report automatically, without asking -any questions. - -.PP -.B Info options -.IP "\-b, \-\-backtrace" -Includes the crash backtrace in the info output if the backtrace is -available. - -.SH ENVIRONMENT VARIABLES -The editor used to edit the crash report is chosen from the -ABRT_EDITOR environment variable, the VISUAL environment variable, or -the EDITOR environment variable, in that order. - -.SH SEE ALSO -.IR abrtd (8), -.IR abrt.conf (5), -.IR abrt-plugins (7) diff --git a/src/cli/abrt-cli.txt b/src/cli/abrt-cli.txt new file mode 100644 index 00000000..ea3111c8 --- /dev/null +++ b/src/cli/abrt-cli.txt @@ -0,0 +1,75 @@ +abrt-cli(1) +=========== + +NAME +---- +abrt-cli - Work with ABRT dump directories from command line. + +SYNOPSIS +-------- +'abrt-cli' [-vsp] -l[f] [-D BASE_DIR]... + +'abrt-cli' [-vsp] -i[f] DUMP_DIR + +'abrt-cli' [-vsp] -L[PREFIX] [DUMP_DIR] + +'abrt-cli' [-vsp] -e EVENT DUMP_DIR + +'abrt-cli' [-vsp] -a[y] DUMP_DIR + +'abrt-cli' [-vsp] -r[y] DUMP_DIR + +'abrt-cli' [-vsp] -d DUMP_DIR + +DESCRIPTION +----------- +'abrt-cli' is a command line tool that manages application crashes and other problems +catched by abrtd daemon. It enables access to, manipulation of problem data, and reporting. + +OPTIONS +------- +-l:: + List not yet reported problems, or all problems with -f + +-D BASE_DIR:: + Directory to list problems from (default: -D $HOME/.abrt/spool -D /var/spool/abrt) + +-i, --info:: + Print information about DUMP_DIR (detailed with -f) + +-L[PREFIX]:: + List possible events [which start with PREFIX] + +-e EVENT:: + Run EVENT on DUMP_DIR + +-a, --analyze:: + Run analyze event(s) on DUMP_DIR + +-r, --report:: + Send a report about DUMP_DIR + +-d, --delete:: + Remove DUMP_DIR + +-f, --full:: + Full listing + +-y, --always:: + Noninteractive: don't ask questions, assume positive answer to all of them + +-v, --verbose:: + Be verbose + +-s:: + Log to syslog + +-p:: + Add program names to log + +-V, --version:: + Display version and exit + +AUTHORS +------- +* ABRT team diff --git a/src/cli/abrt-handle-crashdump.c b/src/cli/abrt-handle-crashdump.c new file mode 100644 index 00000000..d04e4fef --- /dev/null +++ b/src/cli/abrt-handle-crashdump.c @@ -0,0 +1,99 @@ +/* + Copyright (C) 2010 ABRT team + Copyright (C) 2010 RedHat inc. + + This program 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 2 of the License, or + (at your option) any later version. + + This program 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, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "abrtlib.h" +#include "parse_options.h" + +static const char *dump_dir_name = NULL; +//static const char *conf_filename = CONF_DIR"/abrt_event.conf"; +static const char *event; +static const char *pfx = ""; + +static char *do_log(char *log_line, void *param) +{ + log("%s", log_line); + return log_line; +} + +int main(int argc, char **argv) +{ + abrt_init(argv); + + /* Can't keep these strings/structs static: _() doesn't support that */ + const char *program_usage_string = _( + "\b [-vs]" /*" [-c CONFFILE]"*/ " -d DIR -e EVENT\n" + " or: \b [-vs]" /*" [-c CONFFILE]"*/ " [-d DIR] -l[PFX]\n" + "\n" + "Handles dump directory DIR according to rules in abrt_event.conf" + ); + enum { + OPT_v = 1 << 0, + OPT_s = 1 << 1, + OPT_d = 1 << 2, + OPT_e = 1 << 3, + OPT_l = 1 << 4, + OPT_p = 1 << 5, +// OPT_c = 1 << ?, + }; + /* Keep enum above and order of options below in sync! */ + struct options program_options[] = { + OPT__VERBOSE(&g_verbose), + OPT_BOOL( 's', NULL, NULL , _("Log to syslog" )), + OPT_STRING( 'd', NULL, &dump_dir_name, "DIR" , _("Dump directory")), + OPT_STRING( 'e', NULL, &event , "EVENT" , _("Handle EVENT" )), + OPT_OPTSTRING('l', NULL, &pfx , "PFX" , _("List possible events [which start with PFX]")), + OPT_BOOL( 'p', NULL, NULL , _("Add program names to log")), +// OPT_STRING( 'c', NULL, &conf_filename, "CONFFILE", _("Configuration file" )), + OPT_END() + }; + unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); + if (!(opts & (OPT_e|OPT_l))) + show_usage_and_die(program_usage_string, program_options); + + export_abrt_envvars(opts & OPT_p); + + if (opts & OPT_s) + { + openlog(msg_prefix, 0, LOG_DAEMON); + logmode = LOGMODE_SYSLOG; + } + + if (opts & OPT_l) + { + /* Note that dump_dir_name may be NULL here, it means "show all + * possible events regardless of dir" + */ + char *events = list_possible_events(NULL, dump_dir_name, pfx); + if (!events) + return 1; /* error msg is already logged */ + fputs(events, stdout); + free(events); + return 0; + } + + /* -e EVENT: run event */ + + struct run_event_state *run_state = new_run_event_state(); + run_state->logging_callback = do_log; + int r = run_event_on_dir_name(run_state, dump_dir_name ? dump_dir_name : ".", event); + if (r == 0 && run_state->children_count == 0) + error_msg_and_die("No actions are found for event '%s'", event); + free_run_event_state(run_state); + + return r; +} diff --git a/src/cli/cli.c b/src/cli/cli.c index 1a84b1a5..3981c1bc 100644 --- a/src/cli/cli.c +++ b/src/cli/cli.c @@ -120,92 +120,15 @@ static void print_crash_info(problem_data_t *problem_data, bool show_multiline) free(desc); } -/* Program options */ -enum +static char *do_log(char *log_line, void *param) { - OPT_GET_LIST, - OPT_REPORT, - OPT_DELETE, - OPT_INFO -}; - -/** - * Long options. - * Do not use the has_arg field. Arguments are handled after parsing all options. - * The reason is that we want to use all the following combinations: - * --report ID - * --report ID --always - * --report --always ID - */ -static const struct option longopts[] = -{ - /* name, has_arg, flag, val */ - { "help" , no_argument, NULL, '?' }, - { "verbose" , no_argument, NULL, 'v' }, - { "version" , no_argument, NULL, 'V' }, - { "list" , no_argument, NULL, 'l' }, - { "full" , no_argument, NULL, 'f' }, - { "always" , no_argument, NULL, 'y' }, - { "report" , no_argument, NULL, 'r' }, - { "delete" , no_argument, NULL, 'd' }, - { "info" , no_argument, NULL, 'i' }, - { 0, 0, 0, 0 } /* prevents crashes for unknown options*/ -}; - -/* Gets the program name from the first command line argument. */ -static const char *progname(const char *argv0) -{ - const char* name = strrchr(argv0, '/'); - if (name) - return ++name; - return argv0; -} - -/** - * Prints abrt-cli version and some help text. - * Then exits the program with return value 1. - */ -static void print_usage_and_die(char *argv0) -{ - const char *name = progname(argv0); - printf("%s "VERSION"\n\n", name); - - /* Message has embedded tabs. */ - printf(_( - "Usage: %s -l[f] [-D BASE_DIR]...]\n" - " or: %s -r[y] CRASH_DIR\n" - " or: %s -i[b] CRASH_DIR\n" - " or: %s -d CRASH_DIR\n" - "\n" - " -l, --list List not yet reported problems\n" - " -f, --full List all problems\n" - " -D BASE_DIR Directory to list problems from\n" - " (default: -D $HOME/.abrt/spool -D %s)\n" - "\n" - " -r, --report Send a report about CRASH_DIR\n" - " -y, --always ...without editing and asking\n" - " -i, --info Print detailed information about CRASH_DIR\n" - " -f, --full ...including multi-line entries\n" - " Note: -if will run analyzers\n" - " (if this CRASH_DIR have defined analyzers)\n" - " -d, --delete Remove CRASH_DIR\n" - "\n" - " -V, --version Display version and exit\n" - " -v, --verbose Be verbose\n" - ), - name, name, name, name, - DEBUG_DUMPS_DIR - ); - exit(1); + log("%s", log_line); + return log_line; } int main(int argc, char** argv) { - GList *D_list = NULL; - char *dump_dir_name = NULL; - int op = -1; - bool full = false; - bool always = false; + abrt_init(argv); setlocale(LC_ALL, ""); #if ENABLE_NLS @@ -213,41 +136,91 @@ int main(int argc, char** argv) textdomain(PACKAGE); #endif - while (1) - { - /* Do not use colons, arguments are handled after parsing all options. */ - int c = getopt_long(argc, argv, "?Vvrdlfyib", longopts, NULL); + GList *D_list = NULL; + const char *event_name = NULL; + const char *pfx = ""; + + /* Can't keep these strings/structs static: _() doesn't support that */ + const char *program_usage_string = _( + "\b [-vsp] -l[f] [-D BASE_DIR]...\n" + "or: \b [-vsp] -i[f] DUMP_DIR\n" + "or: \b [-vsp] -L[PREFIX] [DUMP_DIR]\n" + "or: \b [-vsp] -e EVENT DUMP_DIR\n" + "or: \b [-vsp] -a[y] DUMP_DIR\n" + "or: \b [-vsp] -r[y] DUMP_DIR\n" + "or: \b [-vsp] -d DUMP_DIR" + ); + enum { + OPT_list = 1 << 0, + OPT_D = 1 << 1, + OPT_info = 1 << 2, + OPT_list_events = 1 << 3, + OPT_run_event = 1 << 4, + OPT_analyze = 1 << 5, + OPT_report = 1 << 6, + OPT_delete = 1 << 7, + OPT_version = 1 << 8, + OPTMASK_op = OPT_list|OPT_info|OPT_list_events|OPT_run_event|OPT_analyze|OPT_report|OPT_delete|OPT_version, + OPTMASK_need_arg = OPT_info|OPT_run_event|OPT_analyze|OPT_report|OPT_delete, + OPT_f = 1 << 9, + OPT_y = 1 << 10, + OPT_v = 1 << 11, + OPT_s = 1 << 12, + OPT_p = 1 << 13, + }; + /* Keep enum above and order of options below in sync! */ + struct options program_options[] = { + /* short_name long_name value parameter_name help */ + OPT_BOOL( 'l', "list" , NULL, _("List not yet reported problems, or all with -f")), + OPT_LIST( 'D', NULL , &D_list, "BASE_DIR", _("Directory to list problems from (default: -D $HOME/.abrt/spool -D "DEBUG_DUMPS_DIR")")), + OPT_BOOL( 'i', "info" , NULL, _("Print information about DUMP_DIR (detailed with -f)")), + OPT_OPTSTRING('L', NULL , &pfx, "PREFIX", _("List possible events [which start with PREFIX]")), + OPT_STRING( 'e', NULL , &event_name, "EVENT", _("Run EVENT on DUMP_DIR")), + OPT_BOOL( 'a', "analyze", NULL, _("Run analyze event(s) on DUMP_DIR")), + OPT_BOOL( 'r', "report" , NULL, _("Send a report about DUMP_DIR")), + OPT_BOOL( 'd', "delete" , NULL, _("Remove DUMP_DIR")), + OPT_BOOL( 'V', "version", NULL, _("Display version and exit")), + OPT_BOOL( 'f', "full" , NULL, _("Full listing")), + OPT_BOOL( 'y', "always" , NULL, _("Noninteractive: don't ask questions, assume 'yes'")), + OPT__VERBOSE(&g_verbose), + OPT_BOOL( 's', NULL , NULL, _("Log to syslog")), + OPT_BOOL( 'p', NULL , NULL, _("Add program names to log")), + OPT_END() + }; + unsigned opts = parse_opts(argc, argv, program_options, program_usage_string); + unsigned op = (opts & OPTMASK_op); + if (!op || ((op-1) & op)) + /* "You must specify exactly one operation" */ + show_usage_and_die(program_usage_string, program_options); + argv += optind; + argc -= optind; + if (argc > 1 + /* dont_need_arg == have_arg? bad in both cases: + * TRUE == TRUE (dont need arg but have) or + * FALSE == FALSE (need arg but havent). + * OPT_list_events is an exception, it can be used in both cases. + */ + || ((op != OPT_list_events) && (!(opts & OPTMASK_need_arg) == argc)) + ) { + show_usage_and_die(program_usage_string, program_options); + } -#define SET_OP(newop) \ - do { \ - if (op != -1 && op != newop) \ - error_msg_and_die(_("You must specify exactly one operation")); \ - op = newop; \ - } while (0) + if (op == OPT_version) + { + printf("%s "VERSION"\n", g_progname); + return 0; + } - switch (c) - { - case -1: goto end_of_arg_parsing; - case 'r': SET_OP(OPT_REPORT); break; - case 'd': SET_OP(OPT_DELETE); break; - case 'l': SET_OP(OPT_GET_LIST); break; - case 'i': SET_OP(OPT_INFO); break; - case 'f': full = true; break; - case 'y': always = true; break; - case 'v': g_verbose++; break; - case 'D': - D_list = g_list_append(D_list, optarg); - break; - case 'V': - printf("%s "VERSION"\n", progname(argv[0])); - return 0; - case '?': - default: /* some error */ - print_usage_and_die(argv[0]); /* exits app */ - } -#undef SET_OP + export_abrt_envvars(opts & OPT_p); + if (opts & OPT_s) + { + openlog(msg_prefix, 0, LOG_DAEMON); + logmode = LOGMODE_SYSLOG; } - end_of_arg_parsing: ; + + char *dump_dir_name = argv[0]; + bool full = (opts & OPT_f); + bool always = (opts & OPT_y); if (!D_list) { @@ -257,31 +230,6 @@ int main(int argc, char** argv) D_list = g_list_append(D_list, (void*)DEBUG_DUMPS_DIR); } - /* Handle option arguments. */ - argc -= optind; - switch (argc) - { - case 0: - if (op == OPT_REPORT || op == OPT_DELETE || op == OPT_INFO) - print_usage_and_die(argv[0]); - break; - case 1: - if (op != OPT_REPORT && op != OPT_DELETE && op != OPT_INFO) - print_usage_and_die(argv[0]); - dump_dir_name = argv[optind]; - break; - default: - print_usage_and_die(argv[0]); - } - - /* Check if we have an operation. - * Limit --full and --always to certain operations. - */ - if ((always && op != OPT_REPORT) || op == -1) - { - print_usage_and_die(argv[0]); - } - /* Get settings */ load_event_config_data(); @@ -289,7 +237,7 @@ int main(int argc, char** argv) int exitcode = 0; switch (op) { - case OPT_GET_LIST: + case OPT_list: { vector_of_problem_data_t *ci = new_vector_of_problem_data(); while (D_list) @@ -302,7 +250,49 @@ int main(int argc, char** argv) free_vector_of_problem_data(ci); break; } - case OPT_REPORT: + case OPT_list_events: /* -L[PREFIX] */ + { + /* Note that dump_dir_name may be NULL here, it means "show all + * possible events regardless of dir" + */ + char *events = list_possible_events(NULL, dump_dir_name, pfx); + if (!events) + return 1; /* error msg is already logged */ + fputs(events, stdout); + free(events); + break; + } + case OPT_run_event: /* -e EVENT: run event */ + { + struct run_event_state *run_state = new_run_event_state(); + run_state->logging_callback = do_log; + int r = run_event_on_dir_name(run_state, dump_dir_name, event_name); + if (r == 0 && run_state->children_count == 0) + error_msg_and_die("No actions are found for event '%s'", event_name); + free_run_event_state(run_state); + break; + } + case OPT_analyze: + { + /* Load problem_data from dump dir */ + struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); + if (!dd) + return 1; + char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze"); + dd_close(dd); + + if (analyze_events_as_lines && *analyze_events_as_lines) + { + GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n'); + char *event = select_event_option(list_analyze_events); + list_free_with_free(list_analyze_events); + exitcode = run_analyze_event(dump_dir_name, event); + free(event); + } + free(analyze_events_as_lines); + break; + } + case OPT_report: { struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); if (!dd) @@ -327,43 +317,18 @@ int main(int argc, char** argv) error_msg_and_die("Crash '%s' not found", dump_dir_name); break; } - case OPT_DELETE: + case OPT_delete: { exitcode = delete_dump_dir_possibly_using_abrtd(dump_dir_name); break; } - case OPT_INFO: + case OPT_info: { /* Load problem_data from dump dir */ struct dump_dir *dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); if (!dd) return -1; - char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze"); - - if (full && analyze_events_as_lines && *analyze_events_as_lines) - { - dd_close(dd); - - GList *list_analyze_events = str_to_glist(analyze_events_as_lines, '\n'); - free(analyze_events_as_lines); - - char *event = select_event_option(list_analyze_events); - list_free_with_free(list_analyze_events); - - int analyzer_result = run_analyze_event(dump_dir_name, event); - free(event); - - if (analyzer_result != 0) - return 1; - - /* Reload problem_data from (possibly updated by analyze) dump dir */ - dd = dd_opendir(dump_dir_name, DD_OPEN_READONLY); - if (!dd) - return -1; - } else - free(analyze_events_as_lines); - problem_data_t *problem_data = create_problem_data_from_dump_dir(dd); dd_close(dd); diff --git a/src/cli/report.c b/src/cli/report.c index 96bf0062..7f722480 100644 --- a/src/cli/report.c +++ b/src/cli/report.c @@ -422,6 +422,31 @@ static bool set_echo(bool enable) return true; } +/* Returns true if the string contains the specified number. */ +static bool is_number_in_string(unsigned number, const char *str) +{ + const char *c; + char numstr[sizeof(int) * 3 + 2]; + int len; + + len = snprintf(numstr, sizeof(numstr), "%u", number); + for (c = str; *c; c++) + { + c = strstr(c, numstr); + if (!c) + /* no such number exists in the string */ + return false; + if ((c == str || !isalnum(c[-1])) && !isalnum(c[len])) + /* found */ + return true; + + /* found, but it's part of another number. Continue + * from the next position. */ + } + + return false; +} + /** * Asks user for missing information */ @@ -584,12 +609,12 @@ char *select_event_option(GList *list_options) if (!list_options) return NULL; - unsigned count = g_list_length(list_options) - 1; - if (!count) - return NULL; + unsigned count = g_list_length(list_options); + if (count == 1) + return xstrdup((char*)list_options->data); - int pos = -1; - fprintf(stdout, _("Select how you would like to analyze the problem:\n")); + int pos = 0; + fprintf(stdout, _("How you would like to analyze the problem?\n")); for (GList *li = list_options; li; li = li->next) { char *opt = (char*)li->data; @@ -605,14 +630,9 @@ char *select_event_option(GList *list_options) unsigned ii; for (ii = 0; ii < 3; ++ii) { - fprintf(stdout, _("Choose option [0 - %u]: "), count); - fflush(NULL); - char answer[16]; - if (!fgets(answer, sizeof(answer), stdin)) - continue; - answer[strlen(answer) - 1] = '\0'; + read_from_stdin(_("Select analyzer: "), answer, sizeof(answer)); if (!*answer) continue; @@ -620,6 +640,7 @@ char *select_event_option(GList *list_options) if (picked > count) { fprintf(stdout, _("You have chosen number out of range")); + fprintf(stdout, "\n"); continue; } @@ -629,7 +650,7 @@ char *select_event_option(GList *list_options) if (ii == 3) error_msg_and_die(_("Invalid input, program exiting...")); - GList *choosen = g_list_nth(list_options, picked); + GList *choosen = g_list_nth(list_options, picked - 1); return xstrdup((char*)choosen->data); } @@ -661,7 +682,7 @@ int report(const char *dump_dir_name, int flags) /* Load problem_data from (possibly updated by analyze) dump dir */ struct dump_dir *dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) - return -1; + return -1; char *analyze_events_as_lines = list_possible_events(dd, NULL, "analyze"); dd_close(dd); @@ -684,7 +705,7 @@ int report(const char *dump_dir_name, int flags) /* Load problem_data from (possibly updated by analyze) dump dir */ dd = dd_opendir(dump_dir_name, /*flags:*/ 0); if (!dd) - return -1; + return -1; char *report_events_as_lines = list_possible_events(dd, NULL, "report"); problem_data_t *problem_data = create_problem_data_from_dump_dir(dd); @@ -744,30 +765,33 @@ int report(const char *dump_dir_name, int flags) else { const char *rating_str = get_problem_item_content_or_NULL(problem_data, FILENAME_RATING); - unsigned rating = rating_str ? xatou(rating_str) : 4; + unsigned i, rating = rating_str ? xatou(rating_str) : 4; + GList *li; + char wanted_reporters[255]; - /* For every reporter, ask if user really wants to report using it. */ - for (GList *li = report_events; li; li = li->next) + puts(_("How would you like to report the problem?")); + /* Print list of reporters and ask the user which should be used. */ + for (li = report_events, i = 1; li; li = li->next, i++) { char *reporter_name = (char *) li->data; event_config_t *config = get_event_config(reporter_name); - char question[255]; - char *show_reporter_name; - if (config) - show_reporter_name = (config->screen_name) ? config->screen_name : reporter_name; - else - show_reporter_name = reporter_name; - snprintf(question, sizeof(question), _("Report using %s?"), show_reporter_name); + printf(" %d) %s\n", i, (config && config->screen_name) ? config->screen_name : reporter_name); + } + + read_from_stdin(_("Select reporter(s): "), wanted_reporters, sizeof(wanted_reporters)); + + for (li = report_events, i = 1; li; li = li->next, i++) + { + char *reporter_name = (char *) li->data; + event_config_t *config = get_event_config(reporter_name); if (!config) VERB1 log("No configuration file found for '%s' reporter", reporter_name); - - if (!ask_yesno(question)) - { - puts(_("Skipping...")); + + /* Was this reporter requested? */ + if (!is_number_in_string(i, wanted_reporters)) continue; - } /* TODO: npajkovs; not implemented yet */ //const char *rating_required = get_map_string_item_or_NULL(single_plugin_settings, "RatingRequired"); |