/* xyz */

/*
 * Copyright (c) 1991, 1992, 1993, 1994, 1995 by Wayne C. Gramlich.
 * All rights reserved.
 *
 * Permission to use, copy, modify, distribute, and sell this software
 * for any purpose is hereby granted without fee provided that the above
 * copyright notice and this permission are retained.  The author makes
 * no representations about the suitability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 */

/* This file contains the code to implement each debugger command: */

#ifndef ERROR_EXPORTS_H
#include "error_exports.h"
#endif

#ifndef FILE_DEFS_H
#include "file_defs.h"
#endif

#ifndef HEAP_EXPORTS_H
#include "heap_exports.h"
#endif

#ifndef LIBC_EXPORTS_H
#include "libc_exports.h"
#endif

#ifndef LINT_H
#include "lint.h"
#endif

#ifndef ROUTINE_INFO_DEFS_H
#include "routine_info_defs.h"
#endif

#ifndef RUN_TIME_DEFS_H
#include "run_time_defs.h"
#endif

#ifndef STHEADERS_H
#include "stheaders.h"
#endif

#ifndef STR_EXPORTS_H
#include "str_exports.h"
#endif

#ifndef TABLE_EXPORTS_H
#include "table_exports.h"
#endif

#ifndef UNIX_ASSERT_H
#include "unix_assert.h"
#endif

#ifndef UNIX_CTYPE_H
#include "unix_ctype.h"
#endif

/* #ifndef UNIX_DLFCN_H */
/* #include "unix_dlfcn.h" */
/* #endif */

#ifndef UNIX_FCNTL_H
#include "unix_fcntl.h"
#endif

#ifndef UNIX_MEMORY_H
#include "unix_memory.h"
#endif

#ifndef UNIX_SIGNAL_H
#include "unix_signal.h"
#endif

#ifndef UNIX_SYS_STAT_H
#include "unix_sys_stat.h"
#endif

#ifndef UNIX_STDLIB_H
#include "unix_stdlib.h"
#endif

#ifndef UNIX_UNISTD_H
#include "unix_unistd.h"
#endif

#ifndef VECTOR_DEFS_H
#include "vector_defs.h"
#endif

LOCAL int	object_object_equal(object___object *, object___object *);
LOCAL int	object_object_hash(object___object*);
LOCAL int	run__time__event_set(Run_time, Event_kind,
				     Str, Str, Str, int, int);
Run_time	run__time__global;
LOCAL int	routine_object_equal(routine___object *, routine___object *);
LOCAL int	routine_object_hash(routine___object*);
LOCAL int	type_reference_equal(type___reference *, type___reference *);
LOCAL void	type_reference_print(type___reference *, Strvec);
LOCAL void	handler_user_interrupt(int);
LOCAL void	handler_hardware_fault(int);

/* ARGSUSED */
LOCAL void
handler_user_interrupt(
	int		zilch)
{
	activation___object *activation;
	routine___object *routine;
	Run_time	run_time;

	run_time = run__time__global;
	run_time->run_mode = Run_mode_stop;
	activation = run_time->leaf_activation;
	routine = activation->routine;
	run__time__breakpoints_prepare(run_time, routine);
	activation->breakpoints = run_time->breakpoints_stop;
	(void)printf("\n");
}

/* ARGSUSED */
LOCAL void
handler_hardware_fault(
	int		zilch)
{
	Run_time	run_time;

	(void)printf("Fatal hardware fault encountered!\n");
	(void)fflush(stdout);
	run_time = run__time__global;
	run_time->std_state = Std_state_failed;
	run_time->current_activation = run_time->leaf_activation;
	vec_append(activation___object *,
		   run_time->faults, run_time->leaf_activation);
	if (getenv("STD_NAME") != (Str)0) {
		run__time__cmd_loop();
	}
	exit(1);
}

/*
 * routine_object_equal(routine1, routine2)
 *	This routine will return 1 if "routine1" is equal to "routine2".
 */
LOCAL int
routine_object_equal(
	routine___object *routine1,
	routine___object *routine2)
{
	char		chr1;
	char		chr2;
	Str		info1;
	Str		info2;

	info1 = routine1->info;
	info2 = routine2->info;
	do {
		chr1 = *info1++;
		chr2 = *info2++;
		assert(chr1 != '\0');
		assert(chr2 != '\0');
	} while ((chr1 == chr2) && (chr1 != '('));
	return (chr1 == '(');
}

