for review: new debugger command set (part 4 of 4)

Zoltan Somogyi zs at cs.mu.OZ.AU
Thu Oct 1 18:52:25 AEST 1998


Index: trace/mercury_trace_external.c
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_external.c,v
retrieving revision 1.1
diff -u -u -r1.1 mercury_trace_external.c
--- mercury_trace_external.c	1998/09/29 05:11:52	1.1
+++ mercury_trace_external.c	1998/09/29 06:58:57
@@ -26,7 +26,7 @@
RECAST MERCURY_TRACE_UTIL AS MERCURY_LAYOUT_UTIL
 
 #include "mercury_trace.h"
 #include "mercury_trace_external.h"
-#include "mercury_trace_util.h"
+#include "mercury_layout_util.h"
 #include <stdio.h>
 #include <errno.h>
 #include <sys/types.h>
@@ -68,11 +68,11 @@
MISC RUNTIME CHANGES
 			Integer *debugger_request_type_ptr);
 	
 static bool	MR_found_match(const MR_Stack_Layout_Label *layout,
-			MR_trace_port port, Unsigned seqno, Unsigned depth,
+			MR_Trace_Port port, Unsigned seqno, Unsigned depth,
 			/* XXX registers */
 			const char *path, Word search_data);
 static void	MR_output_current_slots(const MR_Stack_Layout_Label *layout,
-			MR_trace_port port, Unsigned seqno, Unsigned depth, 
+			MR_Trace_Port port, Unsigned seqno, Unsigned depth, 
 			const char *path);
 static void	MR_output_current_vars(Word var_list, Word string_list);
 static void	MR_output_current_nth_var(Word var);
@@ -80,9 +80,10 @@
RECAST MERCURY_TRACE_UTIL AS MERCURY_LAYOUT_UTIL
 						 Word type_list);
 static Word	MR_trace_make_var_names_list(
 			const MR_Stack_Layout_Label *layout);
-static Word	MR_trace_make_type_list(const MR_Stack_Layout_Label *layout);
+static Word	MR_trace_make_type_list(const MR_Stack_Layout_Label *layout,
+			Word *saved_regs);
 static Word	MR_trace_make_nth_var(const MR_Stack_Layout_Label *layout, 
-				      Word debugger_request);
+			Word *saved_regs, Word debugger_request);
 static int	MR_get_var_number(Word debugger_request);
 
 #if 0
@@ -320,9 +321,9 @@
RECAST MERCURY_TRACE_UTIL AS MERCURY_LAYOUT_UTIL
MISC RUNTIME CHANGES
 }
 
 void
-MR_trace_event_external(MR_trace_cmd_info *cmd, 
-	const MR_Stack_Layout_Label *layout, MR_trace_port port,
-	Unsigned seqno, Unsigned depth, const char *path)
+MR_trace_event_external(MR_Trace_Cmd_Info *cmd, 
+	const MR_Stack_Layout_Label *layout, Word *saved_regs,
+	MR_Trace_Port port, Unsigned seqno, Unsigned depth, const char *path)
 {
 	static bool searching = FALSE;
 	static Word search_data;
@@ -370,9 +371,10 @@
 				}
 				var_names_list = 
 				  MR_trace_make_var_names_list(layout);
-				type_list = MR_trace_make_type_list(layout);
+				type_list = MR_trace_make_type_list(layout,
+						saved_regs);
 				MR_output_current_live_var_names(var_names_list,
-								 type_list);
+					type_list);
 				break;
 
 			case MR_REQUEST_CURRENT_VARS:
@@ -380,7 +382,7 @@
 					fprintf(stderr, "\nMercury runtime: "
 						"REQUEST_CURRENT_VARS\n");
 				}
-				var_list = MR_trace_make_var_list(layout);
+				var_list = MR_make_var_list(layout, saved_regs);
 				var_names_list = 
 				  MR_trace_make_var_names_list(layout);
 				MR_output_current_vars(var_list, 
@@ -392,7 +394,7 @@
 					fprintf(stderr, "\nMercury runtime: "
 						"REQUEST_NTH_CURRENT_VAR\n");
 				}
