/*
 * Copyright (c) 1991-2004 by Wayne C. Gramlich.
 * All rights reserved.
 */

/* Some run-time utilities for the debugger: */

#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 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 void	run__time__pipe_write(Run_time, Str, int);


extern Run_time run__time__global;

/*
 * run__time__argument_read(run_time, error_str)
 *	This routine will read in and return an argument from
 *	"run_time->in_file". If any error occurs, "error_str" is
 *	printed and (Str)0 is returned.
 */
Str
run__time__argument_read(
	Run_time	run_time,
	Str		error_str)
{
	Str		argument;
	File_argument	argument_error;
	
	if (run_time->error) {
		return (Str)0;
	}
	argument = file_argument_read(run_time->in_file,
				      &argument_error, run_time->heap);
	if (argument_error == File_argument_ok) {
		if (run_time->echo) {
			(void)printf(" %s", argument);
		}
		return argument;
	} else {
		run_time->error = 1;
		(void)fprintf(stderr, "%s!\n", error_str);
		return (Str)0;
	}
}

/*
 * run__time__breakpoints_prepare(run_time, routine)
 *	This routine will cause "routine" to stop at every breakpoint.
 */
void
run__time__breakpoints_prepare(
	Run_time	run_time,
	routine___object *routine)
{
	Heap		heap;
	routine__info__type routine_info;
	int		size;

	routine_info = run__time__routine_info_get(run_time, routine);
	size = (vec_size(int, routine_info->line_numbers) + 7) >> 3;
	if (size > run_time->breakpoints_size) {
		heap = run_time->heap;
		/*
		 * Can't free anything without walking up the stack
		 * and fixing everything.
		 * heap_free(heap, (void *)run_time->breakpoints_none);
		 * heap_free(heap, (void *)run_time->breakpoints_stop);
		 */
		run_time->breakpoints_none = (char *)heap_alloc(heap, size);
		run_time->breakpoints_stop = (char *)heap_alloc(heap, size);
		(void)memset(run_time->breakpoints_none, '\0', size);
		(void)memset(run_time->breakpoints_stop, '\377', size);
		run_time->breakpoints_size = size;
	}
}

/*
 * run__time__chr_non_white_peek(run_time)
 *	This routine will return the next non-white character from 
 *	"run_time->in_file".
 */
int
run__time__chr_non_white_peek(
	Run_time	run_time)
{
	return file_chr_non_white_peek(run_time->in_file);
}

/*
 * run__time__env_delete(run_time, env_var_name)
 *	This routine will delete "env_var_name" from the enviroment.
 *	If "env_var_name" does not exist, 1 is returned; otherwise
 *	0 is returned.
 */
int
run__time__env_delete(
	Run_time	run_time,
	Str		env_var_name)
{
	int		bytes;
	extern Str	*environ;
	Str		env_var;
	Str		*env_vars;
	int		env_var_index;
	int		env_vars_size;
	int		size;

	size = strlen(env_var_name);
	env_vars = environ;
	env_var_index = -1;
	env_vars_size = 0;
	while ((env_var = *env_vars++) != (Str)0) {
		if ((strncmp(env_var_name, env_var, size) == 0) &&
		    (env_var[size] = '=')) {
			env_var_index = env_vars_size;
		}
		env_vars_size++;
	}
	if (env_var_index < 0) {
		return 1;
	}
	bytes = sizeof(Str) * env_vars_size;
	env_vars = (Str *)heap_alloc(run_time->heap, bytes);
	(void)memcpy((Str)env_vars, (Str)environ, bytes);
	env_vars[env_var_index] = env_vars[env_vars_size-1];
	env_vars[env_vars_size-1] = (Str)0;
	environ = env_vars;
	return 0;
}

/*
 * run__time__env_int_set(run_time, env_var_name, env_var_value)
 *	This routine will set "env_var_name" to "env_var_value".
 */
void
run__time__env_int_set(
	Run_time	run_time,
	Str		env_var_name,
	int		env_var_value)
{
	char		number[20];

	(void)sprintf(number, "%d", env_var_value);
	run__time__env_str_set(run_time, env_var_name, number);
}

/*
 * run__time__env_state_set(run_time, std_state)
 *	This routine will set the program's state to "std_state".
 */