/*
 * routine_object_hash(routine)
 *	This routine will return a hash value for "routine".
 */
LOCAL int
routine_object_hash(
	routine___object* routine)
{
	char		chr;
	Str		info;
	int		hash;

	hash = 0;
	info = routine->info;
	do {
		chr = *info++;
		assert(chr != '\0');
		hash += chr;
	} while (chr != '(');
	return hash;
}

/*
 * type_reference_equal(type1, type2)
 *	This routine will return 1 if "type1" equalas "type2" and 0 otherwise.
 */
LOCAL int
type_reference_equal(
	type___reference *type1,
	type___reference *type2)
{
	int		index;
	type___reference **parameters1;
	type___reference **parameters2;
	int		size;

	if (!strequal(type1->name, type2->name)) {
		return 0;
	}
	size = type1->count;
	if (type2->count != size) {
		return 0;
	}
	parameters1 = type1->parameters;
	parameters2 = type2->parameters;
	for (index = 0; index < size; index++) {
		type1 = parameters1[index];
		type2 = parameters2[index];
		if (!type_reference_equal(type1, type2)) {
			return 0;
		}
	}
	return 1;
}

/*
 * type_reference_print(type, strvec)
 *	This routine will append an ascii representation of "type" onto the
 *	end of "strvec".
 */
LOCAL void
type_reference_print(
	type___reference *type,
	Strvec		strvec)
{
	int		index;
	type___reference **parameters;
	int		size;
	Str		seperator;

	strvec_str_append(strvec, type->name);
	size = type->count;
	if (size > 0) {
		seperator = "[";
		parameters = type->parameters;
		for (index = 0; index < size; index++) {
			strvec_str_append(strvec, seperator);
			type_reference_print(parameters[index], strvec);
			seperator = ", ";
		}
		strvec_str_append(strvec, "]");
	}
}

/*
 * run__time__alias_set(run_time, name, command)
 *	This routine will add alias "name" with value "command" to the
 *	alias table.  If "name" already exists, its value will be replaced
 *	by "command".
 */
void
run__time__alias_set(
	Run_time	run_time,
	Str		name,
	Str		command)
{
	Alias		alias;
	Vec(Alias)	aliases;
	int		index;
	int		size;

	aliases = run_time->aliases;
	size = vec_size(Alias, aliases);
	for (index = 0; index < size; index++) {
		alias = vec_fetch(Alias, aliases, index);
		if (strequal(alias->name, name)) {
			alias->command = command;
			return;
		}
	}	
	alias = heap_allocate(run_time->heap, Alias);
	alias->name = name;
	alias->command = command;
	vec_append(Alias, aliases, alias);
}

/*
 * run__time__breakpoint_at(run_time, set)
 *	This routine will parse a breakpoint location from the command
 *	line and set or clear depending upon whether "set" is 1 or 0,
 *	respectively.
 */
void
run__time__breakpoint_at(
	Run_time	run_time,
	int		set)
{
	activation___object *activation;
	int		chr;
	Str		file_name;
	int		line_number;

	/* Parse the breakpoint. */
	chr = run__time__chr_non_white_peek(run_time);
	if (chr == '"') {
		/* A file name is comming: */
		file_name = run__time__string_read(run_time,
				"No file name specified");
		chr = run__time__chr_non_white_peek(run_time);
		if (chr == ':') {
		    (void)file_chr_read(run_time->in_file);
		}
	} else {
		file_name = (Str)0;
	}
	line_number = run__time__int_read(run_time,
			"No line number specified");
	run__time__eol_read(run_time);
	if (line_number < 0) {
		/* Error message already generated. */
		return;
	}

	if (file_name == (Str)0) {
		activation = run_time->current_activation;
		if (activation == (activation___object *)0) {
			(void)fprintf(stderr, "No file is active!\n");
			return;
		} else {
			file_name = activation->routine->module->file_name;
		}
	}
	(void)run__time__event_set(run_time, Event_kind_stop_at, file_name,
				   (Str)0, (Str)0, line_number, set);
}

/*
 * run__time__breakpoint_in(run_time, set)
 *	This routine will parse a routine name from the command
 *	line and set or clear depending upon whether "set" is 1 or 0,
 *	respectively.
 */