-				var = MR_trace_make_nth_var(layout, 
+				var = MR_trace_make_nth_var(layout, saved_regs,
 							    debugger_request);
 				MR_output_current_nth_var(var);
 				break;			
@@ -420,7 +422,7 @@
 
 static void
 MR_output_current_slots(const MR_Stack_Layout_Label *layout,
-	MR_trace_port port, Unsigned seqno, Unsigned depth, const char *path)
+	MR_Trace_Port port, Unsigned seqno, Unsigned depth, const char *path)
 {
 	MR_DI_output_current_slots(
 		MR_trace_event_number,
@@ -477,7 +479,7 @@
 
 static bool
 MR_found_match(const MR_Stack_Layout_Label *layout,
-	MR_trace_port port, Unsigned seqno, Unsigned depth,
+	MR_Trace_Port port, Unsigned seqno, Unsigned depth,
 	/* XXX live vars */
 	const char *path, Word search_data)
 {
@@ -548,7 +550,7 @@
 */
 
 static Word
-MR_trace_make_type_list(const MR_Stack_Layout_Label *layout)
+MR_trace_make_type_list(const MR_Stack_Layout_Label *layout, Word *saved_regs)
 {
 	int 				var_count;
 	const MR_Stack_Layout_Vars 	*vars;
@@ -571,7 +573,7 @@
 		name = MR_name_if_present(vars, i);
 		var = &vars->MR_slvs_pairs[i];
 
-		if (!MR_trace_get_type_filtered(var, name, &type_info))
+		if (! MR_get_type_filtered(var, saved_regs, name, &type_info))
 		{
 			continue;
 		}
@@ -591,7 +593,7 @@
 */
 
 static Word
-MR_trace_make_nth_var(const MR_Stack_Layout_Label *layout, 
+MR_trace_make_nth_var(const MR_Stack_Layout_Label *layout, Word *saved_regs,
 		      Word debugger_request)
 {
 	int 				var_number;
@@ -615,8 +617,8 @@
 	incr_hp(univ, 2);
 
 
-	if (MR_trace_get_type_and_value_filtered(var, name, &type_info, 
-						     &value))
+	if (MR_get_type_and_value_filtered(var, saved_regs, name,
+			&type_info, &value))
 	{
 		field(mktag(0), univ, UNIV_OFFSET_FOR_TYPEINFO) = type_info;
 		field(mktag(0), univ, UNIV_OFFSET_FOR_DATA) = value;
Index: trace/mercury_trace_external.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_external.h,v
retrieving revision 1.1
diff -u -u -r1.1 mercury_trace_external.h
--- mercury_trace_external.h	1998/09/29 05:11:54	1.1
+++ mercury_trace_external.h	1998/09/29 06:58:58
RECAST MERCURY_TRACE_UTIL AS MERCURY_LAYOUT_UTIL
MISC RUNTIME CHANGES
@@ -11,10 +11,10 @@
 
 extern	void	MR_trace_init_external(void);
 extern	void	MR_trace_final_external(void);
-extern	void	MR_trace_event_external(MR_trace_cmd_info *cmd,
+extern	void	MR_trace_event_external(MR_Trace_Cmd_Info *cmd,
 			const MR_Stack_Layout_Label *layout,
-			MR_trace_port port, Unsigned seqno, Unsigned depth,
-			const char *path);
+			Word *saved_regs, MR_Trace_Port port, Unsigned seqno,
+			Unsigned depth, const char *path);
 
 #endif	/* MR_USE_EXTERNAL_DEBUGGER */
 
Index: trace/mercury_trace_help.c
===================================================================
RCS file: mercury_trace_help.c
diff -N mercury_trace_help.c
--- /dev/null	Wed May 28 10:49:58 1997
+++ mercury_trace_help.c	Wed Sep 23 18:30:47 1998
IMPLEMENT NEW DEBUGGER COMMAND SET
@@ -0,0 +1,126 @@
+/*
+** Copyright (C) 1998 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+/*
+** mercury_trace_help.c
+**
+** Help system for the internal debugger.
+** Most of the implementation is in library/help.m;
+** this is only the interface.
+**
+** Main author: Zoltan Somogyi.
+*/
+
+#include "mercury_imp.h"
+#include "mercury_trace_help.h"
+#include "mercury_macros.h"
+#include "mercury_misc.h"
+#include "mercury_deep_copy.h"
+#include "help.h"
+#include "io.h"
+#include <stdio.h>
+
+static	Word		MR_trace_help_system;
+static	Word		MR_trace_help_system_type;
+static	Word		MR_trace_help_stdout;
+
+static	const char	*MR_trace_help_add_node(Word path, const char *name,
+				int slot, const char *text);
+static	void		MR_trace_help_ensure_init(void);
+static	void		MR_trace_help_make_permanent(void);
+
+const char *
+MR_trace_add_cat(const char *category, int slot, const char *text)
+{
+	Word	path;
+
+	MR_trace_help_ensure_init();
+	path = list_empty();
+	return MR_trace_help_add_node(path, category, slot, text);
+}
+
+const char *
+MR_trace_add_item(const char *category, const char *item, int slot,
+	const char *text)
+{
+	Word	path;
+
+	MR_trace_help_ensure_init();
+	path = list_empty();
+	path = list_cons(category, path);
+	return MR_trace_help_add_node(path, item, slot, text);
+}
+
+static const char *
+MR_trace_help_add_node(Word path, const char *name, int slot, const char *text)
+{
+	Word	result;
+	char	*msg;
+
+	ML_HELP_add_help_node(MR_trace_help_system, path, slot,
+		(String) (Word) name, (String) (Word) text,
+		&result, &MR_trace_help_system);
+	MR_trace_help_make_permanent();
+	if (ML_HELP_result_is_error(result, &msg)) {
+		return msg;
+	} else {
+		return NULL;
+	}
+}
+
+void
+MR_trace_help(void)
+{
+	MR_trace_help_ensure_init();
+	ML_HELP_help(MR_trace_help_system, MR_trace_help_stdout);
+}
+
+void
+MR_trace_help_word(const char *word)
+{
+	MR_trace_help_ensure_init();
+	ML_HELP_name(MR_trace_help_system, (String) (Word) word,
+		MR_trace_help_stdout);
+}
+
+void
+MR_trace_help_cat_item(const char *category, const char *item)
+{
+	Word	path;
+	Word	result;
+	char	*msg;
+
+	MR_trace_help_ensure_init();
+	path = list_empty();
+	path = list_cons(item, path);
+	path = list_cons(category, path);
+	ML_HELP_path(MR_trace_help_system, path, MR_trace_help_stdout, &result);
+	if (ML_HELP_result_is_error(result, &msg)) {
+		printf("internal error in the trace help system: %s\n", msg);
+	}
+}
+
+static void
+MR_trace_help_ensure_init(void)
+{
+	static	bool	MR_trace_help_init_done = FALSE;
+
+	if (! MR_trace_help_init_done) {
+		ML_HELP_init(&MR_trace_help_system);
+		ML_HELP_help_system_type(&MR_trace_help_system_type);
+		MR_trace_help_make_permanent();
+		ML_io_stdout_stream(&MR_trace_help_stdout);
+		MR_trace_help_init_done = TRUE;
+	}
+}
+
+static void
+MR_trace_help_make_permanent(void)
+{
+	save_transient_registers();
+	MR_trace_help_system = MR_make_permanent(MR_trace_help_system,
+				MR_trace_help_system_type);
+	restore_transient_registers();
+}
Index: trace/mercury_trace_help.h
===================================================================
RCS file: mercury_trace_help.h
diff -N mercury_trace_help.h
--- /dev/null	Wed May 28 10:49:58 1997
+++ mercury_trace_help.h	Tue Sep 22 12:30:04 1998
RECAST MERCURY_TRACE_UTIL AS MERCURY_LAYOUT_UTIL
@@ -0,0 +1,43 @@
+/*
+** Copyright (C) 1998 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** mercury_trace_help.h
+**
+** Defines the interface of the help system for the internal debugger.
+*/
+
+#ifndef	MERCURY_TRACE_HELP_H
+#define MERCURY_TRACE_HELP_H
+
+/*
+** These function add a help node, which must a category or an item
+** within a category. It returns NULL if the addition was successful,
+** and a pointer to an error message otherwise.
+*/
+
+extern	const char	*MR_trace_add_cat(const char *category, int slot,
+				const char *text);
+
+extern	const char	*MR_trace_add_item(const char *category,
+				const char *item, int slot, const char *text);
+
+/*
+** These functions print help to standard output.
+**
+** MR_trace_help prints a list of the top-level help nodes.
+** MR_trace_help_word prints the text of all the help nodes with the given
+**	name. If there are none, it prints a list of the top-level help nodes.
+** MR_trace_help_cat_item prints the text of the node at path cat/item.
+*/
+
+extern	void		MR_trace_help(void);
+extern	void		MR_trace_help_word(const char *word);
+
+extern	void		MR_trace_help_cat_item(const char *cat,
+				const char *item);
+
+#endif	/* MERCURY_TRACE_HELP_H */
Index: trace/mercury_trace_internal.c
<the whole file is smaller and much more readable than the diff>
IMPLEMENT NEW DEBUGGER COMMAND SET
SUPPORT RETRY
ADD REDO EVENTS
ADD TRACE DEPTH HISTOGRAMS
/*
** Copyright (C) 1998 The University of Melbourne.
** This file may only be copied under the terms of the GNU Library General
** Public License - see the file COPYING.LIB in the Mercury distribution.
*/

/*
** This file contains the code of the internal, in-process debugger.
**
** Main author: Zoltan Somogyi.
*/

#include "mercury_imp.h"
#include "mercury_trace.h"
#include "mercury_trace_internal.h"
#include "mercury_trace_alias.h"
#include "mercury_trace_help.h"
#include "mercury_trace_spy.h"
#include "mercury_trace_tables.h"
#include "mercury_layout_util.h"
#include "mercury_macros.h"
#include "mercury_getopt.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

/* The initial size of the spy points table. */
#define	MR_INIT_SPY_POINTS	10

/* The initial size of arrays of argument values. */
#define	MR_INIT_ARG_COUNT	20

/* The initial size of arrays of words. */
#define	MR_INIT_WORD_COUNT	20

/* The initial size of arrays of characters. */
#define	MR_INIT_BUF_LEN		80

/* The initial number of lines in documentation entries. */
#define	MR_INIT_DOC_CHARS	800

#define	MDBRC_FILENAME		".mdbrc"
#define	DEFAULT_MDBRC_FILENAME	"mdbrc"

/* XXX We should consider whether the following should be thread local. */
static	MR_Spy_Point    	**MR_spy_points;
static	int			MR_spy_point_next = 0;
static	int			MR_spy_point_max  = 0;

static	MR_Trace_Print_Level	MR_default_print_level = MR_PRINT_LEVEL_SOME;

static	bool			MR_scroll_control = FALSE;
static	int			MR_scroll_next = 0;
static	int			MR_scroll_limit = 24;
static	bool			MR_echo_commands = FALSE;

static	int			MR_trace_saved_call_seqno;
static	int			MR_trace_saved_call_depth;
static	int			MR_trace_saved_event_number;

typedef struct MR_Line_Struct {
	char			*MR_line_contents;
	struct MR_Line_Struct	*MR_line_next;
} MR_Line;

static	MR_Line			*MR_line_head = NULL;
static	MR_Line			*MR_line_tail = NULL;

typedef enum {
	KEEP_INTERACTING,
	STOP_INTERACTING
} MR_Next;

static	void	MR_trace_internal_ensure_init(void);
static	bool	MR_trace_internal_init_from_env(const char *env_var);
static	bool	MR_trace_internal_init_from_file(const char *filename);
static	bool	MR_trace_internal_init_from_env_dir(const char *env_var,
			const char *filename);
static	MR_Next	MR_trace_debug_cmd(char *line, MR_Trace_Cmd_Info *cmd,
			const MR_Stack_Layout_Label *layout,
			Word *saved_regs, MR_Trace_Port port,
			int seqno, int depth, const char *path,
			int *ancestor_level, int *max_mr_num, Code **jumpaddr);
static	bool	MR_trace_options_strict_print(MR_Trace_Cmd_Info *cmd,
			char ***words, int *word_count,
			const char *cat, const char *item);
static	bool	MR_trace_options_when_action(MR_Spy_When *when,
			MR_Spy_Action *action, char ***words, int *word_count,
			const char *cat, const char *item);
static	bool	MR_trace_options_quiet(bool *verbose,
			char ***words, int *word_count,
			const char *cat, const char *item);
static	bool	MR_trace_options_confirmed(bool *confirmed, char ***words,
			int *word_count, const char *cat, const char *item);
static	void	MR_trace_retry(const MR_Stack_Layout_Label *layout,
			Word *saved_regs, int seqno, int depth,
			int *max_mr_num, Code **jumpaddr);
static	Word	MR_trace_find_input_arg(const MR_Stack_Layout_Label *label,
			Word *saved_regs, const char *name, bool *succeeded);
static	void	MR_trace_internal_add_spy_point(MR_Spy_When when,
			MR_Spy_Action action,
			const MR_Stack_Layout_Entry *entry,
			const MR_Stack_Layout_Label *label, const char *path);
static	void	MR_print_spy_point(int i);
static	void	MR_trace_list_vars(const MR_Stack_Layout_Label *top_layout,
			Word *saved_regs, int ancestor_level);
static	const char *MR_trace_browse_check_level(const MR_Stack_Layout_Label
			*top_layout, Word *saved_regs, int ancestor_level);
static	void	MR_trace_browse_one(const MR_Stack_Layout_Label *top_layout,
			Word *saved_regs, int ancestor_level, int which_var);
static	void	MR_trace_browse_all(const MR_Stack_Layout_Label *top_layout,
			Word *saved_regs, int ancestor_level);
static	void	MR_trace_browse_var(const char *name,
			const MR_Stack_Layout_Var *var,
			Word *saved_regs, Word *base_sp, Word *base_curfr,
			Word *type_params);

static	const char *MR_trace_read_help_text(void);
static	bool	MR_trace_is_number(char *word, int *value);
static	bool	MR_trace_is_number_prefix(char *word, char **suffix,
			int *value);
static	const char *MR_trace_parse_line(char *line,
			char ***words, int *word_max, int *word_count);
static	int	MR_trace_break_into_words(char *line,
			char ***words_ptr, int *word_max_ptr);
static	void	MR_trace_source(const char *filename);
static	void	MR_trace_source_from_open_file(FILE *fp);
static	char	*MR_trace_getline(const char *prompt, FILE *fp);
static	char	*MR_trace_getline_queue(void);
static	char	*MR_trace_getline_raw(FILE *fp);
static	void	MR_insert_line_at_head(const char *line);
static	void	MR_insert_line_at_tail(const char *line);

static	Code	*MR_trace_event_internal_report(MR_Trace_Cmd_Info *cmd,
			const MR_Stack_Layout_Label *layout, Word *saved_regs,
			MR_Trace_Port port, int seqno, int depth,
			const char *path, int *max_mr_num);
static	void	MR_trace_event_print_internal_report(
			const MR_Stack_Layout_Label *layout,
			MR_Trace_Port port, int seqno, int depth,
			const char *path);
static	void	MR_trace_print_port(MR_Trace_Port port);

static	bool	MR_trace_valid_command(const char *word);

Code *
MR_trace_event_internal(MR_Trace_Cmd_Info *cmd, bool interactive,
	const MR_Stack_Layout_Label *layout, Word *saved_regs,
	MR_Trace_Port port, int seqno, int depth,
	const char *path, int *max_mr_num)
{
	int	i;
	int	c;
	int	count;
	bool	count_given;
	int	ancestor_level;
	Code	*jumpaddr;
	char	*line;
	MR_Next	res;

	if (! interactive) {
		return MR_trace_event_internal_report(cmd, layout, saved_regs,
				port, seqno, depth, path, max_mr_num);
	}

	MR_trace_enabled = FALSE;
	MR_trace_internal_ensure_init();

	MR_trace_event_print_internal_report(layout, port, seqno, depth, path);

	/*
	** These globals can be overwritten when we call Mercury code,
	** such as the term browser. We therefore save and restore them
	** across calls to MR_trace_debug_cmd. However, we store the
	** saved values in global instead of local variables, to allow them
	** to be modified by MR_trace_retry().
	*/

	MR_trace_saved_call_seqno = MR_trace_call_seqno;
	MR_trace_saved_call_depth = MR_trace_call_depth;
	MR_trace_saved_event_number = MR_trace_event_number;

	/* by default, print variables from the current call */
	ancestor_level = 0;

	/* by default, return where we came from */
	jumpaddr = NULL;

	do {
		line = MR_trace_getline("mdb> ", stdin);
		res = MR_trace_debug_cmd(line, cmd, layout, saved_regs,
				port, seqno, depth, path,
				&ancestor_level, max_mr_num, &jumpaddr);
	} while (res == KEEP_INTERACTING);

	cmd->MR_trace_must_check = (! cmd->MR_trace_strict) ||
			(cmd->MR_trace_print_level != MR_PRINT_LEVEL_NONE);

	MR_trace_call_seqno = MR_trace_saved_call_seqno;
	MR_trace_call_depth = MR_trace_saved_call_depth;
	MR_trace_event_number = MR_trace_saved_event_number;

	MR_scroll_next = 0;
	MR_trace_enabled = TRUE;
	return jumpaddr;
}

static const char MR_trace_banner[] =
"Melbourne Mercury Debugger, mdb version 0.8.\n\
Copyright 1998 University of Melbourne, Australia.\n\
mdb is free software, covered by the GNU General Public License.\n\
There is absolutely no warranty for mdb.\n";

static	bool	MR_trace_internal_initialized = FALSE;

static void
MR_trace_internal_ensure_init(void)
{
	if (! MR_trace_internal_initialized) {
		char	*env;
		int	n;

		printf(MR_trace_banner);
		env = getenv("LINES");
		if (env != NULL && MR_trace_is_number(env, &n)) {
			MR_scroll_limit = n;
		}

		MR_trace_internal_init_from_env("MERCURY_DEBUGGER_INIT") ||
		MR_trace_internal_init_from_file(MDBRC_FILENAME) ||
		MR_trace_internal_init_from_env_dir("HOME", MDBRC_FILENAME) ||
		MR_trace_internal_init_from_env("DEFAULT_MERCURY_DEBUGGER_INIT");

		MR_trace_internal_initialized = TRUE;
	}
}

static bool
MR_trace_internal_init_from_env(const char *env_var)
{
	char	*init;

	init = getenv(env_var);
	if (init != NULL) {
		MR_trace_source(init);
		return TRUE;
	} else {
		return FALSE;
	}
}

static bool
MR_trace_internal_init_from_file(const char *filename)
{
	FILE	*fp;

	if ((fp = fopen(filename, "r")) != NULL) {
		MR_trace_source_from_open_file(fp);
		fclose(fp);
		return TRUE;
	} else {
		return FALSE;
	}
}

static bool
MR_trace_internal_init_from_env_dir(const char *env_var, const char *filename)
{
	char	*env;
	char	*buf;
	int	len;
	FILE	*fp;

	env = getenv(env_var);
	if (env == NULL) {
		return FALSE;
	}

	buf = checked_malloc(strlen(env) + strlen(filename) + 2);
	(void) strcpy(buf, env);
	(void) strcat(buf, "/");
	(void) strcat(buf, filename);
	if ((fp = fopen(buf, "r")) != NULL) {
		MR_trace_source_from_open_file(fp);
		fclose(fp);
		free(buf);
		return TRUE;
	} else {
		free(buf);
		return FALSE;
	}
}

static MR_Next
MR_trace_debug_cmd(char *line, MR_Trace_Cmd_Info *cmd,
	const MR_Stack_Layout_Label *layout, Word *saved_regs,
	MR_Trace_Port port, int seqno, int depth, const char *path,
	int *ancestor_level, int *max_mr_num, Code **jumpaddr)
{
	char		**words;
	char		**orig_words;
	int		word_max;
	int		word_count;
	const char	*alias_key;
	char		**alias_words;
	int		alias_word_count;
	int		alias_copy_start;
	const char	*problem;
	char		*semicolon;
	int		i;
	int		n;

	if (line == NULL) {
		/*
		** We got an EOF.
		** We arrange things so we don't have to treat this case
		** specially in the command interpreter below.
		*/

		line = MR_copy_string("quit");
	}

	if (MR_echo_commands) {
		fputs(line, stdout);
		putc('\n', stdout);
	}

	if ((semicolon = strchr(line, ';')) != NULL) {
		/*
		** The line contains at least two commands.
		** Execute only the first command now; put the others
		** back in the input to be processed later.
		*/

		*semicolon = '\0';
		MR_insert_line_at_head(MR_copy_string(semicolon+1));
	}

	problem = MR_trace_parse_line(line, &words, &word_max, &word_count);
	if (problem != NULL) {
		printf("%s\n", problem);
		goto return_keep_interacting;
	}

	if (word_count == 0) {
		alias_key = "EMPTY";
		alias_copy_start = 0;
	} else if (MR_trace_is_number(words[0], &n)) {
		alias_key = "NUMBER";
		alias_copy_start = 0;
	} else {
		alias_key = words[0];
		alias_copy_start = 1;
	}

	if (MR_trace_lookup_alias(alias_key, &alias_words, &alias_word_count))
	{
		MR_ensure_big_enough(alias_word_count, word, const char *,
			MR_INIT_WORD_COUNT);

		/* Move the original words (except the alias key) up. */
		for (i = word_count - 1; i >= alias_copy_start; i--) {
			words[i + alias_word_count - alias_copy_start]
				= words[i];
		}

		/* Move the alias body to the words array. */
		for (i = 0; i < alias_word_count; i++) {
			words[i] = alias_words[i];
		}

		word_count += alias_word_count - alias_copy_start;
	}

	/*
	** At this point, the first word_count members of the words
	** array contain the command. We save the value of words for
	** freeing just before return, since the variable words itself
	** can be overwritten by option processing.
	*/

	orig_words = words;
	if (word_count == 0) {
		cmd->MR_trace_cmd = MR_CMD_GOTO;
		cmd->MR_trace_stop_event = MR_trace_event_number + 1;
		cmd->MR_trace_strict = FALSE;
		cmd->MR_trace_print_level = MR_default_print_level;
		goto return_stop_interacting;
	} else if (MR_trace_is_number(words[0], &n)) {
		if (word_count == 1) {
			cmd->MR_trace_cmd = MR_CMD_GOTO;
			cmd->MR_trace_stop_event = MR_trace_event_number + n;
			cmd->MR_trace_strict = FALSE;
			cmd->MR_trace_print_level = MR_default_print_level;
			goto return_stop_interacting;
		} else {
			printf("One of the first two words "
				"must be a command.\n");
		}
	} else if (streq(words[0], "step")) {
		cmd->MR_trace_strict = FALSE;
		cmd->MR_trace_print_level = MR_default_print_level;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "step")) {
			; /* the usage message has already been printed */
		} else if (word_count == 1) {
			cmd->MR_trace_cmd = MR_CMD_GOTO;
			cmd->MR_trace_stop_event = MR_trace_event_number + 1;
			goto return_stop_interacting;
		} else if (word_count == 2
				&& MR_trace_is_number(words[1], &n)) {
			cmd->MR_trace_cmd = MR_CMD_GOTO;
			cmd->MR_trace_stop_event = MR_trace_event_number + n;
			goto return_stop_interacting;
		} else {
			MR_trace_help_cat_item("forward", "step");
		}
	} else if (streq(words[0], "goto")) {
		cmd->MR_trace_strict = TRUE;
		cmd->MR_trace_print_level = MR_default_print_level;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "goto")) {
			; /* the usage message has already been printed */
		} else if (word_count == 2 && MR_trace_is_number(words[1], &n))
				{
			if (MR_trace_event_number < n) {
				cmd->MR_trace_cmd = MR_CMD_GOTO;
				cmd->MR_trace_stop_event = n;
				goto return_stop_interacting;
			} else {
				printf("The debugger cannot go "
					"to a past event.\n");
			}
		} else {
			MR_trace_help_cat_item("forward", "goto");
		}
	} else if (streq(words[0], "finish")) {
		int	stop_depth;

		cmd->MR_trace_strict = TRUE;
		cmd->MR_trace_print_level = MR_default_print_level;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "finish")) {
			; /* the usage message has already been printed */
			goto return_keep_interacting;
		} else if (word_count == 2 && MR_trace_is_number(words[1], &n))
		{
			stop_depth = depth - n;
		} else if (word_count == 1) {
			stop_depth = depth;
		} else {
			MR_trace_help_cat_item("forward", "finish");
			goto return_keep_interacting;
		}

		if (depth == stop_depth && MR_port_is_final(port)) {
			printf("This command is a no-op from this port.\n");
		} else {
			cmd->MR_trace_cmd = MR_CMD_FINISH;
			cmd->MR_trace_stop_depth = stop_depth;
			goto return_stop_interacting;
		}
	} else if (streq(words[0], "return")) {
		cmd->MR_trace_strict = TRUE;
		cmd->MR_trace_print_level = MR_default_print_level;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "return")) {
			; /* the usage message has already been printed */
		} else if (word_count == 1) {
			if (port == MR_PORT_EXIT) {
				cmd->MR_trace_cmd = MR_CMD_RETURN;
				goto return_stop_interacting;
			} else {
				printf("This command is a no-op from this port.\n");
			}
		} else {
			MR_trace_help_cat_item("forward", "return");
		}
	} else if (streq(words[0], "forward")) {
		cmd->MR_trace_strict = TRUE;
		cmd->MR_trace_print_level = MR_default_print_level;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "forward")) {
			; /* the usage message has already been printed */
		} else if (word_count == 1) {
			if (port == MR_PORT_FAIL || port == MR_PORT_REDO) {
				cmd->MR_trace_cmd = MR_CMD_RESUME_FORWARD;
				goto return_stop_interacting;
			} else {
				printf("This command is a no-op from this port.\n");
			}
		} else {
			MR_trace_help_cat_item("forward", "forward");
		}
	} else if (streq(words[0], "mindepth")) {
		int	newdepth;

		cmd->MR_trace_strict = TRUE;
		cmd->MR_trace_print_level = MR_default_print_level;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "mindepth")) {
			; /* the usage message has already been printed */
		} else if (word_count == 2 &&
				MR_trace_is_number(words[1], &newdepth))
		{
			cmd->MR_trace_cmd = MR_CMD_MIN_DEPTH;
			cmd->MR_trace_stop_depth = newdepth;
			goto return_stop_interacting;
		} else {
			MR_trace_help_cat_item("forward", "mindepth");
		}
	} else if (streq(words[0], "maxdepth")) {
		int	newdepth;

		cmd->MR_trace_strict = TRUE;
		cmd->MR_trace_print_level = MR_default_print_level;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "maxdepth")) {
			; /* the usage message has already been printed */
		} else if (word_count == 2 &&
				MR_trace_is_number(words[1], &newdepth))
		{
			cmd->MR_trace_cmd = MR_CMD_MAX_DEPTH;
			cmd->MR_trace_stop_depth = newdepth;
			goto return_stop_interacting;
		} else {
			MR_trace_help_cat_item("forward", "maxdepth");
		}
	} else if (streq(words[0], "continue")) {
		cmd->MR_trace_strict = FALSE;
		cmd->MR_trace_print_level = (MR_Trace_Cmd_Type) -1;
		if (! MR_trace_options_strict_print(cmd, &words, &word_count,
				"forward", "continue")) {
			; /* the usage message has already been printed */
		} else if (word_count == 1) {
			cmd->MR_trace_cmd = MR_CMD_TO_END;
			if (cmd->MR_trace_print_level ==
					(MR_Trace_Cmd_Type) -1) {
				/*
				** The user did not specify the print level;
				** select the intelligent default.
				*/
				if (cmd->MR_trace_strict) {
					cmd->MR_trace_print_level =
						MR_PRINT_LEVEL_NONE;
				} else {
					cmd->MR_trace_print_level =
						MR_PRINT_LEVEL_SOME;
				}
			}
			goto return_stop_interacting;
		} else {
			MR_trace_help_cat_item("forward", "continue");
		}
	} else if (streq(words[0], "retry")) {
		int	stop_depth;

		if (word_count == 2 && MR_trace_is_number(words[1], &n)) {
			stop_depth = depth - n;
		} else if (word_count == 1) {
			stop_depth = depth;
		} else {
			MR_trace_help_cat_item("backward", "retry");
			goto return_keep_interacting;
		}

		if (stop_depth == depth && MR_port_is_final(port)) {
			MR_trace_retry(layout, saved_regs, seqno, depth,
				max_mr_num, jumpaddr);

			cmd->MR_trace_cmd = MR_CMD_GOTO;
			cmd->MR_trace_stop_event = MR_trace_event_number + 1;
			cmd->MR_trace_strict = FALSE;
			cmd->MR_trace_print_level = MR_default_print_level;
			goto return_stop_interacting;
		} else {
			char	*retry_cmd;

			/* Finish the call to be retried. */
			cmd->MR_trace_cmd = MR_CMD_FINISH;
			cmd->MR_trace_stop_depth = stop_depth;
			cmd->MR_trace_strict = TRUE;
			cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE;

			/* Arrange to retry the call once it is finished. */
			MR_insert_line_at_head("retry");
			goto return_stop_interacting;
		}
	} else if (streq(words[0], "level")) {
		if (word_count == 2 && MR_trace_is_number(words[1], &n)) {
			problem = MR_trace_browse_check_level(layout,
					saved_regs, n);
			if (problem == NULL) {
				*ancestor_level = n;
				printf("Ancestor level set to %d\n",
					*ancestor_level);
			} else {
				printf("%s\n", problem);
			}
		} else {
			MR_trace_help_cat_item("browsing", "level");
		}
	} else if (streq(words[0], "up")) {
		if (word_count == 2 && MR_trace_is_number(words[1], &n)) {
			problem = MR_trace_browse_check_level(layout,
					saved_regs, *ancestor_level + n);
			if (problem == NULL) {
				*ancestor_level += n;
				printf("Ancestor level set to %d\n",
					*ancestor_level);
			} else {
				printf("%s\n", problem);
			}
		} else if (word_count == 1) {
			problem = MR_trace_browse_check_level(layout,
					saved_regs, *ancestor_level + 1);
			if (problem == NULL) {
				*ancestor_level += 1;
				printf("Ancestor level set to %d\n",
					*ancestor_level);
			} else {
				printf("%s\n", problem);
			}
		} else {
			MR_trace_help_cat_item("browsing", "up");
		}
	} else if (streq(words[0], "down")) {
		if (word_count == 2 && MR_trace_is_number(words[1], &n)) {
			problem = MR_trace_browse_check_level(layout,
					saved_regs, *ancestor_level - n);
			if (problem == NULL) {
				*ancestor_level -= n;
				printf("Ancestor level set to %d\n",
					*ancestor_level);
			} else {
				printf("%s\n", problem);
			}
		} else if (word_count == 1) {
			problem = MR_trace_browse_check_level(layout,
					saved_regs, *ancestor_level - 1);
			if (problem == NULL) {
				*ancestor_level -= 1;
				printf("Ancestor level set to %d\n",
					*ancestor_level);
			} else {
				printf("%s\n", problem);
			}
		} else {
			MR_trace_help_cat_item("browsing", "down");
		}
	} else if (streq(words[0], "vars")) {
		if (word_count == 1) {
			MR_trace_list_vars(layout, saved_regs,
				*ancestor_level);
		} else {
			MR_trace_help_cat_item("browsing", "vars");
		}
	} else if (streq(words[0], "print")) {
		if (word_count == 2) {
			if (MR_trace_is_number(words[1], &n)) {
				MR_trace_browse_one(layout, saved_regs,
					*ancestor_level, n);
			} else if streq(words[1], "*") {
				MR_trace_browse_all(layout, saved_regs,
					*ancestor_level);
			} else {
				MR_trace_help_cat_item("browsing", "print");
			}
		} else {
			MR_trace_help_cat_item("browsing", "print");
		}
	} else if (streq(words[0], "stack")) {
		if (word_count == 1) {
			const char	*result;

			do_init_modules();
			result = MR_dump_stack_from_layout(stdout,
					layout->MR_sll_entry,
					MR_saved_sp(saved_regs),
					MR_saved_curfr(saved_regs));
			if (result != NULL) {
				printf("%s\n", result);
			}
		} else {
			MR_trace_help_cat_item("browsing", "stack");
		}
	} else if (streq(words[0], "current")) {
		if (word_count == 1) {
			MR_trace_event_print_internal_report(layout, port,
				seqno, depth, path);
		} else {
			MR_trace_help_cat_item("browsing", "current");
		}
	} else if (streq(words[0], "break")) {
		MR_Proc_Spec	spec;
		MR_Spy_When	when;
		MR_Spy_Action	action;

		if (word_count == 2 && streq(words[1], "info")) {
			for (i = 0; i < MR_spy_point_next; i++) {
				MR_print_spy_point(i);
			}

			goto return_keep_interacting;
		}

		when = MR_SPY_INTERFACE;
		action = MR_SPY_STOP;
		if (! MR_trace_options_when_action(&when, &action,
				&words, &word_count, "breakpoint", "break")) {
			; /* the usage message has already been printed */
		} else if (word_count == 2 && streq(words[1], "here")) {
			MR_register_all_modules_and_procs(stdout, TRUE);
			MR_trace_internal_add_spy_point(MR_SPY_SPECIFIC,
				action, layout->MR_sll_entry, layout, path);
		} else if (word_count == 2 &&
				MR_parse_proc_spec(words[1], &spec))
		{
			const MR_Stack_Layout_Entry	*spy_proc;
			bool				unique;

			MR_register_all_modules_and_procs(stdout, TRUE);
			spy_proc = MR_search_for_matching_procedure(&spec,
					&unique);
			if (spy_proc != NULL) {
				if (unique) {
					MR_trace_internal_add_spy_point(when,
						action, spy_proc, NULL, path);
				} else {
					printf("Ambiguous procedure "
						"specification. "
						"The matches are:\n");
					MR_process_matching_procedures(&spec,
						MR_print_proc_id_for_debugger);
				}
			} else {
				printf("There is no such procedure.\n");
			}
		} else {
			MR_trace_help_cat_item("breakpoint", "break");
		}
	} else if (streq(words[0], "enable")) {
		if (word_count == 2 && MR_trace_is_number(words[1], &n)) {
			if (0 <= n && n < MR_spy_point_next) {
				MR_spy_points[n]->spy_enabled = TRUE;
				MR_print_spy_point(n);
			} else {
				printf("Break point #%d does not exist.\n", n);
			}
		} else if (word_count == 2 && streq(words[1], "*")) {
			for (i = 0; i < MR_spy_point_next; i++) {
				MR_spy_points[i]->spy_enabled = TRUE;
				MR_print_spy_point(n);
			}

			if (MR_spy_point_next == 0) {
				printf("There no break points yet.\n");
			}
		} else {
			MR_trace_help_cat_item("breakpoint", "enable");
		}
	} else if (streq(words[0], "disable")) {
		if (word_count == 2 && MR_trace_is_number(words[1], &n)) {
			if (0 <= n && n < MR_spy_point_next) {
				MR_spy_points[n]->spy_enabled = FALSE;
				MR_print_spy_point(n);
			} else {
				printf("Break point #%d does not exist.\n", n);
			}
		} else if (word_count == 2 && streq(words[1], "*")) {
			for (i = 0; i < MR_spy_point_next; i++) {
				MR_spy_points[i]->spy_enabled = FALSE;
				MR_print_spy_point(n);
			}

			if (MR_spy_point_next == 0) {
				printf("There no break points yet.\n");
			}
		} else {
			MR_trace_help_cat_item("breakpoint", "disable");
		}
	} else if (streq(words[0], "register")) {
		bool	verbose;

		if (! MR_trace_options_quiet(&verbose, &words, &word_count,
				"breakpoint", "register")) {
			; /* the usage message has already been printed */
		} else if (word_count == 1) {
			MR_register_all_modules_and_procs(stdout, verbose);
		} else {
			MR_trace_help_cat_item("breakpoint", "register");
		}
	} else if (streq(words[0], "modules")) {
		if (word_count == 1) {
			MR_register_all_modules_and_procs(stdout, TRUE);
			MR_dump_module_list(stdout);
		} else {
			MR_trace_help_cat_item("breakpoint", "modules");
		}
	} else if (streq(words[0], "procedures")) {
		if (word_count == 2) {
			MR_register_all_modules_and_procs(stdout, TRUE);
			MR_dump_module_procs(stdout, words[1]);
		} else {
			MR_trace_help_cat_item("breakpoint", "procedures");
		}
	} else if (streq(words[0], "printlevel")) {
		if (word_count == 2) {
			if (streq(words[1], "none")) {
				MR_default_print_level = MR_PRINT_LEVEL_NONE;
				printf("Default print level set to none.\n");
			} else if (streq(words[1], "some")) {
				MR_default_print_level = MR_PRINT_LEVEL_SOME;
				printf("Default print level set to some.\n");
			} else if (streq(words[1], "all")) {
				MR_default_print_level = MR_PRINT_LEVEL_ALL;
				printf("Default print level set to all.\n");
			} else {
				MR_trace_help_cat_item("parameter", "printlevel");
			}
		} else if (word_count == 1) {
			printf("The default print level is ");
			switch (MR_default_print_level) {
				case MR_PRINT_LEVEL_NONE:
					printf("none.\n");
					break;
				case MR_PRINT_LEVEL_SOME:
					printf("some.\n");
					break;
				case MR_PRINT_LEVEL_ALL:
					printf("all.\n");
					break;
				default:
					printf("something weird.\n");
					break;
			}
		} else {
			MR_trace_help_cat_item("parameter", "printlevel");
		}
	} else if (streq(words[0], "scroll")) {
		if (word_count == 2) {
			if (streq(words[1], "off")) {
				MR_scroll_control = FALSE;
				printf("Scroll control disabled\n");
			} else if (streq(words[1], "on")) {
				MR_scroll_control = TRUE;
				printf("Scroll control enabled\n");
			} else if (MR_trace_is_number(words[1], &n)) {
				MR_scroll_limit = n;
				printf("Scroll window size set to %d\n",
					MR_scroll_limit);
			} else {
				MR_trace_help_cat_item("parameter", "scroll");
			}
		} else if (word_count == 1) {
			printf("Scroll control is ");
			if (MR_scroll_control) {
				printf("on");
			} else {
				printf("off");
			}
			printf(", scroll window size is %d\n", MR_scroll_limit);
		} else {
			MR_trace_help_cat_item("parameter", "scroll");
		}
	} else if (streq(words[0], "echo")) {
		if (word_count == 2) {
			if (streq(words[1], "off")) {
				MR_echo_commands = FALSE;
				printf("Command echo disabled\n");
			} else if (streq(words[1], "on")) {
				MR_echo_commands = TRUE;
				printf("Command echo enabled\n");
			} else {
				MR_trace_help_cat_item("parameter", "echo");
			}
		} else if (word_count == 1) {
			printf("Command echo is ");
			if (MR_echo_commands) {
				printf("on.\n");
			} else {
				printf("off.\n");
			}
		} else {
			MR_trace_help_cat_item("parameter", "echo");
		}
	} else if (streq(words[0], "alias")) {
		if (word_count == 1) {
			MR_trace_print_all_aliases(stdout);
		} else if (word_count == 2) {
			MR_trace_print_alias(stdout, words[1]);
		} else {
			if (MR_trace_valid_command(words[2])) {
				MR_trace_add_alias(words[1],
					words+2, word_count-2);
				MR_trace_print_alias(stdout, words[1]);
			} else {
				printf("%s is not a valid command\n", words[2]);
			}
		}
	} else if (streq(words[0], "unalias")) {
		if (word_count == 2) {
			if (MR_trace_remove_alias(words[1])) {
				printf("Alias %s removed.\n", words[1]);
			} else {
				printf("Alias %s cannot be removed, since it does not exist.\n", words[1]);
			}
		} else {
			MR_trace_help_cat_item("parameter", "unalias");
		}
	} else if (streq(words[0], "document_category")) {
		int		slot;
		const char	*msg;
		const char	*help_text;

		help_text = MR_trace_read_help_text();
		if (word_count != 3) {
			MR_trace_help_cat_item("help", "document_category");
		} else if (! MR_trace_is_number(words[1], &slot)) {
			MR_trace_help_cat_item("help", "document_category");
		} else {
			msg = MR_trace_add_cat(words[2], slot, help_text);
			if (msg != NULL) {
				printf("Document category %s not added: %s.\n",
					words[2], msg);
			}
		}
	} else if (streq(words[0], "document")) {
		int		slot;
		const char	*msg;
		const char	*help_text;

		help_text = MR_trace_read_help_text();
		if (word_count != 4) {
			MR_trace_help_cat_item("help", "document");
		} else if (! MR_trace_is_number(words[2], &slot)) {
			MR_trace_help_cat_item("help", "document");
		} else {
			msg = MR_trace_add_item(words[1], words[3], slot,
				help_text);
			if (msg != NULL) {
				printf("Document item %s in category %s"
					" not added: %s.\n",
					words[3], words[1], msg);
			}
		}
	} else if (streq(words[0], "help")) {
		if (word_count == 1) {
			MR_trace_help();
		} else if (word_count == 2) {
			MR_trace_help_word(words[1]);
		} else if (word_count == 3) {
			MR_trace_help_cat_item(words[1], words[2]);
		} else {
			MR_trace_help_cat_item("misc", "help");
		}
	} else if (streq(words[0], "histogram_all")) {
		if (word_count == 2) {
			FILE	*fp;

			fp = fopen(words[1], "w");
			if (fp == NULL) {
				perror(words[1]);
			} else {
				MR_trace_print_histogram(fp, "All-inclusive",
					MR_trace_histogram_all,
					MR_trace_histogram_hwm);
				fclose(fp);
			}
		} else {
			MR_trace_help_cat_item("exp", "histogram_all");
		}
	} else if (streq(words[0], "histogram_exp")) {
		if (word_count == 2) {
			FILE	*fp;

			if (fp == NULL) {
				perror(words[1]);
			} else {
				MR_trace_print_histogram(fp, "Experimental",
					MR_trace_histogram_exp,
					MR_trace_histogram_hwm);
				fclose(fp);
			}
		} else {
			MR_trace_help_cat_item("exp", "histogram_exp");
		}
	} else if (streq(words[0], "clear_histogram")) {
		if (word_count == 1) {
			for (i = 0; i <= MR_trace_histogram_hwm; i++) {
				MR_trace_histogram_exp[i] = 0;
			}
		} else {
			MR_trace_help_cat_item("exp", "clear_histogram");
		}
	} else if (streq(words[0], "nondet_stack")) {
		if (word_count == 1) {
			do_init_modules();
			MR_dump_nondet_stack_from_layout(stdout,
				MR_saved_maxfr(saved_regs));
		} else {
			MR_trace_help_cat_item("developer", "nondet_stack");
		}
	} else if (streq(words[0], "stack_regs")) {
		if (word_count == 1) {
			printf("sp = %p, curfr = %p, maxfr = %p\n",
				MR_saved_sp(saved_regs),
				MR_saved_curfr(saved_regs),
				MR_saved_maxfr(saved_regs));
		} else {
			MR_trace_help_cat_item("developer", "stack_regs");
		}
	} else if (streq(words[0], "source")) {
		if (word_count == 2) {
			MR_trace_source(words[1]);
		} else {
			MR_trace_help_cat_item("misc", "source");
		}
	} else if (streq(words[0], "quit")) {
		bool	confirmed;

		confirmed = FALSE;
		if (! MR_trace_options_confirmed(&confirmed,
				&words, &word_count, "misc", "quit")) {
			; /* the usage message has already been printed */
		} else if (word_count == 1) {
			if (! confirmed) {
				char	*line2;

				line2 = MR_trace_getline(
					"mdb: are you sure you want to quit? ",
					stdin);
				if (line2 == NULL) {
					/* This means the user input EOF. */
					confirmed = TRUE;
				} else {
					i = 0;
					while (line2[i] != '\0' &&
							MR_isspace(line2[i]))
					{
						i++;
					}

					if (line2[i] == 'y' || line2[i] == 'Y')
					{
						confirmed = TRUE;
					}

					free(line2);
				}
			}

			if (confirmed) {
				exit(EXIT_SUCCESS);
			}
		} else {
			MR_trace_help_cat_item("misc", "quit");
		}
	} else {
		printf("Unknown command `%s'. "
			"Give the command `help' for help.\n", words[0]);
	}

	/* fall through */