void
run__time__env_state_set(
	Run_time	run_time,
	Std_state	std_state)
{
	run_time->std_state = std_state;
	run__time__env_int_set(run_time, "STD_STATE", (int)std_state);
}

/*
 * run__time__env_str_get(run_time, env_var_name)
 *	This will return the string value of "env_var_name".
 *	A fatal error occurs is "env_var_name" does not exists.
 */
/* ARGSUSED */
Str
run__time__env_str_get(
	Run_time	run_time,
	Str		env_var_name)
{
	Str		env_var_value;

	env_var_value = getenv(env_var_name);
	if (env_var_value == (Str)0) {
		(void)fprintf(stderr, "Environment variable `%s' not found!\n",
			      env_var_name);
		assert_fail();
	}
	return env_var_value;
}

/*
 * run__time__env_str_set(run_time, env_var_name, env_var_value)
 *	This routine will set "env_var_name" to "env_var_value".
 */
void
run__time__env_str_set(
	Run_time	run_time,
	Str		env_var_name,
	Str		env_var_value)
{
	Str		env_var;
	Strvec		strvec;

	strvec = run_time->strvec;
	strvec_erase(strvec);
	strvec_print(strvec, "%s=%s", env_var_name, env_var_value);
	env_var = strvec_str_get(strvec, run_time->heap);
	assert(putenv(env_var) == 0);
}

/*
 * run__time__eol_read(run_time)
 *	This routine will read and ignore all characters up to and include
 *	the next new-line character.
 */
void
run__time__eol_read(
	Run_time	run_time)
{
	int		chr;
	File		in_file;
	Vec(File)	stack;

	in_file = run_time->in_file;
	for (;;) {
		chr = file_chr_read(in_file);
		switch (chr) {
		    case EOF:	/* Treat end-of-file as end-of-line: */
			run_time->eol_chr = chr;
			run_time->error = 0;
			stack = run_time->in_file_stack;
			for (;;) {
				if (vec_empty(File, stack)) {
					file_close(in_file);
					run_time->in_file =
						file_open("/dev/tty", 1,
							  run_time->heap);
					return;
				}
				/* Lie about EOL chr to force a prompt. */
				run_time->eol_chr = '\n';
				in_file = vec_pop(File, stack);
				run_time->in_file = in_file;
				if (vec_empty(File, stack)) {
					return;
				}
				chr = file_chr_peek(in_file);
				if (chr != EOF) {
					return;
				}
			}
			/* NOTREACHED */
		    case '\n':
			if (run_time->echo) {
				(void)printf("\n");
			}
			run_time->eol_chr = chr;
			run_time->error = 0;
			return;
		    case ';':
			run_time->eol_chr = chr;
			if (!run_time->error) {
				if (run_time->echo) {
					(void)printf(";");
				}
				return;
			}
			break;
		}
	}
}

/*
 * run__time__eol_test(run_time)
 *	This routine will return 1 if we are at the logical end-of-line.
 */
int
run__time__eol_test(
	Run_time	run_time)
{
	int		chr;

	chr = file_chr_non_white_peek(run_time->in_file);
	switch (chr) {
	    case EOF:
	    case ';':
	    case '\n':
		return 1;
	}
	return 0;
}

/*
 * run__time__int_read(run_time, error)
 *	This routine will read an integer from "run_time".  If there is
 *	any error, "error" is printed and -1 is returned.
 */
int
run__time__int_read(
	Run_time	run_time,
	Str		error)
{
	Str		number;

	number = run__time__argument_read(run_time, error);
	if (number == (Str)0) {
		return -1;
	}
	if (isdigit(number[0])) {
		return atoi(number);
	} else {
		(void)fprintf(stderr, "%s!\n", error);
		return -1;
	}
}

/*
 * run__time__line_display(run_time, routine, line_number, is_call_stack)
 *	This routine will display the "line_number'th" line of "routine".
 */