void
run__time__breakpoint_in(
	Run_time	run_time,
	int		set)
{
	Str		full_routine;
	Str		full_pointer;
	int		index;
	int		line_number;
	Vec(int)	line_numbers;
	module___object	*module;
	Vec(module___object *) modules;
	routine__info__type routine_info;
	Str		routine_name;
	routine___object *routine;
	routine___object **routines;
	int		size;
	Str		type_name;

	/* Parse the routine name: */
	full_routine = run__time__argument_read(run_time,
				"No routine name specified");
	run__time__eol_read(run_time);
	if (full_routine == (Str)0) {
		/* Error message already generated. */
		return;
	}
	full_pointer = strchr(full_routine, '@');
	if (full_pointer == (Str)0) {
		routine_name = full_routine;
		type_name = "global__";
	} else {
		*full_pointer = '\0';
		routine_name = strdupl(full_routine, run_time->heap);
		type_name = strdupl(full_pointer + 1, run_time->heap);
		*full_pointer = '@';
	}

	/* Search for matching routine: */
	modules = run_time->modules;
	VEC_LOOP(module___object *, modules, module) {
		routines = module->routines;
		size = module->routine_count;
		for (index = 0; index < size; index++) {
			routine = routines[index];
			routine_info = run__time__routine_info_get(run_time,
								   routine);
			if (strequal(routine_name, routine_info->name) &&
			    strequal(type_name, routine_info->type_name)) {
				line_numbers = routine_info->line_numbers;
				line_number = vec_fetch(int, line_numbers, 0);
				(void)run__time__event_set(run_time,
							   Event_kind_stop_in,
							   module->file_name,
							   type_name,
							   routine_name,
							   line_number,
							   set);
				return;
			}
		}
	}
	(void)fprintf(stderr, "Could not find routine `%s'!\n", routine_name);
}

/*
 * run__time__button(run_time, button_name, selection)
 *	This routine will remove a button from the user interface.
 */
void
run__time__button(
	Run_time	run_time,
	Str		button_name,
	Dbx_button 	selection)
{
	run__time__pipe_command(run_time, Dbxtool_cmd_button);
	run__time__pipe_integer(run_time, (int)selection);
	run__time__pipe_string(run_time, button_name);
}

/*
 * run__time__event_set(run_time, event_kind, file_name, type_name,
 *			routine_name, line_number, set)
 *	This routine will set or clear a breakpoint in "file_name" at
 *	"line_number" (if it is >= 0), or at "routine_name"@"type_name",
 *	otherwise.  If "set" is 1, the breakpoint is set; otherwise,
 *	it is cleared.  1 is returned if any error occurs.
 */
int
run__time__event_set(
	Run_time	run_time,
	Event_kind	event_kind,
	Str		file_name,
	Str		type_name,
	Str		routine_name,
	int		line_number,
	int		set)
{
	int		breakpoint;
	int		count;
	Event		event;
	Vec(Event)	events;
	int		index;
	Vec(int)	line_numbers;
	int		mask;
	routine___object *routine;
	routine___object **routines;
	routine__info__type routine_info;
	int		size;
	int		temp_line_number;

	module___object	*module;
	Vec(module___object *) modules;

	modules = run_time->modules;
	VEC_LOOP(module___object *, modules, module) {
		if (strequal(module->file_name, file_name)) {
			goto found_it;
		}
	}
	(void)fprintf(stderr, "Could not find file \"%s\"!\n", file_name);
	return 1;
    found_it:
	size = module->routine_count;
	routines = module->routines;
	for (index = 0; index < size; index++) {
		routine = routines[index];
		routine_info = run__time__routine_info_get(run_time, routine);
		line_numbers = routine_info->line_numbers;
		switch (event_kind) {
		    case Event_kind_stop_at:
			/* Stop at */
			count = vec_size(int, line_numbers);
			for (breakpoint = 0; breakpoint < count; breakpoint++) {
				temp_line_number = vec_fetch(int, line_numbers,
							     breakpoint);
				if (temp_line_number == line_number) {
					goto got_it;
				}
			}
			break;
		    case Event_kind_stop_in:
			/* Stop in */
			if (strequal(routine_name, routine_info->name) &&
			    strequal(type_name, routine_info->type_name)) {
				breakpoint = 0;
				line_number = vec_fetch(int, line_numbers, 0);
				goto got_it;
			}
			break;
		    default:
			assert_fail();
		}
	}
	switch (event_kind) {
	    case Event_kind_stop_at:
		(void)fprintf(stderr,
			      "Could not find line number %d\n", line_number);
		break;
	    case Event_kind_stop_in:
		if (strequal(type_name, "global__")) {
			(void)fprintf(stderr, "Could not find routine %s!\n",
				      routine_name);
		} else {
			(void)fprintf(stderr, "Could not find routine %s@%s!\n",
				      routine_name, type_name);
		}
		break;
	    default:
		assert_fail();
	}
	return 1;
    got_it:
	mask = 1 << (breakpoint & 7);
	if (set) {
		routine_info->breakpoints[breakpoint >> 3] |= mask;
		run__time__pipe_command(run_time, Dbxtool_cmd_brkset);
		run__time__pipe_string(run_time, file_name);
		run__time__pipe_integer(run_time, line_number);
	} else {
		routine_info->breakpoints[breakpoint >> 3] &= ~mask;
		run__time__pipe_command(run_time, Dbxtool_cmd_brkdel);
		run__time__pipe_string(run_time, file_name);
		run__time__pipe_integer(run_time, line_number);
	}

	/* Search the event table for a match: */
	events = run_time->events;
	size = vec_size(Event, events);
	for (index = 0; index < size; index++) {
		event = vec_fetch(Event, events, index);
		if (event->file_name == (Str)0) {
			continue;
		}
		if (strequal(event->file_name, file_name) &&
		    strequal(event->routine_name, routine_info->name) &&
		    strequal(event->type_name, routine_info->type_name) &&
		    (event->line_number == line_number)) {
			if (set) {
				return 0;
			} else {
				event->event_kind = Event_kind_deleted;
				vec_delete(Event, events, index);
				size--;
			}
		}
	}
	if (set) {
		if (routine_name == (Str)0) {
			routine_name = routine_info->name;
		}
		if (type_name == (Str)0) {
			type_name = routine_info->type_name;
		}
		event = heap_allocate(run_time->heap, Event);
		event->event_kind = event_kind;
		event->file_name = file_name;
		event->routine_name = routine_name;
		event->type_name = type_name;
		event->line_number = line_number;
		vec_append(Event, events, event);
	}
	return 0;
}