return_keep_interacting:
	free(line);
	free(orig_words);
	return KEEP_INTERACTING;

return_stop_interacting:
	free(line);
	free(orig_words);
	return STOP_INTERACTING;
}

static struct MR_option MR_trace_strict_print_opts[] =
{
	{ "all",	FALSE,	NULL,	'a' },
	{ "none",	FALSE,	NULL,	'n' },
	{ "some",	FALSE,	NULL,	's' },
	{ "nostrict",	FALSE,	NULL,	'N' },
	{ "strict",	FALSE,	NULL,	'S' }
};

static bool
MR_trace_options_strict_print(MR_Trace_Cmd_Info *cmd,
	char ***words, int *word_count, const char *cat, const char *item)
{
	int	c;

	MR_optind = 0;
	while ((c = MR_getopt_long(*word_count, *words, "NSans",
			MR_trace_strict_print_opts, NULL)) != EOF)
	{
		switch (c) {

			case 'N':
				cmd->MR_trace_strict = FALSE;
				break;

			case 'S':
				cmd->MR_trace_strict = TRUE;
				break;

			case 'a':
				cmd->MR_trace_print_level = MR_PRINT_LEVEL_ALL;
				break;

			case 'n':
				cmd->MR_trace_print_level = MR_PRINT_LEVEL_NONE;
				break;

			case 's':
				cmd->MR_trace_print_level = MR_PRINT_LEVEL_SOME;
				break;

			default:
				MR_trace_help_cat_item(cat, item);
				return FALSE;
		}
	}

	*words = *words + MR_optind - 1;
	*word_count = *word_count - MR_optind + 1;
	return TRUE;
}