void
run__time__line_display(
	Run_time	run_time,
	routine___object *routine,
	int		line_number,
	int		is_call_stack)
{
	char		file_name[2000];
	Stdio		in_file;
	int		index;
	char		line[2000];
	module___object *module;
	Str		new_file_name;
	routine__info__type routine_info;

	/* Decide whether to use generate file: */
	module = routine->module;
	routine_info = routine->routine_info;
	assert(routine_info != (routine__info__type)0);
	if ((module->generate_boundary < 0) ||
	    (line_number <= module->generate_boundary)) {
		new_file_name = module->file_name;
	} else {
		new_file_name = module->generate_name;
		line_number -= module->generate_boundary;
	}
	if ((run_time->last_file_name != (Str)0) &&
	    !strequal(run_time->last_file_name, new_file_name)) {
		if (run_time->last_in_file != (Stdio)0) {
			(void)fclose(run_time->last_in_file);
		}
		run_time->last_in_file = (Stdio)0;
		run_time->last_module = module;
		run_time->last_file_name = (Str)0;
	}
	if (run_time->last_file_name == (Str)0) {
		run_time->last_file_name = new_file_name;
	}

	if (run_time->pipe_fd >= 0) {
		if (is_call_stack) {
			run__time__pipe_command(run_time,
						Dbxtool_cmd_callstack);
			run__time__pipe_string(run_time, new_file_name);
			run__time__pipe_integer(run_time, line_number);
		} else {
			run__time__pipe_command(run_time, Dbxtool_cmd_stopped);
			run__time__pipe_string(run_time, new_file_name);
			run__time__pipe_string(run_time, routine_info->name);
			run__time__pipe_integer(run_time, line_number);
			run__time__pipe_string(run_time, new_file_name);
			run__time__pipe_string(run_time, routine_info->name);
			run__time__pipe_integer(run_time, line_number);
		}
		return;
	} else {
		in_file = run_time->last_in_file;
		if (in_file == (Stdio)0) {
			in_file = fopen(new_file_name, "r");
			if (in_file == (Stdio)0) {
				error_warn("Could not open %s\n",
					   new_file_name);
				run_time->last_module = (module___object *)0;
				run_time->last_file_name = (Str)0;
				return;
			}
			run_time->last_in_file = in_file;
		}
		(void)fseek(in_file, 0L, 0);
		for (index = 1; index <= line_number; index++) {
			if (fgets(line, sizeof(line), in_file) == (Str)0) {
				error_warn("Could not find line %d in %s\n",
					   line_number, file_name);
				return;
			}
		}
		(void)printf("%d\t", line_number);
		(void)fputs(line, stdout);
	}
}

/*
 * run__time__lower_case(text)
 *	This routine will convert each character in "text" to lower case.
 */
void
run__time__lower_case(
	Str		text)
{
	int		chr;

	while ((chr = *text) != '\0') {
		*text++ = tolower(chr);
	}
}

/*
 * run__time__pipe_command(run_time, command)
 *	This routine will send "command" to dbxtool if the dbxtool pipe is
 *	open; otherwise, it will do nothing.
 */
void
run__time__pipe_command(
	Run_time	run_time,
	Dbxtool_cmd	command)
{
	if (run_time->pipe_fd >= 0) {
		run__time__pipe_write(run_time,
				      (Str)&command, sizeof(command));
	}
}

/*
 * run__time__pipe_initialize(run_time, pipe_fd)
 *	This routine will initialize the pipe connection to stdtool
 *	(alias dbxtool) using the pipe on file descriptor "pipe_fd".
 */
void
run__time__pipe_initialize(
	Run_time	run_time,
	int		pipe_fd)
{
	run_time->pipe_fd = pipe_fd;
	run__time__pipe_command(run_time, Dbxtool_cmd_version);
	run__time__pipe_integer(run_time, 3);
	run__time__pipe_command(run_time, Dbxtool_cmd_newinitdone);
	run__time__pipe_string(run_time, "");
	run__time__pipe_string(run_time, "");
	run__time__pipe_integer(run_time, 0);
	run__time__pipe_string(run_time, "");
	run__time__pipe_string(run_time, "");
	run__time__pipe_integer(run_time, 0);
}

/*
 * run__time__pipe_string(run_time, text)
 * 	This routine will send "text" to dbxtool via the dbxtool pipe if it
 *	is open; otherwise it will do nothing.
 */