/*
 * run__time__exec(run_time)
 *	This routine will exec the program specified by the STD_NAME
 *	environment variable.
 */
void
run__time__exec(
	Run_time	run_time)
{
	Alias		alias;
	Vec(alias)	aliases;
	Event		event;
	Vec(Event)	events;
	Str		file_name;
	File		in_file;
	Vec(File)	in_file_stack;
	int		index;
	char		name[50];
	int		position;
	Str		program;
	int		size;
	char		value[2000];

	/* See if there are any characters left over on the line: */
	if (run_time->eol_chr == ';') {
		/* EMPTY */
		/* There are some commands left on the command line: */
	}

	/* Remember the alias table: */
	aliases = run_time->aliases;
	size = vec_size(Alias, aliases);
	run__time__env_int_set(run_time, "STD_ALIASC", size);
	for (index = 0; index < size; index++) {
		alias = vec_fetch(Alias, aliases, index);
		(void)sprintf(name, "STD_ALIAS%d", index);
		(void)sprintf(value, "%s %s", alias->name, alias->command);
		run__time__env_str_set(run_time, name, value);
	}

	/* Remember the event table (after squishing out deleted ones): */
	events = run_time->events;
	size = vec_size(Even, events);	
	for (index = 0; index < size; index++) {
		event = vec_fetch(Event, events, index);
		if (event->file_name == (Str)0) {
			vec_delete(Event, events, index);
			size--;
		}
	}
	run__time__env_int_set(run_time, "STD_EVENTC", size);
	for (index = 0; index < size; index++) {
		event = vec_fetch(Event, events, index);
		(void)sprintf(name, "STD_EVENT%d", index);
		(void)sprintf(value, "%d %s@%s %d %s",
			      event->event_kind, event->routine_name,
			      event->type_name, event->line_number,
			      event->file_name);
		run__time__env_str_set(run_time, name, value);
	}

	/* Remember the positions of each file: */
	in_file_stack = run_time->in_file_stack;
	vec_append(File, in_file_stack, run_time->in_file);
	size = vec_size(File, in_file_stack);
	for (index = 0; index < size; index++) {
		in_file = vec_fetch(File, in_file_stack, index);
		position = file_tell(in_file);
		file_name = file_name_get(in_file);
		(void)sprintf(name, "STD_FILE%d", index);
		(void)sprintf(value, "%d %s", position, file_name);
		run__time__env_str_set(run_time, name, value);
		if (!strequal(in_file->file_name, "/dev/tty")) {
			(void)file_close(in_file);
		}
	}
	run__time__env_int_set(run_time, "STD_FILEC", size);
	(void)vec_pop(File, in_file_stack);
	run__time__env_int_set(run_time, "STD_ECHO", run_time->echo);

	/* Now exec the program: */
	program = run__time__env_str_get(run_time, "STD_NAME");
	(void)execl(program, program, (Str)0);
	(void)fprintf(stderr, "Could not exec `%s'!\n", program);
}