static struct MR_option MR_trace_when_action_opts[] =
{
	{ "all",	FALSE,	NULL,	'a' },
	{ "entry",	FALSE,	NULL,	'e' },
	{ "interface",	FALSE,	NULL,	'i' },
	{ "print",	FALSE,	NULL,	'P' },
	{ "stop",	FALSE,	NULL,	'S' }
};

static bool
MR_trace_options_when_action(MR_Spy_When *when, MR_Spy_Action *action,
	char ***words, int *word_count, const char *cat, const char *item)
{
	int	c;

	MR_optind = 0;
	while ((c = MR_getopt_long(*word_count, *words, "PSaei",
			MR_trace_when_action_opts, NULL)) != EOF)
	{
		switch (c) {

			case 'a':
				*when = MR_SPY_ALL;
				break;

			case 'e':
				*when = MR_SPY_ENTRY;
				break;

			case 'i':
				*when = MR_SPY_INTERFACE;
				break;

			case 'P':
				*action = MR_SPY_PRINT;
				break;

			case 'S':
				*action = MR_SPY_STOP;
				break;

			default:
				MR_trace_help_cat_item(cat, item);
				return FALSE;
		}
	}

	*words = *words + MR_optind - 1;
	*word_count = *word_count - MR_optind + 1;
	return TRUE;
}

static bool
MR_trace_options_confirmed(bool *confirmed, char ***words, int *word_count,
	const char *cat, const char *item)
{
	int	c;

	MR_optind = 0;
	while ((c = MR_getopt(*word_count, *words, "NYny")) != EOF) {
		switch (c) {

			case 'n':
			case 'N':
				*confirmed = FALSE;
				break;

			case 'y':
			case 'Y':
				*confirmed = TRUE;
				break;

			default:
				MR_trace_help_cat_item(cat, item);
				return FALSE;
		}
	}

	*words = *words + MR_optind - 1;
	*word_count = *word_count - MR_optind + 1;
	return TRUE;
}

static struct MR_option MR_trace_quiet_opts[] =
{
	{ "quiet",	FALSE,	NULL,	'q' },
	{ "verbose",	FALSE,	NULL,	'v' }
};

static bool
MR_trace_options_quiet(bool *verbose, char ***words, int *word_count,
	const char *cat, const char *item)
{
	int	c;

	MR_optind = 0;
	while ((c = MR_getopt_long(*word_count, *words, "qv",
			MR_trace_quiet_opts, NULL)) != EOF)
	{
		switch (c) {

			case 'q':
				*verbose = FALSE;
				break;

			case 'v':
				*verbose = TRUE;
				break;

			default:
				MR_trace_help_cat_item(cat, item);
				return FALSE;
		}
	}

	*words = *words + MR_optind - 1;
	*word_count = *word_count - MR_optind + 1;
	return TRUE;
}

static void
MR_trace_retry(const MR_Stack_Layout_Label *this_label, Word *saved_regs,
	int seqno, int depth, int *max_mr_num, Code **jumpaddr)
{
	const MR_Stack_Layout_Entry	*entry;
	const MR_Stack_Layout_Label	*call_label;
	const MR_Stack_Layout_Vars	*input_args;
	Word				*args;
	int				arg_max;
	int				arg_num;
	Word				arg_value;
	int				i;
	bool				succeeded;

	entry = this_label->MR_sll_entry;
	call_label = entry->MR_sle_call_label;

	if (call_label->MR_sll_var_count < 0) {
		printf("Cannot perform retry because information about "
			"the input arguments is not available.\n");
		return;
	}

	input_args = &call_label->MR_sll_var_info;

	/*
	** With the Boehm collector, args need not be considered a root, 
	** since its contents are just copies of values from elsewhere,
	** With the native collector, it need not be considered a root
	** because its lifetime spans only this function, in which
	** no native garbage collection can be triggered.
	*/

	args = NULL;
	arg_max = 0;

	for (i = 0; i < call_label->MR_sll_var_count; i++) {
		arg_value = MR_trace_find_input_arg(this_label, saved_regs,
				input_args->MR_slvs_names[i], &succeeded);

		if (! succeeded) {
			printf("Cannot perform retry because the values of "
				"some input arguments are missing.\n");
			return;
		}

		arg_num = MR_get_register_number(
			input_args->MR_slvs_pairs[i].MR_slv_locn);
		if (arg_num > 0) {
			MR_ensure_big_enough(arg_num, arg, Word,
				MR_INIT_ARG_COUNT);
			args[arg_num] = arg_value;
		} else {
			fatal_error("illegal location for input argument");
		}
	}

	MR_trace_saved_call_seqno = seqno - 1;
	MR_trace_saved_call_depth = depth - 1;

	if (MR_DETISM_DET_STACK(entry->MR_sle_detism)) {
		MR_Live_Lval	location;
		Word		*this_frame;

		/*
		** We are at a final port, so both curfr and maxfr
		** must already have been reset to their original values.
		** We only need to set up the succip register for the "call",
		** and then remove this frame from the det stack.
		*/

		location = entry->MR_sle_succip_locn;
		if (MR_LIVE_LVAL_TYPE(location) != MR_LVAL_TYPE_STACKVAR) {
			fatal_error("illegal location for stored succip");
		}

		this_frame = MR_saved_sp(saved_regs);
		MR_saved_succip(saved_regs) = MR_based_stackvar(this_frame,
						MR_LIVE_LVAL_NUMBER(location));
		MR_saved_sp(saved_regs) -= entry->MR_sle_stack_slots;
		MR_trace_saved_event_number =
				MR_event_num_stackvar(this_frame);
	} else {
		Word	*this_frame;

		/*
		** We are at a final port, so sp must already have been reset
		** to its original value. We only need to set up the succip
		** and curfr registers for the "call", and remove this frame,
		** and any other frames above it, from the nondet stack.
		*/

		this_frame = MR_saved_curfr(saved_regs);

		MR_saved_succip(saved_regs) = MR_succip_slot(this_frame);
		MR_saved_curfr(saved_regs) = MR_succfr_slot(this_frame);
		MR_saved_maxfr(saved_regs) = MR_prevfr_slot(this_frame);
		MR_trace_saved_event_number =
				MR_event_num_framevar(this_frame);
	}

	for (i = 1; i < arg_max; i++) {
		saved_reg(saved_regs, i) = args[i];
	}

	if (args != NULL) {
		free(args);
	}

	*max_mr_num = max(*max_mr_num, arg_max);
	*jumpaddr = entry->MR_sle_code_addr;

	MR_trace_call_seqno = MR_trace_saved_call_seqno;
	MR_trace_call_depth = MR_trace_saved_call_depth;
	MR_trace_event_number = MR_trace_saved_event_number;
}

