File:  [ELWIX - Embedded LightWeight unIX -] / libaitcli / example / t.c
Revision 1.2: download - view: text, annotated - select for diffs - revision graph
Wed Mar 16 17:24:03 2011 UTC (13 years, 2 months ago) by misho
Branches: MAIN
CVS tags: cli4_6, cli4_5, cli4_4, cli4_3, cli4_2, cli4_1, cli4_0, cli3_9, cli3_8, cli3_7, cli3_6, cli3_5, cli3_4, cli3_3, cli3_2, cli3_1, cli3_0, cli2_3, cli2_2, cli2_1, HEAD, CLI4_5, CLI4_4, CLI4_3, CLI4_2, CLI4_1, CLI4_0, CLI3_9, CLI3_8, CLI3_7, CLI3_6, CLI3_5, CLI3_4, CLI3_3, CLI3_2, CLI3_1, CLI3_0, CLI2_3, CLI2_2, CLI2_1, CLI2_0
2.0

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <termios.h>
#include <poll.h>
#include <aitio.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/queue.h>
#include "keys.h"


int freeLineCLI(linebuffer_t * __restrict buffer);


static inline void
clrscrEOL(linebuffer_t * __restrict buf)
{
	register int i;

	if (buf) {
		write(buf->line_out, K_CR, 1);

		for (i = 0; i < buf->line_len; i++)
			write(buf->line_out, K_SPACE, 1);
	}
}

static inline void
printfEOL(linebuffer_t * __restrict buf, int len, int prompt)
{
	if (buf) {
		write(buf->line_out, K_CR, 1);

		if (prompt && buf->line_prompt)
			write(buf->line_out, buf->line_prompt, buf->line_bol);

		write(buf->line_out, buf->line_buf, len == -1 ? buf->line_eol - buf->line_bol: len);
	}
}

static inline void
printfCR(linebuffer_t * __restrict buf, int prompt)
{
	if (buf) {
		write(buf->line_out, K_CR, 1);

		if (prompt)
			if (prompt && buf->line_prompt)
				write(buf->line_out, buf->line_prompt, buf->line_bol);
	}
}

static inline void
printfCLI(linebuffer_t * __restrict buf, const unsigned char *text, int textlen, int prompt)
{
	if (buf && text && textlen) {
		if (prompt && buf->line_prompt)
			write(buf->line_out, buf->line_prompt, buf->line_bol);

		write(buf->line_out, text, textlen);
	}
}


static int
catCh2Buf(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;
	int pos;
	unsigned char b[BUFSIZ];

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	pos = buf->line_eol - buf->line_bol;

	if (buf->line_mode == LINEMODE_INS)
		memmove(buf->line_buf + pos + buf->line_keys[idx].key_len, buf->line_buf + pos, 
				buf->line_len - buf->line_eol);
	if (buf->line_mode == LINEMODE_INS || buf->line_eol == buf->line_len - 1)
		buf->line_len += buf->line_keys[idx].key_len;
	buf->line_eol += buf->line_keys[idx].key_len;

	memcpy(buf->line_buf + pos, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
	buf->line_buf[buf->line_len - 1] = 0;

	write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);

	if (buf->line_mode == LINEMODE_INS) {
		printfCLI(buf, (const u_char*) buf->line_buf + pos + buf->line_keys[idx].key_len, 
				buf->line_len - buf->line_eol, 0);
		printfEOL(buf, -1, 1);
	}
	return RETCODE_OK;
}

static int
bufEOL(int idx, void * __restrict buffer)
{
	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	printfCR(buffer, 1);
	return RETCODE_EOL;
}

static int
bufEOF(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	write(buf->line_out, buf->line_keys[idx].key_ch, buf->line_keys[idx].key_len);
	return RETCODE_EOF;
}

static int
bufTab(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	/* show */
	write(buf->line_out, "shmink", 6);

	return RETCODE_OK;
}