/*
 * run__time__debug_initialize(std_state, modules)
 *	This routine will initialize the debugger data structures.
 */
Run_time
run__time__debug_initialize(
	Std_state	std_state,
	Vec(module___object *) modules)
{
	Str		cmd;
	Debug_cmd	debug_cmd;
	Str		*debug_cmds;
	Str		*debug_help;
	char		directory[2000];
	Heap		heap;
	Str		help;
	Run_time	run_time;

	heap = heap_standard_create();
	assert(getcwd(directory, sizeof directory) != (Str)0);
	debug_cmds = (Str *)heap_alloc(heap, (int)Debug_cmd_last * sizeof(Str));
	debug_help = (Str *)heap_alloc(heap, (int)Debug_cmd_last * sizeof(Str));
	run_time = heap_allocate(heap, Run_time);
	run__time__global = run_time;
	run_time->aliases = vec_create(Alias, heap);
	run_time->breakpoints_size = 1;
	run_time->breakpoints_none = (char *)heap_alloc(heap, 1);
	run_time->breakpoints_none[0] = '\0';
	run_time->breakpoints_stop = (char *)heap_alloc(heap, 1);
	run_time->breakpoints_stop[0] = '\377';
	run_time->coverage_file = (Str)0;
	run_time->current_activation = (activation___object *)0;
	run_time->debug_cmds = debug_cmds;
	run_time->debug_help = debug_help;
	run_time->depth = 0;
	run_time->directory = strdupl(directory, heap);
	run_time->echo = 0;
	run_time->eol_chr = '\n';
	run_time->error = 0;
	run_time->events = vec_create(Event, heap);
	run_time->faults = vec_create(activation___object *, heap);
	run_time->heap = heap;
	run_time->in_file = file_standard_input_open(heap);
	run_time->in_file_stack = vec_create(File, heap);
	run_time->in_string = (File)0;	/* Fix this! */
	run_time->last_file_name = (Str)0;
	run_time->last_in_file = (Stdio)0;
	run_time->last_module = (module___object *)0;
	run_time->leaf_activation = (activation___object *)0;
	run_time->modules = modules;
	run_time->object_table = table_create(object___object *,
			object___object *, 100,
			object_object_equal, object_object_hash,
			(object___object *)0, heap);
	run_time->over_activation = (activation___object *)0;
	run_time->out_activation = (activation___object *)0;
	run_time->pipe_fd = -1;
	run_time->routine_table = table_create(routine___object *,
			routine___object *, 100,
			routine_object_equal, routine_object_hash,
			(routine___object *)0, heap);
	run_time->run_mode = (std_state == Std_state_no_debug) ?
					Run_mode_continue : Run_mode_step_in;
	run_time->strvec = strvec_create(heap);
	run__time__env_state_set(run_time, std_state);

	/* Install some signal handlers: */
	run__time__signals_disable();
	assert((int)signal(SIGINT, handler_user_interrupt) != -1);
#ifndef WINDOWS
	assert((int)signal(SIGBUS, handler_hardware_fault) != -1);
#endif /* WINDOWS */
	assert((int)signal(SIGSEGV, handler_hardware_fault) != -1);

	for (debug_cmd = (Debug_cmd)0;
	     debug_cmd < Debug_cmd_last; debug_cmd++) {
		switch (debug_cmd) {
		    case Debug_cmd_alias:
			cmd = "alias";
			help = "define a command alias";
			break;
		    case Debug_cmd_button:
			cmd = "button";
			help = "add a button to the user interface";
			break;
		    case Debug_cmd_cd:
			cmd = "cd";
			help = "change directory";
			break;
		    case Debug_cmd_continue:
			cmd = "continue";
			help = "continue execution until next breakpoint";
			break;
		    case Debug_cmd_debug:
			cmd = "debug";
			help = "debug a program";
			break;
		    case Debug_cmd_delete:
			cmd = "delete";
			help = "delete a breakpoint";
			break;
		    case Debug_cmd_echo:
			cmd = "echo";
			help = "echo arguments";
			break;
		    case Debug_cmd_help:
			cmd = "help";
			help = "describe each command";
			break;
		    case Debug_cmd_in:
			cmd = "in";
			help = "go into the stack by one frame";
			break;
		    case Debug_cmd_module:
			cmd = "module";
			help = "select module or list all modules";
			break;
		    case Debug_cmd_out:
			cmd = "out";
			help = "go up one frame in stack";
			break;
		    case Debug_cmd_print:
			cmd = "print";
			help = "print value of a variable";
			break;
		    case Debug_cmd_printenv:
			cmd = "printenv";
			help = "print environment variables";
			break;
		    case Debug_cmd_pwd:
			cmd = "pwd";
			help = "print working directory";
			break;
		    case Debug_cmd_quit:
			cmd = "quit";
			help = "quit out of std";
			break;
		    case Debug_cmd_run:
			cmd = "run";
			help = "run program with supplied arguments";
			break;
		    case Debug_cmd_rerun:
			cmd = "rerun";
			help = "run program with no arguments";
			break;
		    case Debug_cmd_setenv:
			cmd = "setenv";
			help = "set environment variable";
			break;
		    case Debug_cmd_show:
			cmd = "show";
			help = "show something";
			break;
		    case Debug_cmd_stack:
			cmd = "stack";
			help = "print stack backtrace";
			break;
		    case Debug_cmd_status:
			cmd = "status";
			help = "show list of breakpoints";
			break;
		    case Debug_cmd_step:
			cmd = "step";
			help = "single step command"; 
			break;
		    case Debug_cmd_step_in:
			cmd = "step_in";
			help = "single step into next statement"; 
			break;
		    case Debug_cmd_step_out:
			cmd = "step_out";
			help = "single step until routine returns";
			break;
		    case Debug_cmd_step_over:
			cmd = "step_over";
			help = "single step over next statement";
			break;
		    case Debug_cmd_step_to:
			cmd = "step_to";
			help = "single step to the selected statement";
			break;
		    case Debug_cmd_stop:
			cmd = "stop";
			help = "set a breakpoint";
			break;
		    case Debug_cmd_stop_at:
			cmd = "stop_at";
			help = "set a breakpoint at a given line number";
			break;
		    case Debug_cmd_stop_in:
			cmd = "stop_in";
			help = "set a breakpoint at a given routine";
			break;
		    case Debug_cmd_source:
			cmd = "source";
			help = "execute commands from a file";
			break;
		    case Debug_cmd_unalias:
			cmd = "unalias";
			help = "remove a command alias";
			break;
		    case Debug_cmd_unbutton:
			cmd = "unbutton";
			help = "remove a user interface button";
			break;
		    case Debug_cmd_undebug:
			cmd = "undebug";
			help = "stop debugging the current program";
			break;
		    case Debug_cmd_unsetenv:
			cmd = "unsetenv";
			help = "delete an environment variable";
			break;
		    case Debug_cmd_unstop:
			cmd = "unstop";
			help = "remove a breakpoint";
			break;
		    case Debug_cmd_unstop_at:
			cmd = "unstop_at";
			help = "remove a breakpoint at a statement";
			break;
		    case Debug_cmd_unstop_in:
			cmd = "unstop_in";
			help = "remove a breakpoint at a routine";
			break;
		    default:
			cmd = "";
			help = "";
			assert_fail();
		}
		debug_cmds[(int)debug_cmd] = cmd;
		debug_help[(int)debug_cmd] = help;
	}
	return run_time;
}

/*
 * run__time__unbutton(run_time, button_name)
 *	This routine will remove a button from the user interface.
 */
void
run__time__unbutton(
	Run_time	run_time,
	Str		button_name)
{
	run__time__pipe_command(run_time, Dbxtool_cmd_unbutton);
	run__time__pipe_string(run_time, button_name);
}

/*
 * object_object_equal(object1, object2)
 *	This routine will return 1 if "object1" is equal to "object2".
 */
LOCAL int
object_object_equal(
	object___object *object1,
	object___object *object2)
{
	if ((strequal(object1->type_name, object2->type_name)) &&
	    (strequal(object1->object_name, object2->object_name))) {
		return 1;
	} else {
		return 0;
	}
}

/*
 * object_object_hash(object)
 *	This routine will return a hash value for "object".
 */
LOCAL int
object_object_hash(
	object___object* object)
{
	return strhash(object->type_name) + strhash(object->object_name);
}