static Word
MR_trace_find_input_arg(const MR_Stack_Layout_Label *label, Word *saved_regs,
	const char *name, bool *succeeded)
{
	const MR_Stack_Layout_Vars	*vars;
	int				i;

	vars = &label->MR_sll_var_info;
	if (vars->MR_slvs_names == NULL) {
		*succeeded = FALSE;
		return 0;
	}

	for (i = 0; i < label->MR_sll_var_count; i++) {
		if (streq(vars->MR_slvs_names[i], name)) {
			return MR_lookup_live_lval_base(
				vars->MR_slvs_pairs[i].MR_slv_locn, saved_regs,
				MR_saved_sp(saved_regs),
				MR_saved_curfr(saved_regs), succeeded);
		}
	}

	*succeeded = FALSE;
	return 0;
}

static void
MR_trace_internal_add_spy_point(MR_Spy_When when, MR_Spy_Action action,
	const MR_Stack_Layout_Entry *entry, const MR_Stack_Layout_Label *label,
	const char *path)
{
	MR_Spy_Point	*spy_point;

	MR_ensure_room_for_next(MR_spy_point, MR_Spy_Point *,
		MR_INIT_SPY_POINTS);
	spy_point = MR_add_spy_point(when, action, entry, label, path);
	MR_spy_points[MR_spy_point_next] = spy_point;
	MR_print_spy_point(MR_spy_point_next);
	MR_spy_point_next++;
}

static void
MR_print_spy_point(int spy_point_num)
{
	printf("%2d: %1s %-5s %9s ",
		spy_point_num,
		MR_spy_points[spy_point_num]->spy_enabled ? "+" : "-",
		MR_spy_action_string(MR_spy_points[spy_point_num]->spy_action),
		MR_spy_when_string(MR_spy_points[spy_point_num]->spy_when));
	MR_print_proc_id_for_debugger(MR_spy_points[spy_point_num]->spy_proc);
}

static void
MR_trace_list_vars(const MR_Stack_Layout_Label *top_layout, Word *saved_regs,
	int ancestor_level)
{
	const MR_Stack_Layout_Label	*level_layout;
	Word				*base_sp;
	Word				*base_curfr;
	Word				*type_params;
	int				var_count;
	const MR_Stack_Layout_Vars	*vars;
	int				i;
	const char 			*problem;

	base_sp = MR_saved_sp(saved_regs);
	base_curfr = MR_saved_curfr(saved_regs);
	level_layout = MR_find_nth_ancestor(top_layout, ancestor_level,
				&base_sp, &base_curfr, &problem);

	if (level_layout == NULL) {
		printf("%s\n", problem);
		return;
	}

	var_count = (int) level_layout->MR_sll_var_count;
	if (var_count < 0) {
		printf("mdb: there is no information about live variables\n");
		return;
	} else if (var_count == 0) {
		printf("mdb: there are no live variables\n");
		return;
	}

	vars = &level_layout->MR_sll_var_info;
	for (i = 0; i < var_count; i++) {
		printf("%9d %s\n", i, MR_name_if_present(vars, i));
	}
}

static const char *
MR_trace_browse_check_level(const MR_Stack_Layout_Label *top_layout,
	Word *saved_regs, int ancestor_level)
{
	Word				*base_sp;
	Word				*base_curfr;
	const char 			*problem;

	base_sp = MR_saved_sp(saved_regs);
	base_curfr = MR_saved_curfr(saved_regs);
	if (MR_find_nth_ancestor(top_layout, ancestor_level,
			&base_sp, &base_curfr, &problem) == NULL)
	{
		return problem;
	} else {
		return NULL;
	}
}

static void
MR_trace_browse_one(const MR_Stack_Layout_Label *top_layout,
	Word *saved_regs, int ancestor_level, int which_var)
{
	const MR_Stack_Layout_Label	*level_layout;
	Word				*base_sp;
	Word				*base_curfr;
	Word				*type_params;
	Word				*valid_saved_regs;
	int				var_count;
	const MR_Stack_Layout_Vars	*vars;
	const char 			*problem;

	base_sp = MR_saved_sp(saved_regs);
	base_curfr = MR_saved_curfr(saved_regs);
	level_layout = MR_find_nth_ancestor(top_layout, ancestor_level,
				&base_sp, &base_curfr, &problem);

	if (level_layout == NULL) {
		printf("%s\n", problem);
		return;
	}

	var_count = (int) level_layout->MR_sll_var_count;
	if (var_count < 0) {
		printf("mdb: there is no information about live variables\n");
		return;
	} else if (which_var >= var_count) {
		printf("mdb: there is no such variable\n");
		return;
	}

	vars = &level_layout->MR_sll_var_info;
	if (ancestor_level == 0) {
		valid_saved_regs = saved_regs;
	} else {
		valid_saved_regs = NULL;
	}

	type_params = MR_materialize_typeinfos_base(vars,
				valid_saved_regs, base_sp, base_curfr);
	MR_trace_browse_var(MR_name_if_present(vars, which_var),
		&vars->MR_slvs_pairs[which_var], valid_saved_regs,
		base_sp, base_curfr, type_params);
	free(type_params);
}

static void 
MR_trace_browse_all(const MR_Stack_Layout_Label *top_layout,
	Word *saved_regs, int ancestor_level)
{
	const MR_Stack_Layout_Label	*level_layout;
	Word				*base_sp;
	Word				*base_curfr;
	Word				*type_params;
	Word				*valid_saved_regs;
	int				var_count;
	const MR_Stack_Layout_Vars	*vars;
	const char 			*problem;
	int				i;

	base_sp = MR_saved_sp(saved_regs);
	base_curfr = MR_saved_curfr(saved_regs);
	level_layout = MR_find_nth_ancestor(top_layout, ancestor_level,
				&base_sp, &base_curfr, &problem);

	if (level_layout == NULL) {
		printf("%s\n", problem);
		return;
	}

	var_count = (int) level_layout->MR_sll_var_count;
	if (var_count < 0) {
		printf("mdb: there is no information about live variables\n");
		return;
	} else if (var_count == 0) {
		printf("mdb: there are no live variables\n");
		return;
	}

	vars = &level_layout->MR_sll_var_info;
	if (ancestor_level == 0) {
		valid_saved_regs = saved_regs;
	} else {
		valid_saved_regs = NULL;
	}

	type_params = MR_materialize_typeinfos_base(vars,
				valid_saved_regs, base_sp, base_curfr);

	for (i = 0; i < var_count; i++) {
		MR_trace_browse_var(MR_name_if_present(vars, i),
			&vars->MR_slvs_pairs[i], valid_saved_regs,
			base_sp, base_curfr, type_params);
	}

	free(type_params);
}

static void
MR_trace_browse_var(const char *name, const MR_Stack_Layout_Var *var,
	Word *saved_regs, Word *base_sp, Word *base_curfr, Word *type_params)
{
	Word	value;
	Word	type_info;
	bool	print_value;
	int	i;

	/*
	** XXX The printing of type_infos is buggy at the moment
	** due to the fake arity of the type private_builtin:typeinfo/1.
	**
	** XXX The printing of large data structures is painful
	** at the moment due to the lack of a true browser.
	*/

	if ((strncmp(name, "TypeInfo", 8) == 0)
	|| (strncmp(name, "ModuleInfo", 10) == 0)
	|| (strncmp(name, "HLDS", 4) == 0))
		return;

	/* The initial blanks are to visually separate */
	/* the variable names from the prompt. */

	if (name != NULL) {
		printf("%7s%-21s\t", "", name);
	} else {
		printf("%7s%-21s\t", "", "anonymous variable");
	}

	fflush(stdout);

	/*
	** "variables" representing the saved values of succip, hp etc,
	** which are the "variables" for which get_type_and_value fails,
	** are not of interest to the user.
	*/

	if (MR_get_type_and_value_base(var, saved_regs,
			base_sp, base_curfr, type_params, &type_info, &value))
	{
		printf("\t");
		MR_write_variable(type_info, value);
	}

	printf("\n");
}

/*
** Read lines until we find one that contains only "end".
** Return the lines concatenated together.
*/

static const char *
MR_trace_read_help_text(void)
{
	char	*text;
	char	*doc_chars = NULL;
	int	doc_char_max = 0;
	int	next_char_slot;
	int	line_len;
	int	i;

	next_char_slot = 0;
	while ((text = MR_trace_getline("cat> ", stdin)) != NULL) {
		if (streq(text, "end")) {
			free(text);
			break;
		}

		line_len = strlen(text);
		MR_ensure_big_enough(next_char_slot + line_len + 2,
			doc_char, char, MR_INIT_DOC_CHARS);
		for (i = 0; i < line_len; i++) {
			doc_chars[next_char_slot + i] = text[i];
		}

		next_char_slot += line_len;
		doc_chars[next_char_slot] = '\n';
		next_char_slot += 1;
		free(text);
	}

	doc_chars[next_char_slot] = '\0';
	return doc_chars;
}

/*
** Is the string pointed to by word an integer?
** If yes, return its value in *value.
*/

static bool
MR_trace_is_number(char *word, int *value)
{
	if (MR_isdigit(*word)) {
		*value = *word - '0';
		word++;
		while (MR_isdigit(*word)) {
			*value = (*value * 10) + *word - '0';
			word++;
		}

		if (*word == '\0') {
			return TRUE;
		}
	}

	return FALSE;
}

/*
** Given a text line, break it up into words composed of non-space characters
** separated by space characters. Make each word a NULL-terminated string,
** overwriting some spaces in the line array in the process.
**
** If the first word is a number but the second is not, swap the two.
** If the first word has a number prefix, separate it out.
**
** On return *words will point to an array of strings, with space for
** *words_max strings. The number of strings (words) filled in will be
** given by the return value.
**
** The lifetime of the elements of the *words array expires when
** the line array is freed or further modified or when MR_trace_parse_line
** is called again, whichever comes first.
*/

/* If a number is more than 80 chars long, the user is in trouble. */
#define	MR_NUMBER_LEN	80

static const char *
MR_trace_parse_line(char *line, char ***words, int *word_max, int *word_count)
{
	char		**raw_words;
	int		raw_word_max;
	char		raw_word_count;
	static char	count_buf[MR_NUMBER_LEN];
	char		*s;
	int		i;

	/*
	** Handle a possible number prefix on the first word on the line,
	** separating it out into a word on its own.
	*/

	raw_word_count = MR_trace_break_into_words(line,
				&raw_words, &raw_word_max);

	if (raw_word_count > 0 && MR_isdigit(*raw_words[0])) {
		i = 0;
		s = raw_words[0];
		while (MR_isdigit(*s)) {
			if (i >= MR_NUMBER_LEN) {
				return "too large a number";
			}

			count_buf[i] = *s;
			i++;
			s++;
		}

		count_buf[i] = '\0';

		if (*s != '\0') {
			/* Only part of the first word constitutes a number. */
			/* Put it in an extra word at the start. */
			MR_ensure_big_enough(raw_word_count, raw_word,
				char **, MR_INIT_WORD_COUNT);

			for (i = raw_word_count; i > 0; i--) {
				raw_words[i] = raw_words[i-1];
			}

			raw_words[0] = count_buf;
			raw_words[1] = s;
			raw_word_count++;
		}
	}

	/*
	** If the first word is a number, try to exchange it
	** with the command word, to put the command word first.
	*/

	if (raw_word_count > 1 && MR_trace_is_number(raw_words[0], &i)
			&& ! MR_trace_is_number(raw_words[1], &i)) {
		s = raw_words[0];
		raw_words[0] = raw_words[1];
		raw_words[1] = s;
	}

	*words = raw_words;
	*word_max = raw_word_max;
	*word_count = raw_word_count;
	return NULL;
}

/*
** Given a text line, break it up into words composed of non-space characters
** separated by space characters. Make each word a NULL-terminated string,
** overwriting some spaces in the line array in the process.
**
** On return *words will point to an array of strings, with space for
** *words_max strings. The number of strings filled in will be given by
** the return value.
*/

static int
MR_trace_break_into_words(char *line, char ***words_ptr, int *word_max_ptr)
{
	int	word_max;
	char	**words;
	int	token_number;
	int	char_pos;
	int	int_val;

	token_number = 0;
	char_pos = 0;

	word_max = 0;
	words = NULL;

	/* each iteration of this loop processes one token, or end of line */
	for (;;) {
		while (line[char_pos] != '\0' && MR_isspace(line[char_pos])) {
			char_pos++;
		}

		if (line[char_pos] == '\0') {
			*words_ptr = words;
			*word_max_ptr = word_max;
			return token_number;
		}

		MR_ensure_big_enough(token_number, word, char **,
			MR_INIT_WORD_COUNT);
		words[token_number] = line + char_pos;

		while (line[char_pos] != '\0' && !MR_isspace(line[char_pos])) {
			char_pos++;
		}

		if (line[char_pos] != '\0') {
			line[char_pos] = '\0';
			char_pos++;
		}

		token_number++;
	}
}