static int
bufUP(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;
	int pos;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	if (!buf->line_h)
		buf->line_h = TAILQ_FIRST(&buf->line_history);
	else
		buf->line_h = TAILQ_NEXT(buf->line_h, hist_next);
	if (!buf->line_h)
		return RETCODE_OK;

	clrscrEOL(buf);
	freeLineCLI(buf);

	pos = buf->line_eol - buf->line_bol;

	buf->line_len += buf->line_h->hist_len;
	buf->line_eol += buf->line_h->hist_len;

	memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
	buf->line_buf[buf->line_len - 1] = 0;

	printfEOL(buf, -1, 1);
	return RETCODE_OK;
}

static int
bufDOWN(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;
	int pos;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	if (!buf->line_h)
		buf->line_h = TAILQ_LAST(&buf->line_history, tqHistoryHead);
	else
		buf->line_h = TAILQ_PREV(buf->line_h, tqHistoryHead, hist_next);
	if (!buf->line_h)
		return RETCODE_OK;

	clrscrEOL(buf);
	freeLineCLI(buf);

	pos = buf->line_eol - buf->line_bol;

	buf->line_len += buf->line_h->hist_len;
	buf->line_eol += buf->line_h->hist_len;

	memcpy(buf->line_buf + pos, buf->line_h->hist_line, buf->line_h->hist_len);
	buf->line_buf[buf->line_len - 1] = 0;

	printfEOL(buf, -1, 1);
	return RETCODE_OK;
}

static int
bufClr(int idx, void * __restrict buffer)
{
	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	clrscrEOL(buffer);
	freeLineCLI(buffer);

	printfCR(buffer, 1);
	return RETCODE_OK;
}

static int
bufBS(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	if (buf->line_bol < buf->line_eol) {
		clrscrEOL(buf);

		buf->line_eol--;
		buf->line_len--;
		memmove(buf->line_buf + buf->line_eol - buf->line_bol, 
				buf->line_buf + buf->line_eol - buf->line_bol + 1, 
				buf->line_len - buf->line_eol);
		buf->line_buf[buf->line_len - 1] = 0;

		printfEOL(buf, buf->line_len - 1, 1);
		printfEOL(buf, -1, 1);
	}

	return RETCODE_OK;
}

static int
bufBTAB(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	if (buf->line_bol < buf->line_eol) {
		clrscrEOL(buf);

		buf->line_len = buf->line_eol - buf->line_bol + 1;
		buf->line_buf[buf->line_len - 1] = 0;

		printfEOL(buf, -1, 1);
	}

	return RETCODE_OK;
}

static int
bufMode(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	buf->line_mode = !buf->line_mode ? LINEMODE_OVER : LINEMODE_INS;
	return RETCODE_OK;
}

static int
bufBegin(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	buf->line_eol = buf->line_bol;

	printfCR(buf, 1);
	return RETCODE_OK;
}

static int
bufEnd(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	buf->line_eol = buf->line_len - 1;

	printfEOL(buf, -1, 1);
	return RETCODE_OK;
}

static int
bufLEFT(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	if (buf->line_bol < buf->line_eol)
		printfEOL(buf, --buf->line_eol - buf->line_bol, 1);

	return RETCODE_OK;
}

static int
bufRIGHT(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	if (buf->line_eol < buf->line_len - 1)
		printfEOL(buf, ++buf->line_eol - buf->line_bol, 1);

	return RETCODE_OK;
}

static int
bufDel(int idx, void * __restrict buffer)
{
	linebuffer_t *buf = buffer;

	if (!buffer || idx < 0 || idx > MAX_BINDKEY)
		return RETCODE_ERR;

	clrscrEOL(buf);

	buf->line_len--;
	memmove(buf->line_buf + buf->line_eol - buf->line_bol, 
			buf->line_buf + buf->line_eol - buf->line_bol + 1, 
			buf->line_len - buf->line_eol);
	buf->line_buf[buf->line_len - 1] = 0;

	printfEOL(buf, buf->line_len - 1, 1);
	printfEOL(buf, -1, 1);

	return RETCODE_OK;
}

