/******************************************************************************
 * This file is part of a software distribution, which is furnished under the *
 * terms of a license.  Use of this software  by any means is subject to this *
 * license  and  signifies  the  acceptance of  the  licensing  terms  stated *
 * therein. Please see  the file LICENSE in the  top-level directory  of this *
 * software  distribution  for detailed copyright  disclaimers  and licensing *
 * terms.                                                                     *
 ******************************************************************************
 * Copryight (c) by Andreas S. Wetzel - All rights reserved.                  *
 ******************************************************************************/

/* $Id: ed_funcs.c,v 1.3 2001/03/19 23:17:28 mickey Exp $ */

#include <vchat.h>
#include <proto_common.h>

/*** Globals ***/

extern int in_chat_window;

extern ED *ed;
extern VP vp;
extern VTCAP vtcap;
extern ULIST_ITEM	*ulist_base;
extern int		ulist_cnt;

/*** Code ***/

void curs_left(void)
{
	u_int treshold;

	/*
	 * Nothing happens if already at buffer start.
	 */

	if(ed->curpos <= ed->buffer)
		return;

	/*
	 * Decrement buffer position.
	 */

	ed->curpos--;

	if(ed->curpos < ed->margin && !(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Scrolling _IS_ necessary - determine scrolling mode.
		 */

		if(vp.sc_mode == 0)
		{
			/*
			 * Character scrolling.
			 */

			ed->margin--;

			if(HAS_IC)
			{
				if(in_chat_window)
					mv(ED_SCRPOS, vtcap.rows);

				tputs(vtcap.insertchar, 1, (void *)outc);
				putchar(*ed->curpos);
				mv(ED_SCRPOS, vtcap.rows);
			}
			else if(HAS_IM)
			{
				if(in_chat_window)
					mv(ED_SCRPOS, vtcap.rows);

				tputs(vtcap.insert_on, 1, (void *)outc);
				putchar(*ed->curpos);
				tputs(vtcap.insert_off, 1, (void *)outc);
				mv(ED_SCRPOS, vtcap.rows);
			}
			else
			{
				line_update();
			}
		}
		else if(vp.sc_mode == 1)
		{
			/*
			 * Block scrolling.
			 */

			treshold = (u_int)((vtcap.cols - ed->offset) / 3);
			ed->margin = (ed->curpos > (ed->buffer + treshold)) ? (ed->margin - treshold) : ed->buffer;
			line_update();
		}
	}
	else if(!(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Scrolling is _NOT_ necessary - Just move the cursor.
		 */

		mv(ED_SCRPOS, vtcap.rows);
	}
	else if(in_chat_window)
	{
		/*
		 * NO_ECHO mode - just set the cursor back into the input line.
		 */

		mv((1 + ed->offset), vtcap.rows);
	}

	/*
	 * Cursor should now reside in the input line not the chat window.
	 */

	in_chat_window = 0;
}

void curs_right(void)
{
	u_int treshold;

	/*
	 * When already at end of buffer, nothing happens.
	 */

	if(ed->curpos >= ed->endpos)
		return;

	/*
	 * Increment the buffer position.
	 */

	ed->curpos++;

	if(ED_SCRPOS > vtcap.cols && !(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Scrolling _IS_ necessary. Determine selected scroll mode.
		 */

		if(vp.sc_mode == 0)
		{
			/*
			 * Character scrolling.
			 */

			ed->margin++;

			if(HAS_DC)
			{
				mv((1 + ed->offset), vtcap.rows);
				tputs(vtcap.deletechar, 1, (void *)outc);
				mv(vtcap.cols, vtcap.rows);
				putchar(*ed->curpos);
			}
			else if(HAS_DM)
			{
				/* Huh ? */

				line_update();
			}
			else
			{
				line_update();
			}
		}
		else if(vp.sc_mode == 1)
		{
			/*
			 * Block scrolling.
			 */

			treshold = (u_int)((vtcap.cols - ed->offset) / 3);
			ed->margin = ((ed->margin + treshold) > ED_MARGINMAX) ? ED_MARGINMAX : (ed->margin + treshold);
			line_update();
		}
	}
	else if(!(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Scrolling not necessary, just move the cursor.
		 */

		mv(ED_SCRPOS, vtcap.rows);
	}
	else if(in_chat_window)
	{
		/*
		 * NO_ECHO mode, just move the cursor back to input line.
		 */

		mv((1 + ed->offset), vtcap.rows);
	}

	/*
	 * Cursor should now be in the input line, not in the chat window.
	 */

	in_chat_window = 0;
}

void backspace(void)
{
	u_int bs_treshold;

	/*
	 * We don't do anything if we
	 * are already at buffer-start.
	 */

	if(ed->curpos <= ed->buffer)
		return;

	if(ed->curpos != ed->endpos)
	{
		/*
		 * Cursor stands in-line
		 */

		memmove((char *)(ed->curpos - 1), ed->curpos, ED_REMAIN);
		*(--ed->endpos) = '\0';
		--ed->curpos;

		if(ed->flags & ED_NO_ECHO)
		{
			if(in_chat_window)
			{
				mv((1 + ed->offset), vtcap.rows);
				in_chat_window = 0;
			}
			return;
		}

		switch(vp.bs_mode)
		{
			case 0:		/*
					 * Jump & Move
					 */

					bs_treshold = (u_int)((vtcap.cols - ed->offset) / 3);

					if(ed->curpos < (ed->margin + bs_treshold))
					{
						ed->margin = (ed->margin > (ed->buffer + bs_treshold)) ? (char *)(ed->margin - bs_treshold) : ed->buffer;

						line_update();
					}
					else
					{
						inline_backspace();
					}

					break;

			case 1:		/*
			default:	 * Reverse delete
					 */

					if(ed->margin > ed->buffer)
					{
						slide_backspace();
					}
					else
					{
						inline_backspace();
					}

					break;
		}
	}
	else
	{
		/*
	 	 * Cursor at end of input
		 */

		*(--ed->curpos) = '\0';
		--ed->endpos;

		if(ed->flags & ED_NO_ECHO)
		{
			if(in_chat_window)
			{
				mv((1 + ed->offset), vtcap.rows);
				in_chat_window = 0;
			}
			return;
		}

		switch(vp.bs_mode)
		{
			case 0:		/*
					 * Jump & Move
					 */

					bs_treshold = (u_int)((vtcap.cols - ed->offset) / 3);

					if(ed->curpos < (ed->margin + bs_treshold))
					{
						ed->margin = (ed->margin > (ed->buffer + bs_treshold)) ? (char *)(ed->margin - bs_treshold) : ed->buffer;

						line_update();
					}
					else
					{
						normal_backspace();
					}

					break;

			case 1:		/*
			default:	 * Reverse delete
					 */

					if(ed->margin > ed->buffer)
					{
						slide_backspace();
					}
					else
					{
						normal_backspace();
					}
					break;
		}
	}
}

void normal_backspace(void)
{
	/*
	 * Ordinary backspace
	 */

	if(in_chat_window)
	{
		mv(ED_SCRPOS, vtcap.rows);

		if(vtcap.clear2eol)
			tputs(vtcap.clear2eol, 1, (void *)outc);
		else if(vtcap.clear2eod)
			tputs(vtcap.clear2eod, 1, (void *)outc);

		in_chat_window = 0;
	}
	else
	{
		putchar(BS);
		putchar(' ');
		putchar(BS);
	}		
}

void slide_backspace(void)
{
	/*
	 * Slide window margin
	 */

	--ed->margin;

	if(HAS_DC && HAS_IC)
	{
		if(in_chat_window)
		{
			mv(ED_SCRPOS, vtcap.rows);
			in_chat_window = 0;
		}
		else
		{
			putchar(BS);
		}

		tputs(vtcap.deletechar, 1, (void *)outc);

		mv(1 + ed->offset, vtcap.rows);

		tputs(vtcap.insertchar, 1, (void *)outc);

		putchar(*ed->margin);

		mv(ED_SCRPOS, vtcap.rows);
	}
	else
	{
		line_update();
	}
}

void inline_backspace(void)
{
	/*
	 * Inline backspace
	 */

	if(HAS_DC)
	{
		if(in_chat_window)
		{
			mv(ED_SCRPOS, vtcap.rows);
			in_chat_window = 0;
		}
		else
		{
			putchar(BS);
		}

		tputs(vtcap.deletechar, 1, (void *)outc);

		/*
		 * Anything left to pull over ?
		 */

		if(ed->endpos >= (ed->margin + vtcap.cols))
		{
			mv(vtcap.cols, vtcap.rows);

			putchar(*(ed->margin + vtcap.cols - 1));

			mv(ED_SCRPOS, vtcap.rows);
		}
	}
	else
	{
		/*
		 * We are doing line_update(); here so far :-(
		 */

		line_update();
	}
}

void jump_start(void)
{
	if(ed->margin == ed->buffer)
	{
		/*
		 * Current display area is already at start of buffer.
		 */

		ed->curpos = ed->buffer;

		if(!(ed->flags & ED_NO_ECHO))
		{
			mv(ED_SCRPOS, vtcap.rows);
			in_chat_window = 0;
		}
		else if(in_chat_window)
		{
			mv((1 + ed->offset), vtcap.rows);
			in_chat_window = 0;
		}
	}
	else
	{
		ed->curpos = ed->margin = ed->buffer;

		if(!(ed->flags & ED_NO_ECHO))
		{
			line_update();
		}
		else if(in_chat_window)
		{
			mv((1 + ed->offset), vtcap.rows);
			in_chat_window = 0;
		}
	}
}

void jump_end(void)
{
	ed->curpos = ed->endpos;

	if(ed->curpos >= (ed->margin + vtcap.cols))
	{
		ed->margin = (char *)(ed->endpos - (vtcap.cols + ed->offset) + 1);

		if(!(ed->flags & ED_NO_ECHO))
		{
			line_update();
		}
		else if(in_chat_window)
		{
			mv((1 + ed->offset), vtcap.rows);
			in_chat_window = 0;
		}
	}
	else
	{
		if(!(ed->flags & ED_NO_ECHO))
		{
			mv(ED_SCRPOS, vtcap.rows);
			in_chat_window = 0;
		}
		else if(in_chat_window)
		{
			mv((1 + ed->offset), vtcap.rows);
			in_chat_window = 0;
		}
	}
}

void erase_eol(void)
{
	while(ed->endpos > ed->curpos)
		*ed->endpos-- = '\0';

	*ed->endpos = '\0';

	if(!(ed->flags & ED_NO_ECHO))
	{
		if(vtcap.clear2eol)
		{
			if(in_chat_window)
			{
				mv(ED_SCRPOS, vtcap.rows);
				in_chat_window = 0;
			}
			tputs(vtcap.clear2eol, 1, (void *)outc);
		}
		else if(vtcap.clear2eod)
		{
			if(in_chat_window)
			{
				mv(ED_SCRPOS, vtcap.rows);
				in_chat_window = 0;
			}
			tputs(vtcap.clear2eod, 1, (void *)outc);
		}
		else
		{
			line_update();
		}
	}
	else if(in_chat_window)
	{
		mv((1 + ed->offset), vtcap.rows);
		in_chat_window = 0;
	}
}

void erase_line(void)
{
	BZERO(&ed->buffer, EDBUFSIZE+1);
	ed->endpos = ed->curpos = ed->margin = (char *) &ed->buffer;

	if(!(ed->flags & ED_NO_ECHO))
	{
		if(vtcap.clear2eol)
		{
			mv((1 + ed->offset), vtcap.rows);
			tputs(vtcap.clear2eol, 1, (void *)outc);
			in_chat_window = 0;
		}
		else if(vtcap.clear2eod)
		{
			mv((1 + ed->offset), vtcap.rows);
			tputs(vtcap.clear2eod, 1, (void *)outc);
			in_chat_window = 0;
		}
		else
		{
			line_update();
		}
	}
	else if(in_chat_window)
	{
		mv((1 + ed->offset), vtcap.rows);
		in_chat_window = 0;
	}
}

void insert_char(char c)
{
	/*
	 * Check if there is enough bufferspace available.
	 */

	if(ed->endpos >= (char *) &ed->buffer[ed->maxlen])
		return;

	/*
	 * Move the rest of the buffer one position right.
	 */

	memmove((char *)(ed->curpos + 1), ed->curpos, ED_REMAIN);

	*ed->curpos++ = (u_char) c;

	ed->endpos++;

	if(ED_SCRPOS > vtcap.cols && !(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Cursor has left the screen, scrolling _IS_ necessary.
		 */

		ed->margin++;

		if(HAS_DC && (HAS_IC || HAS_IM))
		{
			mv((1 + ed->offset), vtcap.rows);
			in_chat_window = 0;
			tputs(vtcap.deletechar, 1, (void *)outc);
			mv((vtcap.cols - 1), vtcap.rows);

			if(HAS_IC)
			{
				tputs(vtcap.insertchar, 1, (void *)outc);
				putchar(c);
			}
			else
			{
				tputs(vtcap.insert_on, 1, (void *)outc);
				putchar(c);
				tputs(vtcap.insert_off, 1, (void *)outc);
			}
		}
		else
		{
			line_update();
		}
	}
	else if(!(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Cursor is still on the screen.
		 */

		if(HAS_IC)
		{
			if(in_chat_window)
			{
				mv((ED_SCRPOS - 1), vtcap.rows);
				in_chat_window = 0;
			}

			tputs(vtcap.insertchar, 1, (void *)outc);
			putchar(c);
		}
		else if(HAS_IM)
		{
			if(in_chat_window)
			{
				mv((ED_SCRPOS - 1), vtcap.rows);
				in_chat_window = 0;
			}

			tputs(vtcap.insert_on, 1, (void *)outc);
			putchar(c);
			tputs(vtcap.insert_off, 1, (void *)outc);
		}
		else
		{
			line_update();
		}
	}
	else if(in_chat_window)
	{
		/*
		 * NO_ECHO mode - just set the cursor back into the input line.
		 */

		mv((1 + ed->offset), vtcap.rows);
		in_chat_window = 0;
	}
}

void append_char(char c)
{
	/*
	 * Check if there is enough bufferspace available.
	 */

	if(ed->endpos >= (char *) &ed->buffer[ed->maxlen])
		return;

	*ed->curpos++ = (u_char) c;

	ed->endpos++;

	if(ED_SCRPOS > vtcap.cols && !(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Cursor has left the screen, scrolling _IS_ necessary.
		 */

		ed->margin++;

		if(HAS_DC)
		{
			mv((1 + ed->offset), vtcap.rows);
			tputs(vtcap.deletechar, 1, (void *)outc);
			mv((vtcap.cols - 1), vtcap.rows);
			in_chat_window = 0;
			putchar(c);
		}
		else
		{
			line_update();
		}
	}
	else if(!(ed->flags & ED_NO_ECHO))
	{
		/*
		 * Cursor still on the screen, scrolling is _NOT_ necessary.
		 */

		if(in_chat_window)
		{
			mv((ED_SCRPOS - 1), vtcap.rows);
			in_chat_window = 0;
		}

		putchar(c);
	}
	else if(in_chat_window)
	{
		/*
		 * NO_ECHO mode - just set the cursor back into the input line.
		 */

		mv((1 + ed->offset), vtcap.rows);
		in_chat_window = 0;
	}
}

void delete(void)
{
	/*
	 * Nothing to do here, we're already at end-of-line
	 */

	if(ed->curpos == ed->endpos)
		return;

	if(ed->curpos == (char *)(ed->endpos - 1))
	{
		/*
		 * Delete the last character in line.
		 */

		*(--ed->endpos) = '\0';

		if(in_chat_window)
		{
			mv(ED_SCRPOS, vtcap.rows);
			in_chat_window = 0;
		}

		if(ed->flags & ED_NO_ECHO)
			return;

		if(HAS_DC)
		{
			tputs(vtcap.deletechar, 1, (void *)outc);
		}
		else if(vtcap.clear2eol)
		{
			tputs(vtcap.clear2eol, 1, (void *)outc);
		}
		else if(vtcap.clear2eod)
		{
			tputs(vtcap.clear2eod, 1, (void *)outc);
		}
		else
		{
			putchar(' ');
			putchar(BS);
		}
	}
	else
	{
		/*
		 * Delete ANY character in line.
		 */

		memmove(ed->curpos, (char *)(ed->curpos + 1), (ED_REMAIN-1));
		*(--ed->endpos) = '\0';

		if(ed->flags & ED_NO_ECHO)
		{
			if(in_chat_window)
			{
				mv((1 + ed->offset), vtcap.rows);
				in_chat_window = 0;
			}

			return;
		}

		if(HAS_DC)
		{
			if(in_chat_window)
			{
				mv(ED_SCRPOS, vtcap.rows);
				in_chat_window = 0;
			}

			tputs(vtcap.deletechar, 1, (void *)outc);

			mv(vtcap.cols, vtcap.rows);

			putchar(*(ed->margin + vtcap.cols - 1));

			mv(ED_SCRPOS, vtcap.rows);
		}
		else
		{
			line_update();
		}
	}
}

void word_forward(void)
{
	char *next;

	long int offset;

	next = scan_forward();
	
	offset =  (next - ed->curpos);

	ed->margin += ((ed->margin + offset) <= ED_MARGINMAX) ? offset : (ED_MARGINMAX - ed->margin);
	ed->curpos += offset;

	line_update();
}

char *scan_forward(void)
{
	char *i;

	int flag = 0x00;

	for(i = ed->curpos; i < ed->endpos; i++)
	{
		if(*i == ' ' || *i == '\t')
		{
			flag = 0xff;
		}
		else
		{
			if(flag)
				return(i);
		}
	}

	return(i);
}

void word_reverse(void)
{
	char *prev;

	long int offset;

	prev = scan_reverse();
	
	offset =  ed->curpos - prev;	

	if((ed->margin - offset) >= ed->buffer)
		ed->margin -= offset;
	else
		ed->margin = ed->buffer;

	ed->curpos = (ed->curpos - offset) < ed->buffer ? ed->buffer : (ed->curpos - offset);

	line_update();
}

char *scan_reverse(void)
{
	char *i;

	int flag = 0x00;

	for(i = ed->curpos; i > ed->buffer; i--)
	{
		if(*i == ' ' || *i == '\t')
		{
			flag = 0xff;
		}
		else
		{
			if(flag)
			{
				for(; i > ed->buffer; i--)
					if(*i == ' ' || *i == '\t')
						return(i + 1);
			}
		}
	}

	return(i);
}

int is_key_sequence(char *key, u_char c)
{
	static int index = 0;
	u_char x;

	if(key && c == *(key + index))
	{
		if(++index >= strlen(key))
		{
			index = 0;
			return(1);
		}

		x = getchar();

		if(is_key_sequence(key, x))
		{
			return(1);
		}
		else
		{
			ungetc(x, stdin);
			return(0);
		}
	}

	index = 0;
	return(0);
}

void clear_ed(ED *ced)
{
	BZERO(&ced->buffer, EDBUFSIZE + 1);
	ced->curpos = ced->endpos = ced->margin = (char *)&ced->buffer;
	ced->prompt = NULL;
	ced->offset = 0;
	ced->flags  = 0;
	ced->maxlen = EDBUFSIZE;
}

void tabcomplete()
{
	register int	i;
	char		*ls;
	char		*le;
	char		lookup[VPNICKSIZE + 1];
	char		complete[VPNICKSIZE + 3];    /* Leave room for ": " */
	int		flags	= 0x00;
#define	TCPL_BOB	0x00000001
#define	TCPL_AMBIGIOUS	0x00000002
	int		matches		= 0;
	int		matchchars	= 0;
	int		mchars		= 0;
	int		ichars		= 0;
	ULIST_ITEM	*u;

	/*
	 * Find the beginning of the current word
	 */

	for(ls = ed->curpos; *ls != ' ' && *ls != '\t'
	&& ls > ed->buffer; --ls);

	if(*ls == ' ' || *ls == '\t')
		++ls;

	/*
	 * When hit start of buffer, set flag
	 */

	if(ls == ed->buffer)
		flags |= TCPL_BOB;

	/*
	 * If the current word is empty or
	 * it's size exceeds the maximum size
	 * of a nickname, then do nothing.
	 */

	le = ed->curpos;

	if(le <= ls || (le - ls) > VPNICKSIZE)		/* NOP */
		return;

	/*
	 * Copy out the lookup word
	 */

	BZERO(lookup, sizeof(lookup));
	BCOPY(ls, lookup, (le - ls));

	/*
	 * Step through nickname list and find possible matches.
	 */

	BZERO(complete, sizeof(complete));

	for(i = 0; i < ulist_cnt; i++)
	{
		u = ulist_base + i;

		/*
		 * Calculate the longest possible match
		 */

		mchars = matching_chars(u->nick, lookup);

		/*
		 * Ignore multiple occurances of the same nickname
		 * and matches smaller than the lookup string.
		 */

		if(!strcmp(u->nick, complete) || mchars < strlen(lookup))
			continue;

		/*
		 * Update the completion string and
		 * the number of matched characters.
		 */

		if(matches > 0)
		{
			matchchars = matching_chars(complete, u->nick);
			strncpy(complete, u->nick, matchchars);
			complete[matchchars] = '\0';
			flags |= TCPL_AMBIGIOUS;
			++matches;
		}
		else
		{
			strcpy(complete, u->nick);
			matchchars = strlen(complete);
			matches = 1;
		}
	}

	/*
	 * When there was no match at all, then it is ambigous, too.
	 */

	if(matches == 0)
		flags |= TCPL_AMBIGIOUS;

	/*
	 * When there is no closer match, then there is nothing left to do.
	 */

	if(matchchars <= strlen(lookup))
	{
		if((flags & TCPL_AMBIGIOUS) && vp.matchbeep)
			putchar(BEL);

		return;
	}

	/*
	 * If there is an exact match, append ": " to the completion
	 * string if the current word is at the beginning of the buffer
	 * or " " if the current word is inline.
	 */

	if((flags & TCPL_AMBIGIOUS) == 0)
	{
		if((flags & TCPL_BOB) != 0)
		{
			strcat(complete, ": ");
			matchchars += 2;
		}
		else
		{
			strcat(complete, " ");
			matchchars += 1;
		}
	}

	/*
	 * See if there is enough space in the input
	 * buffer to insert the completion string.
	 */

	ichars = (matchchars - strlen(lookup));

	if(ED_FREE < ichars)
	{
		if(vp.matchbeep)
			putchar(BEL);

		return;
	}

	/*
	 * Move the rest of the input buffer to make space for completion.
	 */

	BCOPY(ed->curpos, (ed->curpos + ichars), ED_REMAIN);
	ed->endpos += ichars;

	/*
	 * Insert the complete string.
	 */

	BCOPY(complete, ls, matchchars);
	ed->curpos += ichars;

	/*
	 * Scroll if necessary.
	 */

	if(ed->curpos >= ED_ENDMARGIN)
	{
		if((ed->margin + ichars) <= ED_MARGINMAX)
		{
			ed->margin += ichars;
		}
	}

	/*
	 * Update screen
	 */

	line_update();

	/*
	 * Bell user if ambigious and matchbeep enabled.
	 */

	if((flags & TCPL_AMBIGIOUS) && vp.matchbeep)
		putchar(BEL);
}

int matching_chars(char *c1, char *c2)
{
	int ret = 0;

	for(; *c1++ == *c2++; ++ret);

	return(ret);
}


syntax highlighted by Code2HTML, v. 0.9.1