static void
MR_trace_source(const char *filename)
{
	FILE	*fp;

	if ((fp = fopen(filename, "r")) != NULL) {
		MR_trace_source_from_open_file(fp);
		fclose(fp);
	} else {
		perror(filename);
		/*
		** We actually want to write to stdout, but the following
		** is too Unix-specific:

		if (errno < sys_nerr) {
			printf("%s: %s\n", filename, sys_errlist[errno]);
		} else {
			printf("Cannot open %s: unknown error\n", filename);
		}
		*/
	}
}

static void
MR_trace_source_from_open_file(FILE *fp)
{
	char	*line;

	while ((line = MR_trace_getline_raw(fp)) != NULL) {
		MR_insert_line_at_tail(line);
	}
}

/*
** If there any lines waiting in the queue, return the first of these.
** If not, print the prompt, read a line from the given file, and return it
** in a malloc'd buffer holding the line (without the final newline).
** If EOF occurs on a nonempty line, treat the EOF as a newline; if EOF
** occurs on an empty line, return NULL.
*/

static char *
MR_trace_getline(const char *prompt, FILE *fp)
{
	char	*line;

	line = MR_trace_getline_queue();
	if (line != NULL) {
		return line;
	}

	printf("%s", prompt);
	fflush(stdout);

	return MR_trace_getline_raw(fp);
}

/*
** If there any lines waiting in the queue, return the first of these.
*/

static char *
MR_trace_getline_queue(void)
{
	if (MR_line_head != NULL) {
		MR_Line	*old;
		char	*contents;

		old = MR_line_head;
		contents = MR_line_head->MR_line_contents;
		MR_line_head = MR_line_head->MR_line_next;
		if (MR_line_head == NULL) {
			MR_line_tail = NULL;
		}

		free(old);
		return contents;
	} else {
		return NULL;
	}
}

/*
**	Read a line from a file, and return a pointer to a malloc'd buffer
**	holding the line (without the final newline). If EOF occurs on a
**	nonempty line, treat the EOF as a newline; if EOF occurs on an empty
**	line, return NULL.
*/

static char *
MR_trace_getline_raw(FILE *fp)
{
	char	*contents;
	int	content_max;
	int	c;
	int	i;

	contents = NULL;
	content_max = 0;

	i = 0;
	while ((c = getc(fp)) != EOF && c != '\n') {
		MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN);
		contents[i++] = c;
	}

	if (c == '\n' || i > 0) {
		MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN);
		contents[i] = '\0';
		return contents;
	} else {
		free(contents);
		return NULL;
	}
}

static void
MR_insert_line_at_head(const char *contents)
{
	MR_Line	*line;

	line = checked_malloc(sizeof(MR_Line));
	line->MR_line_contents = MR_copy_string(contents);
	line->MR_line_next = MR_line_head;

	MR_line_head = line;
	if (MR_line_tail == NULL) {
		MR_line_tail = MR_line_head;
	}
}

static void
MR_insert_line_at_tail(const char *contents)
{
	MR_Line	*line;
	char	*copy;
	int	len;

	len = strlen(contents);
	copy = checked_malloc(len + 1);
	strcpy(copy, contents);

	line = checked_malloc(sizeof(MR_Line));
	line->MR_line_contents = copy;
	line->MR_line_next = NULL;

	if (MR_line_tail == NULL) {
		MR_line_tail = line;
		MR_line_head = line;
	} else {
		MR_line_tail->MR_line_next = line;
		MR_line_tail = line;
	}
}

static Code *
MR_trace_event_internal_report(MR_Trace_Cmd_Info *cmd,
	const MR_Stack_Layout_Label *layout, Word *saved_regs,
	MR_Trace_Port port, int seqno, int depth, const char *path,
	int *max_mr_num)
{
	char	*buf;
	int	i;

	/* We try to leave one line for the prompt itself. */
	if (MR_scroll_control && MR_scroll_next >= MR_scroll_limit - 1) {
	try_again:
		buf = MR_trace_getline("--more-- ", stdin);
		if (buf != NULL) {
			for (i = 0; buf[i] != '\0' && MR_isspace(buf[i]); i++)
				;
			
			if (buf[i] != '\0' && !MR_isspace(buf[i])) {
				switch (buf[i]) {
					case 'a':
						cmd->MR_trace_print_level =
							MR_PRINT_LEVEL_ALL;
						break;

					case 'n':
						cmd->MR_trace_print_level =
							MR_PRINT_LEVEL_NONE;
						break;

					case 's':
						cmd->MR_trace_print_level =
							MR_PRINT_LEVEL_SOME;
						break;

					case 'q':
						free(buf);
						return MR_trace_event_internal(
								cmd, TRUE,
								layout,
								saved_regs,
								port,
								seqno, depth,
								path,
								max_mr_num);

					default:
						printf("unknown command, "
							"try again\n");
						free(buf);
						goto try_again;
				}
			}

			free(buf);
		}

		MR_scroll_next = 0;
	}

	MR_trace_event_print_internal_report(layout, port, seqno, depth, path);
	MR_scroll_next++;

	return NULL;
}

void
MR_trace_event_print_internal_report(const MR_Stack_Layout_Label *layout,
	MR_Trace_Port port, int seqno, int depth, const char *path)
{
	printf("%8ld: %6ld %2ld ",
		(long) MR_trace_event_number, (long) seqno, (long) depth);

	MR_trace_print_port(port);

	/*
	** The following should be a full identification of the procedure
	** provided (a) there was no intermodule optimization and (b) we are
	** not interested in tracing compiler-generated procedures.
	*/

	MR_print_proc_id(stdout, layout->MR_sll_entry, path);
}

static void
MR_trace_print_port(MR_Trace_Port port)
{
	switch (port) {
		case MR_PORT_CALL:
			printf("CALL ");
			break;

		case MR_PORT_EXIT:
			printf("EXIT ");
			break;

		case MR_PORT_REDO:
			printf("REDO ");
			break;

		case MR_PORT_FAIL:
			printf("FAIL ");
			break;

		case MR_PORT_THEN:
			printf("THEN ");
			break;

		case MR_PORT_ELSE:
			printf("ELSE ");
			break;

		case MR_PORT_DISJ:
			printf("DISJ ");
			break;

		case MR_PORT_SWITCH:
			printf("SWTC ");
			break;

		case MR_PORT_PRAGMA_FIRST:
			printf("FRST ");
			break;

		case MR_PORT_PRAGMA_LATER:
			printf("LATR ");
			break;

		default:
			fatal_error("MR_trace_event_internal called "
					"with bad port");
	}
}

typedef struct
{
	const char	*cat;
	const char	*item;
} MR_trace_cmd_cat_item;

static	MR_trace_cmd_cat_item MR_trace_valid_command_list[] =
{
	/* The following block is a verbatim copy of doc/mdb_command_list. */
	/* We do not use a #include to avoid adding a dependency. */
	{ "forward", "step" },
	{ "forward", "goto" },
	{ "forward", "finish" },
	{ "forward", "return" },
	{ "forward", "forward" },
	{ "forward", "mindepth" },
	{ "forward", "maxdepth" },
	{ "forward", "continue" },
	{ "backward", "retry" },
	{ "browsing", "vars" },
	{ "browsing", "print" },
	{ "browsing", "stack" },
	{ "browsing", "up" },
	{ "browsing", "down" },
	{ "browsing", "level" },
	{ "browsing", "current" },
	{ "breakpoint", "break" },
	{ "breakpoint", "disable" },
	{ "breakpoint", "enable" },
	{ "breakpoint", "modules" },
	{ "breakpoint", "procedures" },
	{ "breakpoint", "register" },
	{ "parameter", "printlevel" },
	{ "parameter", "echo" },
	{ "parameter", "scroll" },
	{ "parameter", "alias" },
	{ "parameter", "unalias" },
	{ "help", "document_category" },
	{ "help", "document" },
	{ "help", "help" },
	{ "exp", "histogram_all" },
	{ "exp", "histogram_exp" },
	{ "exp", "clear_histogram" },
	{ "developer", "nondet_stack" },
	{ "developer", "stack_regs" },
	{ "misc", "source" },
	{ "misc", "quit" },
	/* End of doc/mdb_command_list. */
	{ NULL, "NUMBER" },
	{ NULL, "EMPTY" },
	{ NULL, NULL },
};

static bool
MR_trace_valid_command(const char *word)
{
	int	i;

	for (i = 0; MR_trace_valid_command_list[i].item != NULL; i++) {
		if (streq(MR_trace_valid_command_list[i].item, word)) {
			return TRUE;
		}
	}

	return FALSE;
}
Index: trace/mercury_trace_internal.h
===================================================================
RCS file: /home/mercury1/repository/mercury/trace/mercury_trace_internal.h,v
retrieving revision 1.1
diff -u -u -r1.1 mercury_trace_internal.h
--- mercury_trace_internal.h	1998/09/29 05:11:57	1.1
+++ mercury_trace_internal.h	1998/09/29 06:58:58
@@ -7,16 +7,11 @@
SUPPORT RETRY
USE FIXED STACK SLOTS FOR TRACE INFO
 #ifndef	MERCURY_TRACE_INTERNAL_H
 #define	MERCURY_TRACE_INTERNAL_H
 
-extern	void	MR_trace_event_internal(MR_trace_cmd_info *cmd,
+extern	Code	*MR_trace_event_internal(MR_Trace_Cmd_Info *cmd,
+			bool interactive,
 			const MR_Stack_Layout_Label *layout,
-			MR_trace_port port, int seqno, int depth,
-			const char *path);
-
-extern	void	MR_trace_event_internal_report(
-			const MR_Stack_Layout_Label *layout,
-			MR_trace_port port, int seqno, int depth,
-			const char *path);
-
-extern	bool	MR_event_matches_spy_point(const MR_Stack_Layout_Label *layout);
+			Word *saved_regs, MR_Trace_Port port,
+			int seqno, int depth,
+			const char *path, int *max_mr_num);
 
 #endif	/* MERCURY_TRACE_INTERNAL_H */
