Files
mercury/trace/mercury_trace_cmd_browsing.c
Zoltan Somogyi 0de9ea32c7 Let mdb's dump command dump just a part of a variable' value.
trace/mercury_trace_cmd_browsing.c:
    Change the dump command to allow the name of the variable
    whose value is to be dumped to be followed by a path,
    specifying the *part* of the variable's value to be printed.

trace/mercury_trace_vars.[ch]:
    To make that possible, provide a version of an existing function
    that returns an extra value that the new code in
    mercury_trace_cmd_browsing.c needs, and another extra value
    that conceptually belongs with it.

    Mark strange code with an XXX.

NEWS.md:
    Announce both this new capability and a just-now contributes predicate.

library/pqueue.m:
    Make the just-now contributed code more readable.

tests/debugger/browser_test.{m,inp,exp3}:
    Extend this test case to test the new functionality.
2025-07-26 19:36:21 +02:00

1662 lines
53 KiB
C

// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 1998-2008,2010,2012 The University of Melbourne.
// Copyright (C) 2017-2018, 2020, 2025 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// This module implements the mdb commands in the "browsing" category.
//
// The structure of these files is:
//
// - all the #includes
// - local macros and declarations of local static functions
// - one function for each command in the category
// - any auxiliary functions
// - any command argument strings
// - option processing functions.
#include "mercury_std.h"
#include "mercury_getopt.h"
#include "mercury_string.h"
#include "mercury_trace_internal.h"
#include "mercury_trace_cmds.h"
#include "mercury_trace_cmd_browsing.h"
#include "mercury_trace_cmd_parameter.h"
#include "mercury_trace_vars.h"
#include "mercury_trace_hold_vars.h"
#include "mercury_trace_browse.h"
#include "mercury_trace_util.h"
#include "mdb.listing.mh"
#include "mdb.diff.mh"
#include "mdb.declarative_execution.mh"
#include "mdbcomp.program_representation.mh"
////////////////////////////////////////////////////////////////////////////
static void MR_trace_set_level_and_report(int ancestor_level,
MR_bool detailed, MR_bool print_optionals);
static const char *MR_trace_browse_exception(MR_EventInfo *event_info,
MR_Browser browser, MR_BrowseCallerType caller,
MR_BrowseFormat format);
static const char *MR_trace_browse_proc_body(MR_EventInfo *event_info,
MR_Browser browser, MR_BrowseCallerType caller,
MR_BrowseFormat format);
// Functions to invoke the user's web browser on terms or goals.
static void MR_trace_browse_web(MR_Word type_info, MR_Word value,
MR_BrowseCallerType caller, MR_BrowseFormat format);
static void MR_trace_browse_goal_web(MR_ConstString name,
MR_Word arg_list, MR_Word is_func,
MR_BrowseCallerType caller, MR_BrowseFormat format);
static void MR_trace_cmd_stack_2(MR_EventInfo *event_info,
MR_bool detailed, MR_FrameLimit frame_limit,
int line_limit);
static const char *MR_trace_new_source_window(const char *window_cmd,
const char *server_cmd, const char *server_name,
int timeout, MR_bool force, MR_bool verbose,
MR_bool split);
static MR_bool MR_trace_options_detailed(MR_bool *detailed,
char ***words, int *word_count);
static MR_bool MR_trace_options_stack_trace(MR_bool *print_all,
MR_bool *detailed, MR_SpecLineLimit *line_limit,
MR_SpecLineLimit *clique_line_limit,
MR_FrameLimit *frame_limit,
char ***words, int *word_count);
static MR_bool MR_trace_options_print(MR_BrowseFormat *format,
MR_Unsigned *max_printed_actions,
MR_bool *set_max_printed_actions,
char ***words, int *word_count);
static MR_bool MR_trace_options_browse(MR_BrowseFormat *format,
MR_bool *web,
char ***words, int *word_count);
static MR_bool MR_trace_options_view(const char **window_cmd,
const char **server_cmd, const char **server_name,
MR_Unsigned *timeout, MR_bool *force, MR_bool *verbose,
MR_bool *split, MR_bool *close_window,
char ***words, int *word_count);
static MR_bool MR_trace_options_diff(MR_Unsigned *start,
MR_Unsigned *max, char ***words, int *word_count);
static MR_bool MR_trace_options_dump(MR_bool *quiet, MR_bool *xml,
MR_bool *prettyprint, char ***words, int *word_count);
////////////////////////////////////////////////////////////////////////////
MR_Next
MR_trace_cmd_level(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_Unsigned n;
MR_bool detailed;
MR_Level selected_level;
detailed = MR_FALSE;
if (! MR_trace_options_detailed(&detailed, &words, &word_count)) {
// The usage message has already been printed.
;
} else if (word_count == 2 &&
( MR_streq(words[1], "clique") || MR_streq(words[1], "clentry") ))
{
if (MR_find_clique_entry_mdb(event_info, MR_CLIQUE_ENTRY_FRAME,
&selected_level))
{
// The error message has already been printed.
return KEEP_INTERACTING;
}
} else if (word_count == 2 && MR_streq(words[1], "clparent")) {
if (MR_find_clique_entry_mdb(event_info, MR_CLIQUE_ENTRY_PARENT_FRAME,
&selected_level))
{
// The error message has already been printed.
return KEEP_INTERACTING;
}
} else if (word_count == 2 && MR_trace_is_natural_number(words[1], &n)) {
selected_level = n;
} else if (word_count == 1) {
selected_level = 0;
} else {
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
MR_trace_set_level_and_report(selected_level, detailed,
MR_print_optionals);
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_up(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_Unsigned n;
MR_bool detailed;
detailed = MR_FALSE;
if (! MR_trace_options_detailed(&detailed, &words, &word_count)) {
// The usage message has already been printed.
;
} else if (word_count == 2 && MR_trace_is_natural_number(words[1], &n)) {
MR_trace_set_level_and_report(MR_trace_current_level() + n, detailed,
MR_print_optionals);
} else if (word_count == 1) {
MR_trace_set_level_and_report(MR_trace_current_level() + 1, detailed,
MR_print_optionals);
} else {
MR_trace_usage_cur_cmd();
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_down(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_Unsigned n;
MR_bool detailed;
detailed = MR_FALSE;
if (! MR_trace_options_detailed(&detailed, &words, &word_count)) {
// The usage message has already been printed.
;
} else if (word_count == 2 && MR_trace_is_natural_number(words[1], &n)) {
MR_trace_set_level_and_report(MR_trace_current_level() - n, detailed,
MR_print_optionals);
} else if (word_count == 1) {
MR_trace_set_level_and_report(MR_trace_current_level() - 1, detailed,
MR_print_optionals);
} else {
MR_trace_usage_cur_cmd();
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_vars(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
if (word_count == 1) {
const char *problem;
problem = MR_trace_list_vars(MR_mdb_out);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
}
} else {
MR_trace_usage_cur_cmd();
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_held_vars(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
if (word_count == 1) {
MR_trace_list_held_vars(MR_mdb_out);
} else {
MR_trace_usage_cur_cmd();
}
return KEEP_INTERACTING;
}
#define MR_NEXT_NUM_IO_ACTIONS_TO_PRINT 20
#define MR_ALL_NUM_IO_ACTIONS_TO_PRINT 500
MR_Next
MR_trace_cmd_print(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_BrowseFormat format;
MR_Unsigned max_printed_actions;
MR_bool set_max_printed_actions;
const char *problem;
MR_Unsigned action;
MR_Unsigned lo_action;
MR_Unsigned hi_action;
static MR_bool have_next_io_action = MR_FALSE;
static MR_Unsigned next_io_action = 0;
if (! MR_trace_options_print(&format,
&max_printed_actions, &set_max_printed_actions, &words, &word_count))
{
// The usage message has already been printed.
;
} else if (word_count == 1) {
problem = MR_trace_browse_one_goal(MR_mdb_out,
MR_trace_browse_goal_internal, MR_BROWSE_CALLER_PRINT, format);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
}
} else if (word_count == 2) {
if (MR_streq(words[1], "*")) {
problem = MR_trace_browse_all(MR_mdb_out,
MR_trace_browse_internal, format);
} else if (MR_streq(words[1], "goal")) {
problem = MR_trace_browse_one_goal(MR_mdb_out,
MR_trace_browse_goal_internal, MR_BROWSE_CALLER_PRINT, format);
} else if (MR_streq(words[1], "exception")) {
problem = MR_trace_browse_exception(event_info,
MR_trace_browse_internal, MR_BROWSE_CALLER_PRINT, format);
} else if (MR_streq(words[1], "proc_body")) {
problem = MR_trace_browse_proc_body(event_info,
MR_trace_browse_internal, MR_BROWSE_CALLER_PRINT, format);
} else if ((MR_streq(words[1], "io") || MR_streq(words[1], "action")))
{
MR_Unsigned num_printed_actions;
if (!set_max_printed_actions) {
max_printed_actions = MR_NEXT_NUM_IO_ACTIONS_TO_PRINT;
}
if (MR_io_tabling_phase == MR_IO_TABLING_BEFORE) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"mdb: I/O tabling has not yet started.\n");
return KEEP_INTERACTING;
}
if (MR_io_tabling_counter_hwm == 0) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"mdb: There are no tabled I/O actions yet.\n");
return KEEP_INTERACTING;
}
if (have_next_io_action && (!
(MR_io_tabling_start <= next_io_action
&& next_io_action < MR_io_tabling_counter_hwm)))
{
have_next_io_action = MR_FALSE;
}
if (have_next_io_action) {
lo_action = next_io_action;
} else {
lo_action = MR_io_tabling_start;
}
hi_action = lo_action + max_printed_actions;
if (hi_action >= MR_io_tabling_counter_hwm) {
hi_action = MR_io_tabling_counter_hwm - 1;
}
num_printed_actions = hi_action - lo_action + 1;
if (num_printed_actions <= 0) {
fprintf(MR_mdb_out, "There are no I/O actions to print\n");
have_next_io_action = MR_FALSE;
} else {
for (action = lo_action; action <= hi_action; action++) {
fprintf(MR_mdb_out,
"action %" MR_INTEGER_LENGTH_MODIFIER "u: ", action);
problem = MR_trace_browse_action(MR_mdb_out, action,
MR_trace_browse_goal_internal,
MR_BROWSE_CALLER_PRINT, format);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
return KEEP_INTERACTING;
}
}
if (hi_action == MR_io_tabling_counter_hwm - 1) {
fprintf(MR_mdb_out,
"there are no more actions (yet)\n");
} else {
fprintf(MR_mdb_out,
"there are more actions, up to action "
"%" MR_INTEGER_LENGTH_MODIFIER "u\n",
MR_io_tabling_counter_hwm - 1);
}
next_io_action = hi_action + 1;
have_next_io_action = MR_TRUE;
}
} else {
problem = MR_trace_parse_browse_one(MR_mdb_out, MR_TRUE, words[1],
MR_trace_browse_internal, MR_BROWSE_CALLER_PRINT, format,
MR_FALSE);
}
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
}
} else if (word_count == 3 &&
(MR_streq(words[1], "io") || MR_streq(words[1], "action")))
{
if (MR_io_tabling_phase == MR_IO_TABLING_BEFORE) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"mdb: I/O tabling has not yet started.\n");
return KEEP_INTERACTING;
}
if (MR_io_tabling_counter_hwm == 0) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"mdb: There are no tabled I/O actions yet.\n");
return KEEP_INTERACTING;
}
if (MR_streq(words[2], "limits")) {
fprintf(MR_mdb_out,
"I/O tabling has recorded actions "
"%" MR_INTEGER_LENGTH_MODIFIER "u to "
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
fflush(MR_mdb_out);
} else if (MR_trace_is_natural_number(words[2], &action)) {
if (! (MR_io_tabling_start <= action
&& action < MR_io_tabling_counter_hwm))
{
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"I/O tabling has only recorded actions "
"%" MR_INTEGER_LENGTH_MODIFIER "u to "
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
have_next_io_action = MR_FALSE;
return KEEP_INTERACTING;
}
problem = MR_trace_browse_action(MR_mdb_out, action,
MR_trace_browse_goal_internal,
MR_BROWSE_CALLER_PRINT, format);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
have_next_io_action = MR_FALSE;
}
next_io_action = action + 1;
have_next_io_action = MR_TRUE;
} else if (MR_trace_is_natural_number_pair(words[2],
&lo_action, &hi_action))
{
if (lo_action >= hi_action) {
// Swap lo_action and hi_action.
MR_Unsigned tmp;
tmp = lo_action;
lo_action = hi_action;
hi_action = tmp;
}
if (! (MR_io_tabling_start <= lo_action
&& hi_action < MR_io_tabling_counter_hwm))
{
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"I/O tabling has only recorded actions "
"%" MR_INTEGER_LENGTH_MODIFIER "u to "
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
have_next_io_action = MR_FALSE;
return KEEP_INTERACTING;
}
for (action = lo_action; action <= hi_action; action++) {
fprintf(MR_mdb_out,
"action %" MR_INTEGER_LENGTH_MODIFIER "u: ", action);
problem = MR_trace_browse_action(MR_mdb_out, action,
MR_trace_browse_goal_internal,
MR_BROWSE_CALLER_PRINT, format);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
return KEEP_INTERACTING;
}
}
next_io_action = hi_action + 1;
have_next_io_action = MR_TRUE;
} else if (MR_streq(words[2], "*")) {
if (!set_max_printed_actions) {
max_printed_actions = MR_ALL_NUM_IO_ACTIONS_TO_PRINT;
}
lo_action = MR_io_tabling_start;
hi_action = MR_io_tabling_counter_hwm - 1;
if (lo_action + max_printed_actions < hi_action) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"I/O tabling has recorded "
"%" MR_INTEGER_LENGTH_MODIFIER "d actions, "
"numbered %" MR_INTEGER_LENGTH_MODIFIER "u to "
"%" MR_INTEGER_LENGTH_MODIFIER "u.\n",
MR_io_tabling_counter_hwm + MR_io_tabling_start - 1,
MR_io_tabling_start, MR_io_tabling_counter_hwm - 1);
if (set_max_printed_actions) {
fprintf(MR_mdb_err,
"Following your request via the -m option, "
"only the first %" MR_INTEGER_LENGTH_MODIFIER "u "
"actions will be printed.\n",
max_printed_actions);
} else {
fprintf(MR_mdb_err,
"Without your explicit request via the -m option, "
"only the default maximum "
"of %" MR_INTEGER_LENGTH_MODIFIER "u "
"actions will be printed.\n",
max_printed_actions);
}
hi_action = lo_action + max_printed_actions - 1;
}
for (action = lo_action; action <= hi_action; action++) {
fprintf(MR_mdb_out,
"action %" MR_INTEGER_LENGTH_MODIFIER "u: ", action);
problem = MR_trace_browse_action(MR_mdb_out, action,
MR_trace_browse_goal_internal,
MR_BROWSE_CALLER_PRINT, format);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
return KEEP_INTERACTING;
}
}
next_io_action = hi_action + 1;
have_next_io_action = MR_TRUE;
} else {
MR_trace_usage_cur_cmd();
}
} else {
MR_trace_usage_cur_cmd();
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_browse(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_BrowseFormat format;
MR_bool web;
MR_IoActionNum action;
MR_GoalBrowser goal_browser;
MR_Browser browser;
const char *problem;
if (! MR_trace_options_browse(&format, &web, &words, &word_count)) {
// The usage message has already been printed.
;
} else {
if (web) {
goal_browser = MR_trace_browse_goal_web;
browser = MR_trace_browse_web;
} else {
goal_browser = MR_trace_browse_goal_internal;
browser = MR_trace_browse_internal;
}
if (word_count == 1) {
problem = MR_trace_browse_one_goal(MR_mdb_out, goal_browser,
MR_BROWSE_CALLER_BROWSE, format);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
}
} else if (word_count == 2) {
if (MR_streq(words[1], "goal")) {
problem = MR_trace_browse_one_goal(MR_mdb_out, goal_browser,
MR_BROWSE_CALLER_BROWSE, format);
} else if (MR_streq(words[1], "exception")) {
problem = MR_trace_browse_exception(event_info, browser,
MR_BROWSE_CALLER_BROWSE, format);
} else if (MR_streq(words[1], "proc_body")) {
problem = MR_trace_browse_proc_body(event_info, browser,
MR_BROWSE_CALLER_BROWSE, format);
} else {
problem = MR_trace_parse_browse_one(MR_mdb_out, MR_FALSE,
words[1], browser, MR_BROWSE_CALLER_BROWSE, format,
MR_TRUE);
}
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
}
} else if (word_count == 3 &&
(MR_streq(words[1], "io") || MR_streq(words[1], "action"))
&& MR_trace_is_natural_number(words[2], &action))
{
problem = MR_trace_browse_action(MR_mdb_out, action, goal_browser,
MR_BROWSE_CALLER_BROWSE, format);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", problem);
}
} else {
MR_trace_usage_cur_cmd();
}
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_stack(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_bool print_all;
MR_bool detailed;
MR_FrameLimit frame_limit;
MR_SpecLineLimit clique_line_limit;
MR_SpecLineLimit line_limit;
MR_SpecLineLimit spec_line_limit;
const MR_LabelLayout *layout;
MR_Word *saved_regs;
const char *msg;
detailed = MR_FALSE;
print_all = MR_FALSE;
frame_limit = 0;
clique_line_limit = 10;
line_limit = 100;
if (! MR_trace_options_stack_trace(&print_all, &detailed,
&line_limit, &clique_line_limit, &frame_limit, &words, &word_count))
{
// The usage message has already been printed.
return KEEP_INTERACTING;
} else if (word_count == 1) {
line_limit = MR_stack_default_line_limit;
} else if (word_count == 2 &&
MR_trace_is_natural_number(words[1], &spec_line_limit))
{
line_limit = spec_line_limit;
} else {
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
layout = event_info->MR_event_sll;
saved_regs = event_info->MR_saved_regs;
#ifdef MR_DEBUG_STACK_DUMP_CLIQUE
MR_trace_init_modules();
fprintf(MR_mdb_out, "OLD STACK DUMP:\n");
msg = MR_dump_stack_from_layout(MR_mdb_out, layout,
MR_saved_sp(saved_regs), MR_saved_curfr(saved_regs),
detailed, MR_context_position != MR_CONTEXT_NOWHERE,
frame_limit, line_limit,
&MR_dump_stack_record_print);
if (msg != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "%s.\n", msg);
}
fprintf(MR_mdb_out, "\nNEW STACK DUMP:\n");
#endif
msg = MR_dump_stack_from_layout_clique(MR_mdb_out, layout,
MR_saved_sp(saved_regs), MR_saved_curfr(saved_regs),
detailed, MR_context_position != MR_CONTEXT_NOWHERE,
!print_all, clique_line_limit, frame_limit, line_limit,
&MR_dump_stack_record_print);
if (msg != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "%s.\n", msg);
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_current(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
if (word_count == 1) {
MR_trace_event_print_internal_report(event_info);
} else {
MR_trace_usage_cur_cmd();
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_view(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
const char *window_cmd = NULL;
const char *server_cmd = NULL;
const char *server_name = NULL;
MR_Unsigned timeout = 8; // In seconds.
MR_bool force = MR_FALSE;
MR_bool verbose = MR_FALSE;
MR_bool split = MR_FALSE;
MR_bool close_window = MR_FALSE;
const char *msg;
if (! MR_trace_options_view(&window_cmd, &server_cmd, &server_name,
&timeout, &force, &verbose, &split, &close_window,
&words, &word_count))
{
// The usage message has already been printed.
;
} else if (word_count != 1) {
MR_trace_usage_cur_cmd();
} else if (close_window) {
MR_trace_maybe_close_source_window(verbose);
} else {
msg = MR_trace_new_source_window(window_cmd, server_cmd, server_name,
timeout, force, verbose, split);
if (msg != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", msg);
}
MR_trace_maybe_sync_source_window(event_info, verbose);
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_hold(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
char *event_var_name;
char *held_var_name;
MR_TypeInfo type_info;
MR_Word value;
const char *problem;
MR_bool bad_subterm;
if (word_count == 2) {
event_var_name = words[1];
held_var_name = words[1];
} else if (word_count == 3) {
event_var_name = words[1];
held_var_name = words[2];
} else {
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
if (strpbrk(held_var_name, "^/") != NULL) {
// Don't allow path separators in variable names.
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
if (held_var_name[0] == '$') {
// Ignore any unneeded initial $ signs.
held_var_name = &held_var_name[1];
}
problem = MR_trace_parse_lookup_var_path(event_var_name, &type_info,
&value, &bad_subterm);
if (problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s%s.\n",
(bad_subterm? "there is no path " : ""), problem);
return KEEP_INTERACTING;
}
if (! MR_add_hold_var(held_var_name, type_info, value)) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: there is already a held variable $%s\n",
held_var_name);
}
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_diff(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_Unsigned start;
MR_Unsigned max;
char *name1;
char *name2;
MR_TypeInfo type_info1;
MR_TypeInfo type_info2;
MR_Word value1;
MR_Word value2;
MR_Word univ1;
MR_Word univ2;
const char *problem1;
const char *problem2;
MR_bool bad_subterm1;
MR_bool bad_subterm2;
MercuryFile mdb_out;
start = 0;
max = 20;
if (! MR_trace_options_diff(&start, &max, &words, &word_count)) {
// The usage message has already been printed.
return KEEP_INTERACTING;
} else if (word_count != 3) {
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
name1 = words[1];
name2 = words[2];
problem1 = MR_trace_parse_lookup_var_path(name1, &type_info1, &value1,
&bad_subterm1);
problem2 = MR_trace_parse_lookup_var_path(name2, &type_info2, &value2,
&bad_subterm2);
if (problem1 != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s%s.\n",
(bad_subterm1? "arg1: there is no path " : ""), problem1);
return KEEP_INTERACTING;
}
if (problem2 != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s%s.\n",
(bad_subterm2? "arg2: there is no path " : ""), problem2);
return KEEP_INTERACTING;
}
MR_c_file_to_mercury_file(MR_mdb_out, &mdb_out);
MR_TRACE_CALL_MERCURY(
MR_new_univ_on_hp(univ1, type_info1, value1);
MR_new_univ_on_hp(univ2, type_info2, value2);
ML_report_diffs(MR_wrap_output_stream(&mdb_out), start, max,
univ1, univ2);
);
return KEEP_INTERACTING;
}
MR_Next
MR_trace_cmd_dump(char **words, int word_count, MR_TraceCmdInfo *cmd,
MR_EventInfo *event_info, MR_Code **jumpaddr)
{
MR_Word browser_term;
// If bad_path is true, then value_problem contains the problematic part
// of the path.
MR_bool bad_path = MR_FALSE;
const char *value_problem = NULL;
MR_bool quiet = MR_FALSE;
MR_bool xml = MR_FALSE;
MR_bool prettyprint = MR_FALSE;
// Set this to NULL to avoid uninitialization warnings.
browser_term = (MR_Word) NULL;
if (! MR_trace_options_dump(&quiet, &xml, &prettyprint,
&words, &word_count))
{
// The usage message has already been printed.
;
} else if (word_count != 3) {
MR_trace_usage_cur_cmd();
} else {
if (MR_streq(words[1], "goal")) {
const char *name;
MR_Word arg_list;
MR_bool is_func;
MR_convert_goal_to_synthetic_term(&name, &arg_list, &is_func);
browser_term = MR_synthetic_to_browser_term(name, arg_list,
is_func);
} else if (MR_streq(words[1], "exception")) {
MR_Word exception;
exception = MR_trace_get_exception_value();
if (exception == (MR_Word) NULL) {
value_problem = "missing exception value";
} else {
browser_term = MR_univ_to_browser_term(exception);
}
} else if (MR_streq(words[1], "proc_body")) {
const MR_ProcLayout *entry;
MR_Word rep;
entry = event_info->MR_event_sll->MR_sll_entry;
if (entry->MR_sle_body_bytes == NULL) {
value_problem = "current procedure has no body bytecodes";
} else {
MR_TRACE_CALL_MERCURY(
MR_MDBCOMP_trace_read_proc_defn_rep(
entry->MR_sle_body_bytes,
event_info->MR_event_sll, &rep);
);
browser_term = MR_type_value_to_browser_term(
(MR_TypeInfo) ML_proc_defn_rep_type(), rep);
}
} else {
MR_TypeInfo type_info;
MR_Word value;
const char *name;
char *path;
value_problem = MR_trace_parse_lookup_and_return_var_path(words[1],
&type_info, &value, &name, &path, &bad_path);
if (value_problem == NULL) {
browser_term = MR_type_value_to_browser_term(type_info, value);
}
}
if (value_problem != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s%s.\n",
(bad_path ? "there is no path " : ""), value_problem);
} else {
if (xml && prettyprint) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err,
"mdb: the -p and -x options of the \"dump\" command "
"are mutually exclusive; ignoring -p.\n");
}
if (xml) {
MR_trace_save_term_xml(words[2], browser_term);
} else if (prettyprint) {
MR_trace_save_term_doc(words[2], browser_term);
} else {
MR_trace_save_term(words[2], browser_term);
}
if (!quiet) {
fprintf(MR_mdb_out, "Dumped %s to %s\n", words[1], words[2]);
}
}
}
return KEEP_INTERACTING;
}
// list [num]
//
// List num lines of context around the line number of the context of the
// current point (i.e. level in the call stack). If num is not given,
// the number of context lines defaults to the value of the context_lines
// setting.
//
// TODO: add the following (use MR_parse_source_locn()):
// list filename:num[-num]
//
// List a range of lines from a given file. If only one number is given,
// the default number of lines of context is used.
MR_Next
MR_trace_cmd_list(char **words, int word_count,
MR_TraceCmdInfo *cmd, MR_EventInfo *event_info, MR_Code **jumpaddr)
{
const MR_ProcLayout *entry_ptr;
const char *filename;
int lineno;
int first_lineno;
int last_lineno;
MR_Word *base_sp_ptr;
MR_Word *base_curfr_ptr;
MR_Unsigned num = MR_num_context_lines;
MR_String aligned_filename;
if (word_count > 2) {
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
if (word_count == 2 && !MR_trace_is_natural_number(words[1], &num)) {
MR_trace_usage_cur_cmd();
return KEEP_INTERACTING;
}
MR_trace_current_level_details(&entry_ptr, &filename, &lineno,
&base_sp_ptr, &base_curfr_ptr);
MR_TRACE_USE_HP(
MR_make_aligned_string(aligned_filename, (MR_String) filename);
);
first_lineno = lineno - num;
last_lineno = lineno + num;
if (first_lineno < 1) {
first_lineno = 1;
}
if (MR_listing_cmd != NULL && strlen(MR_listing_cmd) > 0) {
MR_TRACE_CALL_MERCURY(
ML_LISTING_list_file_with_command(MR_mdb_out, MR_mdb_err,
MR_listing_cmd, (char *) aligned_filename,
first_lineno, last_lineno, lineno, MR_listing_path);
);
} else {
MR_TRACE_CALL_MERCURY(
ML_LISTING_list_file(MR_mdb_out, MR_mdb_err,
(char *) aligned_filename,
first_lineno, last_lineno, lineno, MR_listing_path);
);
}
return KEEP_INTERACTING;
}
////////////////////////////////////////////////////////////////////////////
static void
MR_trace_set_level_and_report(int ancestor_level, MR_bool detailed,
MR_bool print_optionals)
{
const char *problem;
const MR_ProcLayout *entry;
MR_Word *base_sp;
MR_Word *base_curfr;
const char *filename;
int lineno;
int indent;
problem = MR_trace_set_level(ancestor_level, print_optionals);
if (problem == NULL) {
fprintf(MR_mdb_out, "Ancestor level set to %d:\n",
ancestor_level);
MR_trace_current_level_details(&entry, &filename, &lineno,
&base_sp, &base_curfr);
fprintf(MR_mdb_out, "%4d ", ancestor_level);
if (detailed) {
// We want to print the trace info first regardless
// of the value of MR_context_position.
MR_print_call_trace_info(MR_mdb_out, entry, base_sp, base_curfr);
indent = 26;
} else {
indent = 5;
}
MR_print_proc_id_trace_and_context(MR_mdb_out, MR_FALSE,
MR_context_position, MR_user_event_context, entry, MR_FALSE,
base_sp, base_curfr, "", filename, lineno, MR_FALSE,
"", 0, indent);
} else {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "%s.\n", problem);
}
}
void
MR_trace_browse_internal(MR_Word type_info, MR_Word value,
MR_BrowseCallerType caller, MR_BrowseFormat format)
{
switch (caller) {
case MR_BROWSE_CALLER_BROWSE:
MR_trace_browse(type_info, value, format);
break;
case MR_BROWSE_CALLER_PRINT:
case MR_BROWSE_CALLER_PRINT_ALL:
fprintf(MR_mdb_out, "\t");
fflush(MR_mdb_out);
MR_trace_print(type_info, value, caller, format);
break;
default:
MR_fatal_error("MR_trace_browse_internal: unknown caller type");
}
}
void
MR_trace_browse_goal_internal(MR_ConstString name, MR_Word arg_list,
MR_Word is_func, MR_BrowseCallerType caller, MR_BrowseFormat format)
{
switch (caller) {
case MR_BROWSE_CALLER_BROWSE:
MR_trace_browse_goal(name, arg_list, is_func, format);
break;
case MR_BROWSE_CALLER_PRINT:
MR_trace_print_goal(name, arg_list, is_func, caller, format);
break;
case MR_BROWSE_CALLER_PRINT_ALL:
MR_fatal_error("MR_trace_browse_goal_internal: bad caller type");
default:
MR_fatal_error("MR_trace_browse_goal_internal:"
" unknown caller type");
}
}
static const char *
MR_trace_browse_exception(MR_EventInfo *event_info, MR_Browser browser,
MR_BrowseCallerType caller, MR_BrowseFormat format)
{
MR_TypeInfo type_info;
MR_Word value;
MR_Word exception;
if (event_info->MR_trace_port != MR_PORT_EXCEPTION) {
return "command only available from EXCP ports";
}
exception = MR_trace_get_exception_value();
if (exception == (MR_Word) NULL) {
return "missing exception value";
}
MR_unravel_univ(exception, type_info, value);
(*browser)((MR_Word) type_info, value, caller, format);
return (const char *) NULL;
}
static const char *
MR_trace_browse_proc_body(MR_EventInfo *event_info, MR_Browser browser,
MR_BrowseCallerType caller, MR_BrowseFormat format)
{
const MR_ProcLayout *entry;
MR_Word rep;
entry = event_info->MR_event_sll->MR_sll_entry;
if (entry->MR_sle_body_bytes == NULL) {
return "current procedure has no body info";
}
MR_TRACE_CALL_MERCURY(
MR_MDBCOMP_trace_read_proc_defn_rep(entry->MR_sle_body_bytes,
event_info->MR_event_sll, &rep);
);
(*browser)(ML_proc_defn_rep_type(), rep, caller, format);
return (const char *) NULL;
}
static void
MR_trace_browse_web(MR_Word type_info, MR_Word value,
MR_BrowseCallerType caller, MR_BrowseFormat format)
{
MR_Word browser_term;
browser_term = MR_type_value_to_browser_term((MR_TypeInfo) type_info,
value);
MR_trace_save_and_invoke_web_browser(browser_term);
}
static void
MR_trace_browse_goal_web(MR_ConstString name, MR_Word arg_list,
MR_Word is_func, MR_BrowseCallerType caller, MR_BrowseFormat format)
{
MR_Word browser_term;
browser_term = MR_synthetic_to_browser_term(name, arg_list, is_func);
MR_trace_save_and_invoke_web_browser(browser_term);
}
// Implement the `view' command. First, check if there is a server attached.
// If so, either stop it or abort the command, depending on whether '-f'
// was given. Then, if a server name was not supplied, start a new server
// with a unique name (which has been MR_malloc'd), otherwise attach to the
// server with the supplied name (and make a MR_malloc'd copy of the name).
static const char *
MR_trace_new_source_window(const char *window_cmd, const char *server_cmd,
const char *server_name, int timeout, MR_bool force,
MR_bool verbose, MR_bool split)
{
const char *msg;
if (MR_trace_source_server.server_name != NULL) {
// We are already attached to a server.
if (force) {
MR_trace_maybe_close_source_window(verbose);
} else {
return "error: server already open (use '-f' to force)";
}
}
MR_trace_source_server.split = split;
if (server_cmd != NULL) {
MR_trace_source_server.server_cmd = MR_copy_string(server_cmd);
} else {
MR_trace_source_server.server_cmd = NULL;
}
if (server_name == NULL) {
msg = MR_trace_source_open_server(&MR_trace_source_server,
window_cmd, timeout, verbose);
} else {
MR_trace_source_server.server_name = MR_copy_string(server_name);
msg = MR_trace_source_attach(&MR_trace_source_server, timeout,
verbose);
if (msg != NULL) {
// Something went wrong, so we should free the
// strings we allocated just above.
MR_free(MR_trace_source_server.server_name);
MR_trace_source_server.server_name = NULL;
MR_free(MR_trace_source_server.server_cmd);
MR_trace_source_server.server_cmd = NULL;
}
}
return msg;
}
void
MR_trace_maybe_sync_source_window(MR_EventInfo *event_info, MR_bool verbose)
{
const MR_LabelLayout *parent;
const char *filename;
int lineno;
const char *parent_filename;
int parent_lineno;
const char *problem; // Not used.
MR_Word *base_sp;
MR_Word *base_curfr;
const char *msg;
MR_Level actual_level;
if (MR_trace_source_server.server_name != NULL) {
lineno = 0;
filename = "";
parent_lineno = 0;
parent_filename = "";
if (filename[0] == '\0') {
(void) MR_find_context(event_info->MR_event_sll,
&filename, &lineno);
}
// At interface ports we send both the parent context and the
// current context. Otherwise, we just send the current context.
if (MR_port_is_interface(event_info->MR_trace_port)) {
base_sp = MR_saved_sp(event_info->MR_saved_regs);
base_curfr = MR_saved_curfr(event_info->MR_saved_regs);
parent = MR_find_nth_ancestor(event_info->MR_event_sll, 1,
&base_sp, &base_curfr, &actual_level, &problem);
if (actual_level != 1) {
parent_filename = filename;
parent_lineno = lineno;
} else if (parent != NULL) {
(void) MR_find_context(parent, &parent_filename,
&parent_lineno);
}
}
msg = MR_trace_source_sync(&MR_trace_source_server, filename, lineno,
parent_filename, parent_lineno, verbose);
if (msg != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", msg);
}
}
}
void
MR_trace_maybe_close_source_window(MR_bool verbose)
{
const char *msg;
if (MR_trace_source_server.server_name != NULL) {
msg = MR_trace_source_close(&MR_trace_source_server, verbose);
if (msg != NULL) {
fflush(MR_mdb_out);
fprintf(MR_mdb_err, "mdb: %s.\n", msg);
}
MR_free(MR_trace_source_server.server_name);
MR_trace_source_server.server_name = NULL;
MR_free(MR_trace_source_server.server_cmd);
MR_trace_source_server.server_cmd = NULL;
}
}
////////////////////////////////////////////////////////////////////////////
const char *const MR_trace_print_cmd_args[] =
{ "-f", "-p", "-v", "--flat", "--pretty", "--verbose",
"exception", "goal", "*", NULL };
// It is better to have a single completion where possible,
// so don't include `-d' here.
const char *const MR_trace_stack_cmd_args[] =
{ "--detailed", NULL };
const char *const MR_trace_view_cmd_args[] =
{ "-c", "-f", "-n", "-s", "-t", "-v", "-w", "-2",
"--close", "--verbose", "--force", "--split-screen",
"--window-command", "--server-command", "--server-name",
"--timeout", NULL };
////////////////////////////////////////////////////////////////////////////
static struct MR_option MR_trace_detailed_opts[] =
{
{ "detailed", MR_no_argument, NULL, 'd' },
{ NULL, MR_no_argument, NULL, 0 }
};
static MR_bool
MR_trace_options_detailed(MR_bool *detailed, char ***words, int *word_count)
{
int c;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "d",
MR_trace_detailed_opts, NULL)) != EOF)
{
switch (c) {
case 'd':
*detailed = MR_TRUE;
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}
static MR_bool
MR_trace_options_stack_trace(MR_bool *print_all, MR_bool *detailed,
MR_SpecLineLimit *line_limit, MR_SpecLineLimit *clique_line_limit,
MR_FrameLimit *frame_limit, char ***words, int *word_count)
{
int c;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "ac:df:",
MR_trace_detailed_opts, NULL)) != EOF)
{
switch (c) {
case 'a':
*print_all = MR_TRUE;
*line_limit = 0;
break;
case 'c':
if (! MR_trace_is_natural_number(MR_optarg, clique_line_limit))
{
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*print_all = MR_FALSE;
break;
case 'd':
*detailed = MR_TRUE;
break;
case 'f':
if (! MR_trace_is_natural_number(MR_optarg, frame_limit)) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}
static struct MR_option MR_trace_print_opts[] =
{
// Please keep the formatting options in sync with MR_trace_browse_opts.
{ "flat", MR_no_argument, NULL, 'f' },
{ "raw_pretty", MR_no_argument, NULL, 'r' },
{ "verbose", MR_no_argument, NULL, 'v' },
{ "pretty", MR_no_argument, NULL, 'p' },
{ "max", MR_required_argument, NULL, 'm' },
{ NULL, MR_no_argument, NULL, 0 }
};
static MR_bool
MR_trace_options_print(MR_BrowseFormat *format,
MR_Unsigned *max_printed_actions, MR_bool *set_max_printed_actions,
char ***words, int *word_count)
{
int c;
*format = MR_BROWSE_DEFAULT_FORMAT;
*max_printed_actions = -1;
*set_max_printed_actions = MR_FALSE;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "frvpm:",
MR_trace_print_opts, NULL)) != EOF)
{
switch (c) {
case 'f':
*format = MR_BROWSE_FORMAT_FLAT;
break;
case 'r':
*format = MR_BROWSE_FORMAT_RAW_PRETTY;
break;
case 'v':
*format = MR_BROWSE_FORMAT_VERBOSE;
break;
case 'p':
*format = MR_BROWSE_FORMAT_PRETTY;
break;
case 'm':
if (! MR_trace_is_natural_number(MR_optarg,
max_printed_actions))
{
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
if (*max_printed_actions == 0) {
// Printing a maximum of zero I/O actions
// does not make any sense.
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*set_max_printed_actions = MR_TRUE;
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}
static struct MR_option MR_trace_browse_opts[] =
{
// Please keep the formatting options in sync with MR_trace_print_opts.
{ "flat", MR_no_argument, NULL, 'f' },
{ "raw_pretty", MR_no_argument, NULL, 'r' },
{ "verbose", MR_no_argument, NULL, 'v' },
{ "pretty", MR_no_argument, NULL, 'p' },
{ "web", MR_no_argument, NULL, 'w' },
{ NULL, MR_no_argument, NULL, 0 }
};
static MR_bool
MR_trace_options_browse(MR_BrowseFormat *format, MR_bool *web,
char ***words, int *word_count)
{
int c;
*format = MR_BROWSE_DEFAULT_FORMAT;
*web = MR_FALSE;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "frvpw",
MR_trace_browse_opts, NULL)) != EOF)
{
switch (c) {
case 'f':
*format = MR_BROWSE_FORMAT_FLAT;
break;
case 'r':
*format = MR_BROWSE_FORMAT_RAW_PRETTY;
break;
case 'v':
*format = MR_BROWSE_FORMAT_VERBOSE;
break;
case 'p':
*format = MR_BROWSE_FORMAT_PRETTY;
break;
case 'w':
*web = MR_TRUE;
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}
static struct MR_option MR_trace_view_opts[] =
{
{ "close", MR_no_argument, NULL, 'c' },
{ "window-command", MR_required_argument, NULL, 'w' },
{ "server-command", MR_required_argument, NULL, 's' },
{ "server-name", MR_required_argument, NULL, 'n' },
{ "timeout", MR_required_argument, NULL, 't' },
{ "force", MR_no_argument, NULL, 'f' },
{ "verbose", MR_no_argument, NULL, 'v' },
{ "split-screen", MR_no_argument, NULL, '2' },
{ NULL, MR_no_argument, NULL, 0 }
};
static MR_bool
MR_trace_options_view(const char **window_cmd, const char **server_cmd,
const char **server_name, MR_Unsigned *timeout, MR_bool *force,
MR_bool *verbose, MR_bool *split, MR_bool *close_window,
char ***words, int *word_count)
{
int c;
MR_bool no_close = MR_FALSE;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "cw:s:n:t:fv2",
MR_trace_view_opts, NULL)) != EOF)
{
// Option '-c' is mutually incompatible with '-f', '-t',
// '-s', '-n', '-w' and '-2'.
switch (c) {
case 'c':
if (no_close) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*close_window = MR_TRUE;
break;
case 'w':
if (*close_window) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*window_cmd = MR_optarg;
no_close = MR_TRUE;
break;
case 's':
if (*close_window) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*server_cmd = MR_optarg;
no_close = MR_TRUE;
break;
case 'n':
if (*close_window) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*server_name = MR_optarg;
no_close = MR_TRUE;
break;
case 't':
if (*close_window ||
! MR_trace_is_natural_number(MR_optarg, timeout))
{
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
no_close = MR_TRUE;
break;
case 'f':
if (*close_window) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*force = MR_TRUE;
no_close = MR_TRUE;
break;
case 'v':
*verbose = MR_TRUE;
break;
case '2':
if (*close_window) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
*split = MR_TRUE;
no_close = MR_TRUE;
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}
static struct MR_option MR_trace_diff_opts[] =
{
{ "start", MR_required_argument, NULL, 's' },
{ "max", MR_required_argument, NULL, 'm' },
{ NULL, MR_no_argument, NULL, 0 }
};
static MR_bool
MR_trace_options_diff(MR_Unsigned *start, MR_Unsigned *max,
char ***words, int *word_count)
{
int c;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "m:s:",
MR_trace_diff_opts, NULL)) != EOF)
{
switch (c) {
case 'm':
if (! MR_trace_is_natural_number(MR_optarg, max)) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
break;
case 's':
if (! MR_trace_is_natural_number(MR_optarg, start)) {
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}
static struct MR_option MR_trace_dump_opts[] =
{
{ "quiet", MR_no_argument, NULL, 'q' },
{ "xml", MR_no_argument, NULL, 'x' },
{ "prettyprint", MR_no_argument, NULL, 'p' },
{ NULL, MR_no_argument, NULL, 0 }
};
static MR_bool
MR_trace_options_dump(MR_bool *quiet, MR_bool *xml, MR_bool *prettyprint,
char ***words, int *word_count)
{
int c;
MR_optind = 0;
while ((c = MR_getopt_long(*word_count, *words, "pqx",
MR_trace_dump_opts, NULL)) != EOF)
{
switch (c) {
case 'p':
*prettyprint = MR_TRUE;
break;
case 'q':
*quiet = MR_TRUE;
break;
case 'x':
*xml = MR_TRUE;
break;
default:
MR_trace_usage_cur_cmd();
return MR_FALSE;
}
}
*words = *words + MR_optind - 1;
*word_count = *word_count - MR_optind + 1;
return MR_TRUE;
}
////////////////////////////////////////////////////////////////////////////