/*	$NetBSD: prompt.c,v 1.5 2019/01/09 03:28:31 christos Exp $	*/

/*-
 * Copyright (c) 2004 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by UCHIYAMA Yasushi.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>

#include <sys/param.h>
#include <machine/bfs.h>

#include "cmd.h"
#include "local.h"

#define	LOG_SIZE	2048
#define	LOG_MASK	(LOG_SIZE - 1)
uint8_t __log[LOG_SIZE];
int __log_cnt;

#define	CMDBUF_HISTORY_MAX	8
#define	PROMPT			">> "

struct cmd_buf {
	char buf[CMDBUF_SIZE];
	int cur, min, max;
} __cmd_buf[CMDBUF_HISTORY_MAX];

struct cmd_buf *__cmd;
int __cur_cmd;

void prompt_reset(void);
void prompt_input(int);
extern void __putchar(int);

void
prompt(void)
{
	int c;

	prompt_reset();
	while (/*CONSTCOND*/1)
		if ((c = getchar()) != 0)
			prompt_input(c);
	/* NOTREACHED */
}

void
prompt_input(int c)
{
	int i;

	switch (c) {
	case 32 ... 127:
		__cmd->buf[__cmd->cur] = c;
		__cmd->cur = (__cmd->cur >= CMDBUF_SIZE - 1) ? __cmd->cur :
		    __cmd->cur + 1;
		if (__cmd->cur >= __cmd->max)
			__cmd->max = __cmd->cur;
		putchar(c);
		break;
	case '\r':
		putchar('\n');
		if (__cmd->max > 0) {
			cmd_exec(__cmd->buf);
			prompt_reset();
		} else {
			printf(PROMPT);
		}
		break;
	case '\b':
		if (__cmd->cur > 0) {
			__cmd->buf[--__cmd->cur] = 0;
			putchar(c);
		}
		break;
	case 1:		/* Ctrl a */
		__cmd->cur = __cmd->min;
		putchar('\r');
		printf("%s", PROMPT);
		break;
	case 5:		/* Ctrl e */
		__cmd->cur = __cmd->max;
		goto redraw;
	case 2:		/* Ctrl b */
		if (__cmd->cur == __cmd->min)
			return;
		__cmd->cur--;
		putchar(c);
		break;
	case 6:		/* Ctrl f */
		if (__cmd->cur == __cmd->max)
			return;
		__cmd->cur++;
		putchar(c);
		break;
	case 4:		/* Ctrl d */
		if (__cmd->cur == __cmd->max)
			return;
		for (i = __cmd->cur; i < __cmd->max; i++)
			__cmd->buf[i] = __cmd->buf[i + 1];
		__cmd->buf[i] = '\0';
		__cmd->max--;
		goto redraw;
	case 11:	/* Ctrl k */
		for (i = __cmd->cur; i < __cmd->max; i++) {
			__cmd->buf[i] = 0;
			__cmd->max = __cmd->cur;
		}
		putchar(c);
		break;
	case 14:	/* Ctrl n */
		__cur_cmd = (__cur_cmd + 1) & 0x7;
		goto history_redraw;
	case 16:	/* Ctrl p */
		__cur_cmd = (__cur_cmd - 1) & 0x7;
	history_redraw:
		__cmd = &__cmd_buf[__cur_cmd];
		__cmd->cur = __cmd->max;
	case 12:	/* Ctrl l */
	redraw:
		putchar('\r');
		putchar(11);
		printf("%s%s", PROMPT, __cmd->buf);
		break;
	}
}

void
prompt_reset(void)
{

	__cur_cmd = (__cur_cmd + 1) & 0x7;
	__cmd = &__cmd_buf[__cur_cmd];
	memset(__cmd, 0, sizeof *__cmd);
	printf(PROMPT);
}

bool
prompt_yesno(int interactive)
{
	int i;

	if (!interactive)
		return true;
	/* block until user input */
	while (/*CONSTCOND*/1) {
		if ((i = getchar()) == 0)
			continue;
		if (i == 'N' || i == 'n')
			return false;
		if (i == 'Y' || i == 'y')
			return true;
	}
}

void
putchar(int c)
{

	__putchar(c);

	__log[__log_cnt++] = c;
	__log_cnt &= LOG_MASK;
}

int
cmd_log_save(int argc, char *argp[], int interactive)
{
	struct bfs *bfs;

	if (bfs_init(&bfs) != 0) {
		printf("no BFS partition.\n");
		return 1;
	}

	if (bfs_file_write(bfs, "boot.log", __log, LOG_SIZE) != 0)
		printf("BFS write failed.\n");

	bfs_fini(bfs);

	return 0;
}