Index: trace/mercury_trace_spy.c
===================================================================
RCS file: mercury_trace_spy.c
diff -N mercury_trace_spy.c
--- /dev/null	Wed May 28 10:49:58 1997
+++ mercury_trace_spy.c	Mon Sep 21 20:03:43 1998
IMPLEMENT NEW DEBUGGER COMMAND SET
@@ -0,0 +1,181 @@
+/*
+** Copyright (C) 1998 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** This file contains code to manage spy points for both
+** the internal and external debuggers.
+**
+** Main author: Zoltan Somogyi.
+*/
+
+#undef	USE_GCC_GLOBAL_REGISTERS
+#include "mercury_imp.h"
+#include "mercury_trace_base.h"
+#include "mercury_trace.h"
+#include "mercury_trace_spy.h"
+#include "mercury_macros.h"
+
+typedef struct {
+	const MR_Stack_Layout_Entry	*spy_proc;
+	MR_Spy_Point			*spy_points;
+} MR_Spied_Proc;
+
+static	MR_Spied_Proc	*MR_spied_procs;
+static	int		MR_spied_proc_next = 0;
+static	int		MR_spied_proc_max = 0;
+
+#define	INIT_SPY_TABLE_SIZE	10
+
+		/* Return the index of the entry in MR_spied_procs whose   */
+		/* spy_proc field is entry, or a negative number if absent */
+static	int	MR_search_spy_table_for_proc(const MR_Stack_Layout_Entry
+			*entry);
+
+static int
+MR_search_spy_table_for_proc(const MR_Stack_Layout_Entry *entry)
+{
+	int				lo;
+	int				hi;
+	int				mid;
+
+	lo = 0;
+	hi = MR_spied_proc_next - 1;
+	while (lo <= hi) {
+		mid = (lo + hi) / 2;
+		if (MR_spied_procs[mid].spy_proc == entry) {
+			return mid;
+		} else if (MR_spied_procs[mid].spy_proc < entry) {
+			lo = mid + 1;
+		} else {
+			hi = mid - 1;
+		}
+	}
+
+	return -1;
+}
+
+bool
+MR_event_matches_spy_point(const MR_Stack_Layout_Label *layout,
+	MR_Trace_Port port, MR_Spy_Action *action_ptr)
+{
+	int				slot;
+	bool				enabled;
+	MR_Spy_Point			*point;
+	MR_Spy_Action			action;
+
+	slot = MR_search_spy_table_for_proc(layout->MR_sll_entry);
+	if (slot < 0) {
+		return FALSE;
+	}
+
+	enabled = FALSE;
+	action = MR_SPY_PRINT;
+	for (point = MR_spied_procs[slot].spy_points; point != NULL;
+			point = point->spy_next) {
+		if (! point->spy_enabled) {
+			continue;
+		}
+
+		switch (point->spy_when) {
+
+			case MR_SPY_ALL:
+				enabled = TRUE;
+				action = max(action, point->spy_action);
+				break;
+
+			case MR_SPY_ENTRY:
+				if (MR_port_is_entry(port)) {
+					enabled = TRUE;
+					action = max(action, point->spy_action);
+				} else {
+					continue;
+				}
+
+				break;
+
+			case MR_SPY_INTERFACE:
+				if (MR_port_is_interface(port)) {
+					enabled = TRUE;
+					action = max(action, point->spy_action);
+				} else {
+					continue;
+				}
+
+				break;
+
+			case MR_SPY_SPECIFIC:
+				if (layout == point->spy_label) {
+					enabled = TRUE;
+					action = max(action, point->spy_action);
+				} else {
+					continue;
+				}
+
+				break;
+
+			default:
+				fatal_error("bad spy point when in "
+						"MR_event_matches_spy_point");
+		}
+	}
+
+	if (enabled) {
+		*action_ptr = action;
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+static bool MR_need_move(int i, const MR_Stack_Layout_Entry *entry);
+
+MR_Spy_Point *
+MR_add_spy_point(MR_Spy_When when, MR_Spy_Action action,
+	const MR_Stack_Layout_Entry *entry, const MR_Stack_Layout_Label *label,
+	const char *path)
+{
+	MR_Spy_Point	*point;
+	int		i;
+	int		slot;
+	bool		found;
+
+	slot = MR_search_spy_table_for_proc(entry);
+	if (slot < 0) {
+		MR_ensure_room_for_next(MR_spied_proc, MR_Spied_Proc,
+			INIT_SPY_TABLE_SIZE);
+		MR_prepare_insert_into_sorted(MR_spied_procs,
+			MR_spied_proc_next, slot, MR_need_move(i, entry));
+		MR_spied_procs[slot].spy_proc = entry;
+		MR_spied_procs[slot].spy_points = NULL;
+	}
+
+	/* Insert the spy point at the head of the list for the proc. */
+	point = checked_malloc(sizeof(MR_Spy_Point));
+	point->spy_when    = when;
+	point->spy_enabled = TRUE;
+	point->spy_action  = action;
+	point->spy_proc    = entry;
+	point->spy_label   = label;
+	point->spy_next    = MR_spied_procs[slot].spy_points;
+	MR_spied_procs[slot].spy_points = point;
+
+	return point;
+}
+
+/*
+** The only reason why MR_need_move is a function is that gcc version 2.7.2.3
+** on i686-pc-linux-gnu gets an internal error ("fixed or forbidden register
+** was spilled") if it isn't a function.
+*/
+
+static bool
+MR_need_move(int i, const MR_Stack_Layout_Entry *entry)
+{
+	if (MR_spied_procs[i].spy_proc > entry)
+		return TRUE;
+	else
+		return FALSE;
+}
Index: trace/mercury_trace_spy.h
===================================================================
RCS file: mercury_trace_spy.h
diff -N mercury_trace_spy.h
--- /dev/null	Wed May 28 10:49:58 1997
+++ mercury_trace_spy.h	Tue Sep 22 12:30:04 1998
IMPLEMENT NEW DEBUGGER COMMAND SET
@@ -0,0 +1,56 @@
+/*
+** Copyright (C) 1998 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** This file contains the declarations of the types and functions that
+** the internal and external debuggers can use to manipulate spy points.
+**
+** Main author: Zoltan Somogyi.
+*/
+
+#ifndef	MERCURY_TRACE_SPY_H
+#define	MERCURY_TRACE_SPY_H
+
+typedef enum {
+	MR_SPY_PRINT, MR_SPY_STOP
+} MR_Spy_Action;
+
+#define	MR_spy_action_string(a)		((a == MR_SPY_STOP) ? "stop" :      \
+					(a == MR_SPY_PRINT) ? "print" :     \
+					"weird spy action")
+
+typedef enum {
+	MR_SPY_ALL, MR_SPY_INTERFACE, MR_SPY_ENTRY, MR_SPY_SPECIFIC
+} MR_Spy_When;
+
+#define	MR_spy_when_string(w)		((w == MR_SPY_ALL) ? "all" :          \
+					(w == MR_SPY_INTERFACE) ? "interface":\
+					(w == MR_SPY_ENTRY) ? "entry" : \
+					(w == MR_SPY_SPECIFIC) ? "specific" : \
+					"weird spy when")
+
+typedef struct struct_mr_spy_point MR_Spy_Point;
+
+struct struct_mr_spy_point {
+	MR_Spy_When			spy_when;
+	bool				spy_enabled;
+	MR_Spy_Action			spy_action;
+	const MR_Stack_Layout_Entry	*spy_proc;
+	const MR_Stack_Layout_Label	*spy_label; /* if MR_SPY_SPECIFIC */
+	MR_Spy_Point			*spy_next;
+};
+
+extern	bool		MR_event_matches_spy_point(const MR_Stack_Layout_Label
+				*layout, MR_Trace_Port port,
+				MR_Spy_Action *action);
+
+extern	MR_Spy_Point	*MR_add_spy_point(MR_Spy_When when,
+				MR_Spy_Action action,
+				const MR_Stack_Layout_Entry *entry,
+				const MR_Stack_Layout_Label *label,
+				const char *path);
+
+#endif	/* not MERCURY_TRACE_SPY_H */
Index: trace/mercury_trace_tables.c
===================================================================
RCS file: mercury_trace_tables.c
diff -N mercury_trace_tables.c
--- /dev/null	Wed May 28 10:49:58 1997
+++ mercury_trace_tables.c	Mon Sep 21 20:03:43 1998
IMPLEMENT NEW DEBUGGER COMMAND SET
@@ -0,0 +1,376 @@
+/*
+** Copyright (C) 1998 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** This file manages a table listing the debuggable modules of the program,
+** and subsidiary tables listing the procedures of each of those modules.
+**
+** Main author: Zoltan Somogyi.
+*/
+
+#undef	USE_GCC_GLOBAL_REGISTERS
+#include "mercury_imp.h"
+#include "mercury_label.h"
+#include "mercury_macros.h"
+#include "mercury_trace_tables.h"
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+static	MR_Module_Info	*MR_module_infos;
+static	int		MR_module_info_next = 0;
+static	int		MR_module_info_max  = 0;
+static	int		MR_module_info_proc_count = 0;
+
+#define	INIT_MODULE_TABLE_SIZE	10
+
+static	void		MR_register_from_internal_label(const void *info);
+static	void		MR_ensure_proc_node_is_present(MR_Module_Info *module,
+				const MR_Stack_Layout_Entry *entry);
+static	MR_Module_Info	*MR_search_module_info(const char *name);
+static	MR_Module_Info	*MR_insert_module_info(const char *name);
+static	MR_Module_Info	*MR_ensure_module_info_is_present(const char *name);
+
+static	void		MR_process_matching_procedures_in_module(
+				MR_Module_Info *module, MR_Proc_Spec *spec,
+				void f(const MR_Stack_Layout_Entry *));
+
+void
+MR_register_all_modules_and_procs(FILE *fp, bool verbose)
+{
+	static	bool	done = FALSE;
+
+	if (! done) {
+		if (verbose) {
+			fprintf(fp, "Registering debuggable procedures... ");
+			fflush(fp);
+		}
+
+		do_init_modules();
+		MR_process_all_internal_labels(MR_register_from_internal_label);
+		done = TRUE;
+		if (verbose) {
+			fprintf(fp, "done.\n");
+			if (MR_module_info_next == 0) {
+				fprintf(fp, "There are no debuggable modules.");
+			} else if (MR_module_info_next == 1) {
+				fprintf(fp, "There is one debuggable module, "
+					"with %d procedures.\n",
+					MR_module_info_proc_count);
+			} else {
+				fprintf(fp, "There are %d debuggable modules, "
+					"with a total of %d procedures.\n",
+					MR_module_info_next,
+					MR_module_info_proc_count);
+			}
+		}
+	}
+}
+
+static void
+MR_register_from_internal_label(const void *info)
+{
+	const MR_Stack_Layout_Label	*label;
+	const MR_Stack_Layout_Entry	*entry;
+	MR_Module_Info			*module;
+
+	label = ((const MR_Internal *) info)->i_layout;
+
+	if (label == NULL) {
+		/* some labels have no layout structure */
+		return;
+	}
+
+	if (label->MR_sll_entry == NULL) {
+		/* some hand-crafted label structures have no entry */
+		return;
+	}
+
+	entry = label->MR_sll_entry;
+
+	if (MR_ENTRY_LAYOUT_HAS_EXEC_TRACE(entry) &&
+			! MR_ENTRY_LAYOUT_COMPILER_GENERATED(entry))
+	{
+		module = MR_ensure_module_info_is_present(
+				entry->MR_sle_def_module);
+		MR_ensure_proc_node_is_present(module, entry);
+	}
+}
+
+static void
+MR_ensure_proc_node_is_present(MR_Module_Info *module,
+	const MR_Stack_Layout_Entry *entry)
+{
+	MR_Proc_Node	*cur;
+
+	for (cur = module->MR_module_procs; cur != NULL;
+			cur = cur->MR_proc_next) {
+		if (entry == cur->MR_proc_layout) {
+			return;
+		}
+	}
+
+	cur = checked_malloc(sizeof(MR_Proc_Node));
+	cur->MR_proc_layout = entry;
+	cur->MR_proc_next   = module->MR_module_procs;
+	module->MR_module_procs = cur;
+	MR_module_info_proc_count++;
+}
+
+static MR_Module_Info *
+MR_search_module_info(const char *name)
+{
+	int	slot;
+	bool	found;
+
+	MR_bsearch(MR_module_info_next, slot, found,
+		strcmp(MR_module_infos[slot].MR_module_name, name));
+	if (found) {
+		return &MR_module_infos[slot];
+	} else {
+		return NULL;
+	}
+}
+
+static MR_Module_Info *
+MR_insert_module_info(const char *name)
+{
+	int	slot;
+
+	MR_ensure_room_for_next(MR_module_info, MR_Module_Info,
+		INIT_MODULE_TABLE_SIZE);
+	MR_prepare_insert_into_sorted(MR_module_infos, MR_module_info_next,
+		slot, strcmp(MR_module_infos[slot].MR_module_name, name));
+
+	MR_module_infos[slot].MR_module_name = name;
+	MR_module_infos[slot].MR_module_procs = NULL;
+	return &MR_module_infos[slot];
+}
+
+static MR_Module_Info *
+MR_ensure_module_info_is_present(const char *name)
+{
+	MR_Module_Info	*module;
+
+	module = MR_search_module_info(name);
+	if (module != NULL) {
+		return module;
+	} else {
+		return MR_insert_module_info(name);
+	}
+}
+
+void
+MR_dump_module_tables(FILE *fp)
+{
+	const MR_Proc_Node	*cur;
+	int			i;
+
+	for (i = 0; i < MR_module_info_next; i++) {
+		fprintf(fp, "====================\n");
+		fprintf(fp, "module %s\n", MR_module_infos[i].MR_module_name);
+		fprintf(fp, "====================\n");
+		for (cur = MR_module_infos[i].MR_module_procs; cur != NULL;
+				cur = cur->MR_proc_next) {
+			MR_print_proc_id(fp, cur->MR_proc_layout, NULL);
+		}
+	}
+}
+
+void
+MR_dump_module_list(FILE *fp)
+{
+	int			i;
+
+	fprintf(fp, "List of debuggable modules\n\n");
+	for (i = 0; i < MR_module_info_next; i++) {
+		fprintf(fp, "%s\n", MR_module_infos[i].MR_module_name);
+	}
+}
+
+void
+MR_dump_module_procs(FILE *fp, const char *name)
+{
+	MR_Module_Info		*module;
+	const MR_Proc_Node	*cur;
+
+	module = MR_search_module_info(name);
+	if (module == NULL) {
+		fprintf(fp, "There is no debugging info about module %s\n",
+				name);
+	} else {
+		fprintf(fp, "List of procedures in module %s\n\n", name);
+		for (cur = module->MR_module_procs; cur != NULL;
+				cur = cur->MR_proc_next) {
+			MR_print_proc_id(fp, cur->MR_proc_layout, NULL);
+		}
+	}
+}
+
+bool
+MR_parse_proc_spec(char *str, MR_Proc_Spec *spec)
+{
+	char	*dash;
+	char	*slash;
+	char	*s;
+	int	n;
+	bool	found;
+
+	spec->MR_proc_module = NULL;
+	spec->MR_proc_name   = NULL;
+	spec->MR_proc_arity  = -1;
+	spec->MR_proc_mode   = -1;
+	spec->MR_proc_pf     = (MR_PredFunc) -1;
+
+	if (strneq(str, "pred*", 5)) {
+		spec->MR_proc_pf = MR_PREDICATE;
+		str += 5;
+	} else if (strneq(str, "func*", 5)) {
+		spec->MR_proc_pf = MR_FUNCTION;
+		str += 5;
+	}
+
+	if ((dash = strrchr(str, '-')) != NULL) {
+		found = FALSE;
+		n = 0;
+		for (s = dash + 1; *s != '\0'; s++) {
+			if (MR_isdigit(*s)) {
+				found = TRUE;
+				n = n * 10 + *s - '0';
+			} else {
+				/* a dash followed by a nondigit is an error */
+				return FALSE;
+			}
+		}
+
+		if (! found) {
+			/* a dash with no following digit is an error */
+			return FALSE;
+		}
+
+		spec->MR_proc_mode = n;
+		*dash = '\0';
+	}
+
+	if ((slash = strrchr(str, '/')) != NULL) {
+		found = FALSE;
+		n = 0;
+		for (s = slash + 1; *s != '\0'; s++) {
+			if (MR_isdigit(*s)) {
+				found = TRUE;
+				n = n * 10 + *s - '0';
+			} else {
+				/* a slash followed by a nondigit is an error */
+				return FALSE;
+			}
+		}
+
+		if (! found) {
+			/* a slash with no following digit is an error */
+			return FALSE;
+		}
+
+		spec->MR_proc_arity = n;
+		*slash = '\0';
+	}
+
+	for (s = str; *s != '\0'; s++) {
+		if (*s == ':' || (*s == '_' && *(s+1) == '_')) {
+			if (*s == ':') {
+				spec->MR_proc_name = s+1;
+			} else {
+				spec->MR_proc_name = s+2;
+			}
+
+			*s = '\0';
+			spec->MR_proc_module = str;
+
+			return TRUE;
+		}
+	}
+
+	spec->MR_proc_name = str;
+	return TRUE;
+}
+
+/* These two variables are for communication between */
+/* MR_register_match and MR_search_for_matching_procedure. */
+static	const MR_Stack_Layout_Entry	*matching_entry;
+static	bool	 			match_unique;
+
+static void
+MR_register_match(const MR_Stack_Layout_Entry *entry)
+{
+	if (matching_entry == NULL) {
+		matching_entry = entry;
+	} else {
+		match_unique = FALSE;
+	}
+}
+
+const MR_Stack_Layout_Entry *
+MR_search_for_matching_procedure(MR_Proc_Spec *spec, bool *unique)
+{
+	matching_entry = NULL;
+	match_unique = TRUE;
+	MR_process_matching_procedures(spec, MR_register_match);
+	*unique = match_unique;
+	return matching_entry;
+}
+
+void
+MR_process_matching_procedures(MR_Proc_Spec *spec,
+	void f(const MR_Stack_Layout_Entry *))
+{
+	if (spec->MR_proc_module != NULL) {
+		MR_Module_Info			*module;
+
+		module = MR_search_module_info(spec->MR_proc_module);
+		if (module != NULL) {
+			MR_process_matching_procedures_in_module(
+				module, spec, f);
+		}
+	} else {
+		int	i;
+
+		for (i = 0; i < MR_module_info_next; i++) {
+			MR_process_matching_procedures_in_module(
+				&MR_module_infos[i], spec, f);
+		}
+	}
+}
+
+#define	match_name(spec, cur)	(((spec)->MR_proc_name == NULL) ||	\
+				streq((spec)->MR_proc_name, cur->MR_sle_name))
+
+#define	match_arity(spec, cur)	(((spec)->MR_proc_arity < 0) ||		\
+				(spec)->MR_proc_arity == cur->MR_sle_arity)
+
+#define	match_mode(spec, cur)	(((spec)->MR_proc_mode < 0) ||		\
+				(spec)->MR_proc_mode == cur->MR_sle_mode)
+
+#define	match_pf(spec, cur)	(((int) (spec)->MR_proc_pf < 0) ||	\
+				(spec)->MR_proc_pf == cur->MR_sle_pred_or_func)
+
+static void
+MR_process_matching_procedures_in_module(MR_Module_Info *module,
+	MR_Proc_Spec *spec, void f(const MR_Stack_Layout_Entry *))
+{
+	MR_Proc_Node			*cur;
+	const MR_Stack_Layout_Entry	*cur_entry;
+
+	for (cur = module->MR_module_procs; cur != NULL;
+			cur = cur->MR_proc_next) {
+		cur_entry = cur->MR_proc_layout;
+		if (match_name(spec, cur_entry) &&
+				match_arity(spec, cur_entry) &&
+				match_mode(spec, cur_entry) &&
+				match_pf(spec, cur_entry))
+		{
+			f(cur_entry);
+		}
+	}
+}
Index: trace/mercury_trace_tables.h
===================================================================
RCS file: mercury_trace_tables.h
diff -N mercury_trace_tables.h
--- /dev/null	Wed May 28 10:49:58 1997
+++ mercury_trace_tables.h	Tue Sep 22 12:30:04 1998
IMPLEMENT NEW DEBUGGER COMMAND SET
@@ -0,0 +1,120 @@
+/*
+** Copyright (C) 1998 The University of Melbourne.
+** This file may only be copied under the terms of the GNU Library General
+** Public License - see the file COPYING.LIB in the Mercury distribution.
+*/
+
+/*
+** This file contains the declarations of the tables that contain
+** the identities of the debuggable modules and their procedures.
+**
+** Main author: Zoltan Somogyi.
+*/
+
+#ifndef	MERCURY_TRACE_TABLES_H
+#define	MERCURY_TRACE_TABLES_H
+
+#include	"mercury_stack_layout.h"
+#include	<stdio.h>
+
+/*
+** The module info table is an array with one element for each module
+** that has procedures with execution tracing information. This element
+** gives the module's name and points to a list of the procedure layouts
+** of the traceable procedures of the module.
+*/
+
+typedef struct struct_mr_proc_node	MR_Proc_Node;
+
+struct struct_mr_proc_node {
+	const MR_Stack_Layout_Entry	*MR_proc_layout;
+	MR_Proc_Node			*MR_proc_next;
+};
+
+typedef struct {
+	const char 			*MR_module_name;
+	MR_Proc_Node			*MR_module_procs;
+} MR_Module_Info;
+
+/*
+** MR_register_all_modules_and_procs gathers all available debugging info
+** about the modules and procedures of the program into the module info table.
+** If verbose is TRUE, print progress and summary messages.
+*/
+
+extern	void		MR_register_all_modules_and_procs(FILE *fp,
+				bool verbose);
+
+/*
+** These functions print (parts of) the module info table.
+**
+** MR_dump_module_tables lists all procedures in all modules.
+** Its output can be very big; it should be used only by developers,
+** for debugging the debugger.
+**
+** MR_dump_module_list lists the names of all the modules,
+** while MR_dump_module_procs lists the names of all the procs in the named
+** module. These are intended for ordinary, non-developer users.
+*/
+
+extern	void		MR_dump_module_tables(FILE *fp);
+extern	void		MR_dump_module_list(FILE *fp);
+extern	void		MR_dump_module_procs(FILE *fp, const char *name);
+
+/*
+** A procedure specification gives some or all of
+**
+**	the name of the module defining the procedure
+**	the name of the predicate or function
+**	the arity of the predicate or function
+**	the mode of the predicate or function
+**	whether the procedure belongs to a predicate or function
+**
+** A NULL pointer for the string fields, and a negative number for the other
+** fields signifies the absence of information about that field, which should
+** therefore be treated as a wildcard.
+*/
+
+typedef	struct {
+	const char			*MR_proc_module;
+	const char			*MR_proc_name;
+	int				MR_proc_arity;
+	int				MR_proc_mode;
+	MR_PredFunc			MR_proc_pf;
+} MR_Proc_Spec;
+
+/*
+** Given a string containing the specification of a procedure in the form
+**
+**	[`pred*'|`func*']module:name/arity-mode
+**
+** in which some of the five components (but not the name) may be missing,
+** parse it into the more usable form of a MR_Proc_Spec. The original string
+** may be overwritten in the process.
+**
+** Returns TRUE if the string was correctly formed, and FALSE otherwise.
+*/
+
+extern	bool	MR_parse_proc_spec(char *str, MR_Proc_Spec *spec);
+
+/*
+** Search the tables for a procedure that matches the given specification.
+** If no procedure matches, return NULL.
+** If one procedure matches, return its layout structure,
+** and set *unique to TRUE.
+** If more than one procedure matches, return the layout structure of one
+** and set *unique to FALSE.
+*/
+
+extern	const MR_Stack_Layout_Entry *MR_search_for_matching_procedure(
+					MR_Proc_Spec *spec, bool *unique);
+
+/*
+** Call f(entry) on the layout of every procedure that matches
+** the given specification.
+*/
+
+extern	void	MR_process_matching_procedures(MR_Proc_Spec *spec,
+			void f(const MR_Stack_Layout_Entry *));
+
+#endif	/* not MERCURY_TRACE_TABLES_H */
cvs diff: Diffing trial
cvs diff: Diffing util
Index: util/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/util/Mmakefile,v
retrieving revision 1.3
diff -u -u -r1.3 Mmakefile
--- Mmakefile	1998/09/29 05:12:05	1.3
+++ Mmakefile	1998/09/29 06:52:28
DOCUMENT NEW DEBUG COMMAND SET
@@ -20,7 +20,7 @@
 # we need -I ../runtime for "mercury_getopt.h"
 # the -O0 is to get around a stupid compiler bug in gcc 2.7.2.3 on cyclone
 
