/*
 * Copyright (C) 2000-2002 ASANO Masahiro
 */

#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <readline/readline.h>
#include <readline/history.h>

#include "crash.h"

#ifdef CONF_OLD_READLINE
/* for old version readline */
#define rl_filename_completion_function filename_completion_function
#define rl_completion_matches completion_matches
#endif

addr_t xaddr[MAXARGS];
int xaddr_ptr;

extern sigset_t block_mask, unblock_mask;
char *prompt = PS1;
char *(*input_function)();

char *
command_generator(text, state)
	const char *text;
	int state;
{
	static int list_index, len;
	const commandtable_t *cmd;

	if (state == 0) {
		list_index = 0;
		len = strlen(text);
	}
	while ((cmd = commandtable[++list_index]) != NULL) {
		if (strncmp(cmd->name, text, len) == 0)
			return strdup(cmd->name);
	}
	return NULL;
}

char *
argument_generator(text, state)
	const char *text;
	int state;
{
	static int list_index, len;
	const char *name;

	if (state == 0) {
		list_index = 0;
		len = strlen(text);
	}
	while (list_index < ARGHIST) {
		name = arghist_buf[list_index++];
		if (name && strncmp(name, text, len) == 0)
			return strdup(name);
	}
	return NULL;
}

char *
variable_generator(text, state)
	const char *text;
	int state;
{
	static int list_index, len;
	int len2;
	const char *name;
	char *p;

	if (state == 0) {
		list_index = 0;
		len = strlen(text) - 1;
		if (len == 0) {
			return strdup("$?");
			/* $0-$9, $-1-$-9, $#, $*, $$ */
		}
	}

	while (list_index < variabletable_size) {
		name = variabletable[list_index].name;
		len2 = variabletable[list_index].name_sz;
		list_index++;
		if (len > len2 || strncmp(name, text + 1, len) != 0)
			continue;

		if ((p = malloc(len2 + 2)) == NULL)
			return NULL;
		*p = '$';
		strcpy(p + 1, name);
		return p;
	}
	return NULL;
}

char **
fileman_completion(text, start, end)
	const char *text;
	int start, end;
{
	int i;

	if (start == 0) {
		return rl_completion_matches(text, command_generator);
	}
	if (end > start && *text == '$') {
		return rl_completion_matches(text, variable_generator);
	}
	for (i = 0; i < end; i++) {
		if (rl_line_buffer[i] == '|' ||
		    rl_line_buffer[i] == '!' ||
		    rl_line_buffer[i] == '>')
			return rl_completion_matches(text, rl_filename_completion_function);
	}
	return NULL;	/* argument_generator */
}

void
init_console()
{
	/* GNU readline initialize */
	rl_readline_name = APPNAME;
	rl_completion_entry_function = argument_generator;
	rl_attempted_completion_function = fileman_completion;
	rl_special_prefixes = "$";
	using_history();
	stifle_history(100);
}

PRIVATE void
truncspace(s)
	char *s;
{
	char *p;
	int len;

	if ((len = strlen(s)) == 0)
		return;

	p = s + len;
	while (len > 0 && (*(p - 1) == ' ' || *(p - 1) == ';')) {
		len--;
		*--p = '\0';
	}

	p = s;
	while (len > 0 && (*p == ' ' || *p ==';')) {
		len--;
		p++;
	}
	if (p != s) {
		memmove(s, p, len + 1);
	}
}