// ---------------------------------------------------------------

int
bindKeyCLI(bindkey_t * __restrict key, linebuffer_t * __restrict buffer)
{
	register int i;

	if (!key || !buffer)
		return RETCODE_ERR;

	for (i = 0; i < MAX_BINDKEY; i++)
		if (key->key_len == buffer->line_keys[i].key_len && 
				!memcmp(key->key_ch, buffer->line_keys[i].key_ch, key->key_len)) {
			buffer->line_keys[i].key_func = key->key_func;
			return i;
		}
	return RETCODE_OK;
}

linebuffer_t *
initCLI(int fin, int fout, const char *prompt)
{
	linebuffer_t *buffer;
	bindkey_t *keys;
	register int i;
	struct termios t;

	memset(&t, 0, sizeof t);
	/* init buffer */
	buffer = malloc(sizeof (linebuffer_t));
	if (!buffer)
		return NULL;
	else {
		memset(buffer, 0, sizeof(linebuffer_t));

		buffer->line_in = fin;
		buffer->line_out = fout;

		TAILQ_INIT(&buffer->line_history);

		if (prompt) {
			buffer->line_prompt = strdup(prompt);
			if (!buffer->line_prompt) {
				free(buffer);
				return NULL;
			} else {
				buffer->line_bol = strlen(buffer->line_prompt);
				buffer->line_eol = buffer->line_bol;
			}
		}
	}
	buffer->line_buf = malloc(BUFSIZ);
	if (!buffer->line_buf) {
		if (buffer->line_prompt)
			free(buffer->line_prompt);
		free(buffer);
		return NULL;
	} else {
		memset(buffer->line_buf, 0, BUFSIZ);
		buffer->line_len = 1 + buffer->line_eol;
	}
	keys = calloc(MAX_BINDKEY + 1, sizeof(bindkey_t));
	if (!keys) {
		if (buffer->line_prompt)
			free(buffer->line_prompt);
		free(buffer->line_buf);
		free(buffer);
		return NULL;
	} else
		memset(keys, 0, sizeof(bindkey_t) * (MAX_BINDKEY + 1));

	/* fill key bindings */
	// ascii chars & ctrl+chars
	for (i = 0; i < 256; i++) {
		*keys[i].key_ch = (unsigned char) i;
		keys[i].key_len = 1;

		if (!i || i == *K_CTRL_D)
			keys[i].key_func = bufEOF;
		if (i == *K_CTRL_M || i == *K_CTRL_J)
			keys[i].key_func = bufEOL;
		if (i == *K_CTRL_H || i == *K_BACKSPACE)
			keys[i].key_func = bufBS;
		if (i == *K_CTRL_C)
			keys[i].key_func = bufClr;
		if (i == *K_CTRL_A)
			keys[i].key_func = bufBegin;
		if (i == *K_CTRL_E)
			keys[i].key_func = bufEnd;
		if (i >= *K_SPACE && i < *K_BACKSPACE)
			keys[i].key_func = catCh2Buf;
		if (i > *K_BACKSPACE && i < 0xff)
			keys[i].key_func = catCh2Buf;
	}
	// alt+chars
	for (i = 256; i < 512; i++) {
		keys[i].key_ch[0] = 0x1b;
		keys[i].key_ch[1] = (unsigned char) i - 256;
		keys[i].key_len = 2;
	}

	// 3 bytes
	keys[i].key_len = sizeof K_F1 - 1;
	memcpy(keys[i].key_ch, K_F1, keys[i++].key_len);
	keys[i].key_len = sizeof K_F2 - 1;
	memcpy(keys[i].key_ch, K_F2, keys[i++].key_len);
	keys[i].key_len = sizeof K_F3 - 1;
	memcpy(keys[i].key_ch, K_F3, keys[i++].key_len);
	keys[i].key_len = sizeof K_F4 - 1;
	memcpy(keys[i].key_ch, K_F4, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F1 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F1, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F2 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F2, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F3 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F3, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F4 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F4, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F5 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F5, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F6 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F6, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F7 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F7, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F8 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F8, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F9 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F9, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F10 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F10, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F11 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F11, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_SH_F12 - 1;
	memcpy(keys[i].key_ch, K_CTRL_SH_F12, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F1 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F1, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F2 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F2, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F3 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F3, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F4 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F4, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F5 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F5, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F6 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F6, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F7 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F7, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F8 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F8, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F9 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F9, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F10 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F10, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F11 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F11, keys[i++].key_len);
	keys[i].key_len = sizeof K_CTRL_F12 - 1;
	memcpy(keys[i].key_ch, K_CTRL_F12, keys[i++].key_len);
	keys[i].key_len = sizeof K_HOME - 1;
	keys[i].key_func = bufBegin;
	memcpy(keys[i].key_ch, K_HOME, keys[i++].key_len);
	keys[i].key_len = sizeof K_END - 1;
	keys[i].key_func = bufEnd;
	memcpy(keys[i].key_ch, K_END, keys[i++].key_len);
	keys[i].key_len = sizeof K_UP - 1;
	keys[i].key_func = bufUP;
	memcpy(keys[i].key_ch, K_UP, keys[i++].key_len);
	keys[i].key_len = sizeof K_DOWN - 1;
	keys[i].key_func = bufDOWN;
	memcpy(keys[i].key_ch, K_DOWN, keys[i++].key_len);
	keys[i].key_len = sizeof K_RIGHT - 1;
	keys[i].key_func = bufRIGHT;
	memcpy(keys[i].key_ch, K_RIGHT, keys[i++].key_len);
	keys[i].key_len = sizeof K_LEFT - 1;
	keys[i].key_func = bufLEFT;
	memcpy(keys[i].key_ch, K_LEFT, keys[i++].key_len);
	keys[i].key_len = sizeof K_BTAB - 1;
	keys[i].key_func = bufBTAB;
	memcpy(keys[i].key_ch, K_BTAB, keys[i++].key_len);
	// 4 bytes
	keys[i].key_len = sizeof K_INS - 1;
	keys[i].key_func = bufMode;
	memcpy(keys[i].key_ch, K_INS, keys[i++].key_len);
	keys[i].key_len = sizeof K_DEL - 1;
	keys[i].key_func = bufDel;
	memcpy(keys[i].key_ch, K_DEL, keys[i++].key_len);
	keys[i].key_len = sizeof K_PGUP - 1;
	memcpy(keys[i].key_ch, K_PGUP, keys[i++].key_len);
	keys[i].key_len = sizeof K_PGDN - 1;
	memcpy(keys[i].key_ch, K_PGDN, keys[i++].key_len);
	// 5 bytes
	keys[i].key_len = sizeof K_F5 - 1;
	memcpy(keys[i].key_ch, K_F5, keys[i++].key_len);
	keys[i].key_len = sizeof K_F6 - 1;
	memcpy(keys[i].key_ch, K_F6, keys[i++].key_len);
	keys[i].key_len = sizeof K_F7 - 1;
	memcpy(keys[i].key_ch, K_F7, keys[i++].key_len);
	keys[i].key_len = sizeof K_F8 - 1;
	memcpy(keys[i].key_ch, K_F8, keys[i++].key_len);
	keys[i].key_len = sizeof K_F9 - 1;
	memcpy(keys[i].key_ch, K_F9, keys[i++].key_len);
	keys[i].key_len = sizeof K_F10 - 1;
	memcpy(keys[i].key_ch, K_F10, keys[i++].key_len);
	keys[i].key_len = sizeof K_F11 - 1;
	memcpy(keys[i].key_ch, K_F11, keys[i++].key_len);
	keys[i].key_len = sizeof K_F12 - 1;
	memcpy(keys[i].key_ch, K_F12, keys[i++].key_len);

	tcgetattr(buffer->line_in, &t);
	t.c_lflag &= ~(ICANON | ISIG | IEXTEN | ECHO | ECHOCTL | ECHOE | ECHOK | ECHOKE | ECHONL | ECHOPRT);
	t.c_iflag |= IGNBRK;
	t.c_cc[VMIN] = 1;
	t.c_cc[VTIME] = 0;
	tcsetattr(buffer->line_in, TCSANOW, &t);

	buffer->line_keys = keys;
	return buffer;
}

void
endCLI(linebuffer_t * __restrict buffer)
{
	struct tagHistory *h;

	if (buffer) {
		while ((h = TAILQ_FIRST(&buffer->line_history))) {
			TAILQ_REMOVE(&buffer->line_history, h, hist_next);
			free(h);
		}

		if (buffer->line_prompt)
			free(buffer->line_prompt);

		if (buffer->line_keys)
			free(buffer->line_keys);
		if (buffer->line_buf)
			free(buffer->line_buf);

		free(buffer);
		buffer = NULL;
	}
}

void
setPromptCLI(linebuffer_t * __restrict buffer, const char *prompt)
{
	if (buffer) {
		if (buffer->line_prompt) {
			free(buffer->line_prompt);
			buffer->line_prompt = NULL;
			buffer->line_bol = 0;
		}

		if (prompt) {
			buffer->line_prompt = strdup(prompt);
			if (buffer->line_prompt) {
				buffer->line_bol = strlen(buffer->line_prompt);
				buffer->line_eol = buffer->line_bol;
				buffer->line_len = 1 + buffer->line_eol;
			}
		}
	}
}

int
freeLineCLI(linebuffer_t * __restrict buffer)
{
	int code = RETCODE_ERR;

	if (buffer) {
		if (buffer->line_buf)
			free(buffer->line_buf);

		buffer->line_buf = malloc(BUFSIZ);
		if (buffer->line_buf) {
			memset(buffer->line_buf, 0, BUFSIZ);
			buffer->line_eol = buffer->line_bol;
			buffer->line_len = 1 + buffer->line_eol;

			code = RETCODE_OK;
		}
	}

	return code;
}

int
readLineCLI(linebuffer_t * __restrict buffer)
{
	int code, readLen;
	register int i;
	char buf[BUFSIZ];
	struct pollfd fds;

	if (!buffer)
		return RETCODE_ERR;

	memset(&fds, 0, sizeof fds);
	fds.fd = buffer->line_in;
	fds.events = POLLIN;

	printfCR(buffer, 1);
	while (42) {
		if (poll(&fds, 1, -1) < 1)
			return RETCODE_ERR;

		memset(buf, 0, sizeof buf);
		readLen = read(buffer->line_in, buf, BUFSIZ);
		if (readLen == -1)
			return RETCODE_ERR;
		if (!readLen)
			return RETCODE_EOF;

recheck:
//		for (i = 0; i < readLen; i++)
//			printf("i=%d readLen=%d buf=%x\n", i, readLen, (u_char) buf[i]);
		for (code = RETCODE_OK, i = MAX_BINDKEY - 1; i > -1; i--)
			if (readLen >= buffer->line_keys[i].key_len && 
					!memcmp(buffer->line_keys[i].key_ch, buf, buffer->line_keys[i].key_len)) {
				readLen -= buffer->line_keys[i].key_len;
				if (readLen)
					memmove(buf, buf + buffer->line_keys[i].key_len, readLen);
				else
					memset(buf, 0, buffer->line_keys[i].key_len);

				if (buffer->line_keys[i].key_func)
					if ((code = buffer->line_keys[i].key_func(i, buffer)))
						readLen = 0;

				if (readLen)
					goto recheck;
				else
					break;
			}

		if (code)
			break;
	}

	return code;
}

int
addHistoryCLI(linebuffer_t * __restrict buffer, const char * __restrict str)
{
	struct tagHistory *h;

	if (!buffer)
		return RETCODE_ERR;

	if (!(h = malloc(sizeof(struct tagHistory)))) {
		return RETCODE_ERR;
	} else
		memset(h, 0, sizeof(struct tagHistory));

	if (str) {
		if (!*str) {
			free(h);
			return RETCODE_OK;
		}

		h->hist_len = strlcpy(h->hist_line, str, BUFSIZ);
	} else {
		if (!*buffer->line_buf || buffer->line_len < 2) {
			free(h);
			return RETCODE_OK;
		}

		memcpy(h->hist_line, buffer->line_buf, (h->hist_len = buffer->line_len));
		io_TrimStr((unsigned char*) h->hist_line);
		h->hist_len = strlen(h->hist_line);
	}

	TAILQ_INSERT_HEAD(&buffer->line_history, h, hist_next);
	return h->hist_len;
}

int
saveHistoryCLI(linebuffer_t * __restrict buffer, const char *histfile, int lines)
{
	FILE *f;
	mode_t mode;
	char szFName[MAXPATHLEN];
	struct tagHistory *h;

	if (!buffer)
		return RETCODE_ERR;
	if (!histfile)
		strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
	else
		strlcpy(szFName, histfile, MAXPATHLEN);

	mode = umask(0177);
	f = fopen(szFName, "w");
	if (!f)
		return RETCODE_ERR;
	TAILQ_FOREACH(h, &buffer->line_history, hist_next) {
		fprintf(f, "%s\n", h->hist_line);

		if (lines)
			lines--;
		else
			break;
	}
	fclose(f);
	umask(mode);

	return RETCODE_OK;
}

int
loadHistoryCLI(linebuffer_t * __restrict buffer, const char *histfile)
{
	FILE *f;
	char szFName[MAXPATHLEN], buf[BUFSIZ];
	struct tagHistory *h;

	if (!buffer)
		return RETCODE_ERR;
	if (!histfile)
		strlcpy(szFName, HISTORY_FILE, MAXPATHLEN);
	else
		strlcpy(szFName, histfile, MAXPATHLEN);

	f = fopen(szFName, "r");
	if (!f)
		return RETCODE_ERR;
	while (fgets(buf, BUFSIZ, f)) {
		if (!*buf || *buf == '#')
			continue;
		else
			io_TrimStr((unsigned char*) buf);

		if (!(h = malloc(sizeof(struct tagHistory)))) {
			fclose(f);
			return RETCODE_ERR;
		} else
			memset(h, 0, sizeof(struct tagHistory));

		h->hist_len = strlcpy(h->hist_line, buf, BUFSIZ);
		TAILQ_INSERT_TAIL(&buffer->line_history, h, hist_next);
	}
	fclose(f);

	return RETCODE_OK;
}

inline void
resetHistoryCLI(linebuffer_t * __restrict buffer)
{
	buffer->line_h = NULL;
}

// ------------------------------------------------------------

int
main()
{
	int ret;
	bindkey_t key = { sizeof K_TAB - 1, K_TAB, bufTab };
	linebuffer_t *buffer = initCLI(STDIN_FILENO, STDOUT_FILENO, CLI_PROMPT);

	bindKeyCLI(&key, buffer);

	loadHistoryCLI(buffer, NULL);

	while (42) {
		ret = readLineCLI(buffer);
		addHistoryCLI(buffer, NULL);

		printf("LINE=%s (%d)/%d/%d CODE=%d\n", buffer->line_buf, buffer->line_len, buffer->line_eol, buffer->line_bol, ret);

		freeLineCLI(buffer);
		resetHistoryCLI(buffer);

		if (ret == RETCODE_EOF)
			break;
	}

	saveHistoryCLI(buffer, NULL, HISTORY_LINES);

	endCLI(buffer);
	return 0;
}

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>