-PROGS=mkinit mdemangle
+PROGS=mkinit mdemangle info_to_mdb
 
 #-----------------------------------------------------------------------------#
 
Index: util/info_to_mdb.c
===================================================================
RCS file: info_to_mdb.c
diff -N info_to_mdb.c
--- /dev/null	Wed May 28 10:49:58 1997
+++ info_to_mdb.c	Wed Sep  9 20:29:47 1998
DOCUMENT NEW DEBUG COMMAND SET
@@ -0,0 +1,222 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#define	TRUE		1
+#define	FALSE		0
+#define	bool		char
+
+#define	MAXLINELEN	160
+
+static	bool	is_empty(char *line);
+static	bool	is_all_same_char(char *line, char what);
+static	bool	is_command(char *line, bool *is_concept);
+static	bool	diff_command(char *line, char *command);
+static	void	print_command_line(char *line, bool is_concept);
+
+static	char	*get_next_line(FILE *infp);
+static	void	put_line_back(char *line);
+
+#define	concept_char(c)	(isupper(c) ? tolower(c) : (isspace(c) ? '_' : (c)))
+#define	isalnumunder(c)	(isalnum(c) || (c) == '_')
+
+int
+main(int argc, char **argv)
+{
+	FILE	*infp;
+	char	*line;
+	char	command[MAXLINELEN];
+	int	slot = 0;
+	int	len;
+	int	i;
+	bool	is_concept;
+	bool	next_concept;
+
+	if (argc != 3) {
+		printf("usage: info_to_mdb section_name section_info_file\n");
+		exit(EXIT_FAILURE);
+	}
+
+	if ((infp = fopen(argv[2], "r")) == NULL) {
+		printf("cannot read %s\n", argv[2]);
+		exit(EXIT_FAILURE);
+	}
+
+	/* skip the top part of the node, up to and including */
+	/* the underlined heading */
+
+	while ((line = get_next_line(infp)) != NULL) {
+		if (is_all_same_char(line, '-') || is_all_same_char(line, '='))
+		{
+			break;
+		}
+	}
+
+	while (TRUE) {
+		line = get_next_line(infp);
+		while (line != NULL && is_empty(line)) {
+			line = get_next_line(infp);
+		}
+
+		if (line == NULL) {
+			return 0;
+		}
+
+		if (! is_command(line, &is_concept)) {
+			continue;
+		}
+
+		slot += 100;
+		printf("document %s %d ", argv[1], slot);
+		if (is_concept) {
+			for (i = 0; line[i] != '\n'; i++) {
+				command[i] = concept_char(line[i]);
+				putchar(concept_char(line[i]));
+			}
+			putchar('\n');
+			command[i] = '\0';
+		} else {
+			for (i = 1; isalnumunder(line[i]); i++) {
+				command[i-1] = line[i];
+				putchar(line[i]);
+			}
+			putchar('\n');
+			command[i-1] = '\0';
+		}
+
+		print_command_line(line, is_concept);
+
+		while ((line = get_next_line(infp)) != NULL) {
+			if (is_command(line, &next_concept)) {
+				if (diff_command(line, command)) {
+					put_line_back(line);
+					break;
+				} else {
+					print_command_line(line, next_concept);
+				}
+			} else {
+				printf("%s", line);
+			}
+		}
+
+		printf("end\n");
+	}
+}
+
+static bool
+is_empty(char *line)
+{
+	int	i = 0;
+
+	while (line[i] != '\0') {
+		if (!isspace(line[i])) {
+			return FALSE;
+		}
+
+		i++;
+	}
+
+	return TRUE;
+}
+
+static bool
+is_all_same_char(char *line, char what)
+{
+	int	i = 0;
+
+	while (line[i] != '\0') {
+		if (line[i] != what && line[i] != '\n') {
+			return FALSE;
+		}
+
+		i++;
+	}
+
+	return i > 1;
+}
+
+static bool
+is_command(char *line, bool *is_concept)
+{
+	int	len;
+
+	len = strlen(line);
+	if ((line[0] == '`') && (line[len-2] == '\'')) {
+		*is_concept = FALSE;
+		return TRUE;
+	} else if (isupper(line[0]) && isupper(line[1])) {
+		*is_concept = TRUE;
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+static bool
+diff_command(char *line, char *command)
+{
+	int	i;
+
+	for (i = 0; command[i] != '\0' && line[i + 1] == command[i]; i++)
+		;
+
+	if (command[i] == '\0' && ! isalnumunder(line[i+1])) {
+		return FALSE;
+	} else {
+		return TRUE;
+	}
+}
+
+static void
+print_command_line(char *line, bool is_concept)
+{
+	int	len;
+	int	i;
+
+	if (is_concept) {
+		for (i = 0; line[i] != '\n'; i++) {
+			putchar(concept_char(line[i]));
+		}
+		putchar('\n');
+	} else {
+		len = strlen(line);
+		for (i = 1; i < len - 2; i++) {
+			putchar(line[i]);
+		}
+		putchar('\n');
+	}
+}
+
+static	char	*putback_line = NULL;
+static	char	line_buf[MAXLINELEN];
+
+static char *
+get_next_line(FILE *infp)
+{
+	char	*tmp;
+
+	if (putback_line != NULL) {
+		tmp = putback_line;
+		putback_line = NULL;
+		return tmp;
+	} else {
+		if (fgets(line_buf, MAXLINELEN, infp) == NULL) {
+			return NULL;
+		} else {
+			/* printf("read %s", line_buf); */
+			return line_buf;
+		}
+	}
+}
+
+static void
+put_line_back(char *line)
+{
+	if (putback_line != NULL) {
+		printf("trying to put back more than one line\n");
+		exit(EXIT_FAILURE);
+	}
+
+	putback_line = line;
+}
Index: util/mkinit.c
===================================================================
RCS file: /home/mercury1/repository/mercury/util/mkinit.c,v
retrieving revision 1.41
diff -u -u -r1.41 mkinit.c
--- mkinit.c	1998/09/29 05:12:06	1.41
+++ mkinit.c	1998/09/29 06:52:29
FIX BUG: MAKE THE DEBUGGER PRINT TO STDOUT, NOT THE CURRENT STREAM
@@ -81,7 +81,7 @@
 static const char mercury_funcs[] =
 	"\n"
 	"Declare_entry(%s);\n"
-	"Declare_entry(mercury__io__print_3_0);\n"
+	"Declare_entry(mercury__io__print_4_0);\n"
 	"\n"
 	"#ifdef CONSERVATIVE_GC\n"
 	"extern char *GC_stackbottom;\n"
@@ -135,7 +135,11 @@
 	"#endif\n"
 	"	MR_library_initializer = ML_io_init_state;\n"
 	"	MR_library_finalizer = ML_io_finalize_state;\n"
-	"	MR_library_trace_browser = ENTRY(mercury__io__print_3_0);\n"
+	"	MR_io_stdin_stream = ML_io_stdin_stream;\n"
+	"	MR_io_stdout_stream = ML_io_stdout_stream;\n"
+	"	MR_io_stderr_stream = ML_io_stderr_stream;\n"
+	"	MR_io_print_to_cur_stream = ML_io_print_to_cur_stream;\n"
+	"	MR_io_print_to_stream = ML_io_print_to_stream;\n"
 	"#ifdef MR_USE_EXTERNAL_DEBUGGER\n"
 	"	MR_type_name = ML_type_name;\n"
 	"	MR_DI_output_current_vars = ML_DI_output_current_vars;\n"



More information about the developers mailing list