void
run__time__pipe_string(
	Run_time	run_time,
	Str		text)
{
	int		length;

	if (run_time->pipe_fd >= 0) {
		run__time__pipe_command(run_time, Dbxtool_cmd_string);
		length = strlen(text) + 1;
		run__time__pipe_write(run_time, (Str)&length, sizeof(length));
		run__time__pipe_write(run_time, text, length);
	}
}

/*
 * run__time__pipe_integer(run_time, integer)
 *	This routine will send "integer" to dbxtool via the dbxtool pipe
 *	(if it is open); otherwise, it will do nothing.
 */
void
run__time__pipe_integer(
	Run_time	run_time,
	int		integer)
{
	if (run_time->pipe_fd >= 0) {
		run__time__pipe_command(run_time, Dbxtool_cmd_int);
		run__time__pipe_write(run_time,
				      (Str)&integer, sizeof(integer));
	}
}

/*
 * run__time__pipe_write(run_time, buffer, size)
 *	This routine will write "size" bytes of "buffer" to the dbxtool pipe.
 */
void
run__time__pipe_write(
	Run_time	run_time,
	Str		buffer,
	int		size)
{
	if (write(run_time->pipe_fd, buffer, size) < 0) {
		error_fatal("Pipe write failed!\n");
	}
}

/*
 * run__time__routine_info_get(run_time, routine)
 *	This routine will get the routine information object for "routine"
 *	using "run_time".
 */
routine__info__type
run__time__routine_info_get(
	Run_time	run_time,
	routine___object *routine)
{
	char		*breakpoints;
	Heap		heap;
	routine__info__type routine_info;
	int		size;

	routine_info = routine->routine_info;
	if (routine_info == (routine__info__type)0) {
		heap = run_time->heap;
		routine_info = routine__info__parse(routine->info, heap);
		routine->routine_info = routine_info;
		size = vec_size(int, routine_info->line_numbers);
		size = (size + 7) >> 3;
		breakpoints = (char *)heap_alloc(heap, size);
		(void)memset(breakpoints, 0, size);
		routine_info->breakpoints = breakpoints;
	}
	return routine_info;
}

/*
 * run__time__signals_disable()
 *	This routine will disable signals.
 */
void
run__time__signals_disable(void)
{
#ifdef OLD_OLD
	sigset_t	signal_set;
	
	(void)sigemptyset(&signal_set);
	(void)sigaddset(&signal_set, SIGBUS);
	(void)sigaddset(&signal_set, SIGINT);
	(void)sigaddset(&signal_set, SIGSEGV);
	assert(sigprocmask(SIG_BLOCK, &signal_set, (sigset_t *)0) == 0);
	foo();
#endif /* OLD */
}

/*
 * run__time__signals_enable()
 *	This routine will enable signals.
 */
void
run__time__signals_enable(void)
{
#ifdef OLD_OLD
	sigset_t	signal_set;

	(void)sigemptyset(&signal_set);
	(void)sigaddset(&signal_set, SIGBUS);
	(void)sigaddset(&signal_set, SIGINT);
	(void)sigaddset(&signal_set, SIGSEGV);
	assert(sigprocmask(SIG_UNBLOCK, &signal_set, (sigset_t *)0) == 0);
#endif /* OLD */
}

/*
 * run__time__string_read(run_time, error_str)
 *	This routine will read in and return an string from "in_file".
 *	If any error occurs, "error_str" is printed and (Str)0 is returned.
 */
Str
run__time__string_read(
	Run_time	run_time,
	Str		error_str)
{
	Str		string;
	File_string	string_error;
	
	if (run_time->error) {
		return (Str)0;
	}
	string = file_string_read(run_time->in_file,
				  &string_error, run_time->heap);
	switch (string_error) {
	    case File_string_single_quoted:
	    case File_string_double_quoted:
		if (run_time->echo) {
			char		chr;
			char		*pointer;

			pointer = string;
			(void)printf(" \"");
			while (chr = *pointer++) {	
				if ((isprint(chr) && (chr != '"')) ||
				    (chr == ' ')) {
					(void)printf("%c", chr);
				} else {
					(void)printf("\\%3o", chr);
				}
			}
			(void)printf("\"");
		}
		return string;
	    default:
		run_time->error = 1;
		(void)fprintf(stderr, "%s!\n", error_str);
		return (Str)0;
	}
}