char *
get_line(reason)
	int *reason;
{
	char *in;
	static char *save_in = NULL;	/* for ignore-dup */

start:
	/* command in */
	in = NULL;

	if (input_function) {
		in = input_function();
	} else {
		int signaled = 0;
		TRY {
			sigprocmask(SIG_SETMASK, &unblock_mask, NULL);
			in = readline(noprompt? NULL: prompt);
			sigprocmask(SIG_BLOCK, &block_mask, NULL);
		} CATCH {
			/* SIGINT */
			sigprocmask(SIG_BLOCK, &block_mask, NULL);
			signaled = 1;
		}
		ENDTRY

		if (signaled) {
			if (in) {
				free(in);
			}
			fprintf(stdout, "\n");
			if (reason)
				*reason = G_INTR;
			return NULL;
		}
	}

	/* EOF check */
	if (in == NULL) {
		if (reason)
			*reason = G_EOF;
		return NULL;
	}

	truncspace(in);
	/* null command check */
	if (*in == '\0') {
		free(in);
		goto start;
	}

	if (input_function == NULL) {
		/* add history */
		if (save_in == NULL || strcmp(save_in, in) != 0) {
			add_history(in);
		}
		/* save_in for nodup-history */
		if (save_in) {
			free(save_in);
		}
		save_in = strdup(in);	/* ignore error */
	}

	return in;
}

PRIVATE addr_t save_ad[MAXARGS];
PRIVATE int save_cnt;

void
#ifdef __STDC__
mprintf(const char *fmt, ...)
#else /*__STDC__*/
mprintf(fmt, va_alist)
	char *fmt;
	va_dcl
#endif /*__STDC__*/
{
	va_list ap;
	int longflag;

	sigpiped = 0;
#if __STDC__
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	(void)vfprintf(fp, fmt, ap);

	while (*fmt) {
		switch (*fmt++) {
		case '%':
			while (isdigit(*fmt))
				fmt++;
			if (*fmt == 'l') {
				fmt++;
				longflag = 1;
			} else if (*fmt == 'L') {
				fmt++;
				longflag = 2;
			} else {
				longflag = 0;
			}
			switch (*fmt++) {
			case 'x':
			case 'd':
			case 'u':
			case 'c':
				switch (longflag) {
				case 0:
					save_ad[save_cnt++] = (addr_t)va_arg(ap, int);
					break;
				case 1:
					save_ad[save_cnt++] = (addr_t)va_arg(ap, long);
					break;
				case 2:
					save_ad[save_cnt++] = (addr_t)va_arg(ap, long long);
					break;
				}
				break;
			case 'p':
				save_ad[save_cnt++] = (addr_t)va_arg(ap, void *);
				break;
			case 'f':
				(void)va_arg(ap, double);
				break;
			case 's':
				(void)va_arg(ap, char *);
				break;
			}
			if (save_cnt >= MAXARGS)
				save_cnt--;
			break;

		case '\n':
			while (save_cnt > 0) {
				xaddr[xaddr_ptr++] = save_ad[--save_cnt];
				if (xaddr_ptr >= MAXARGS)
					xaddr_ptr = 0;
			}
			break;
		}
	}
	va_end(ap);

	if (sigpiped) {
		sigpiped = 0;
		THROW("Broken pipe");
	}
}

void
mprintbit(bit, flags)
	const struct bitname *bit;
	long flags;
{
	long decode = 0;
	sigpiped = 0;

	while (bit->value) {
		if (bit->value & flags) {
			decode |= bit->value;
			fprintf(fp, " %s", bit->name);
		}
		bit++;
	}

	if (decode != flags)
		fprintf(fp, " 0x%lx", flags - decode);
	else if (flags == 0)
		fprintf(fp, " -");

	save_ad[save_cnt] = flags;
	if (save_cnt < MAXARGS - 1)
		save_cnt++;

	if (sigpiped) {
		sigpiped = 0;
		THROW("Broken pipe");
	}
}

int
mprintstr(p, maxlen)
	const unsigned char *p;
	int maxlen;
{
	int nc = 0;
	sigpiped = 0;

	while (--maxlen >= 0 && *p) {
		if (*p >= ' ' && *p < 0x7f) {
			fputc(*p, fp);
			nc++;
		} else {
			fprintf(fp, "\\x%02x", *p);
			nc += 4;
		}
		p++;
	}

	if (sigpiped) {
		sigpiped = 0;
		THROW("Broken pipe");
	}
	return nc;
}
