/* TTY input line editing
 * Copyright 1991 Phil Karn, KA9Q
 * split screen by G. J. van der Grinten, PA0GRI
 * fixed split screen ^B and ^W functions - KO4KS
 */
#ifdef MSDOS
#include <conio.h>
#endif
#include "global.h"
#include "ctype.h"
#include "mbuf.h"
#include "session.h"
#include "tty.h"

#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: ttydriv.c,v 1.18 2000/05/09 16:48:29 brian Exp $";
#endif

#ifdef TNOS_68K
extern short UseCurses;
#endif

#define	OFF	0
#define	ON	1

#ifndef CTLR
#define CTLR    18		/* reprint current line */
#define CTLU    21		/* delete current line in total */
#define	CTLW	23		/* erase last word including preceding space */
#define	CTLZ	26		/* EOF char in dos */
#define	CTLB	02		/* use as F3 in dos but no editing */
#endif

extern unsigned char SCREENwidth, SCREENlength;

#ifdef ALLSERV

static void backspace (void);
static void space (void);
static void switchattr (void);
static void tochatline (struct session * sp, int gothere);
static void fromchatline (struct session * sp);
void clrchatline (struct session * sp);

static int Lastsize = 1;
static char Lastline[LINESIZE + 1] = "\n";


static void
backspace ()
{
int x;

	x = wherex ();
#ifdef TNOS_68K
	if (!x && UseCurses)
#else
	if (x == 1)
#endif
		gotoxy (SCREENwidth, wherey () - 1);
	else {
#ifdef TNOS_68K
		if (UseCurses)
			gotoxy (x - 1, wherey ());
		else
#endif
			cputs ("\b");
	}
}


static void
space ()
{
	cputs (" ");
}


static void
switchattr ()
{
#if !defined(TNOS_68K) && !defined(UNIX)
struct text_info tr;
unsigned char attr;

	gettextinfo (&tr);
	attr = ((tr.attribute & 0x0f) << 4) + ((tr.attribute & 0x70) >> 4);
	textattr (attr);
#endif
#if defined(UNIX) && !defined(PATCURSES)

	normvideo ();
#endif
}


static void
tochatline (sp, gothere)
struct session *sp;
int gothere;
{
#ifndef TNOS_68K
	sp->tsavex = wherex ();
	sp->tsavey = wherey ();
	window (1, SCREENlength - 1, SCREENwidth, SCREENlength);
	switchattr ();
	if (gothere)
		gotoxy (sp->bsavex, sp->bsavey);
#else
	if (UseCurses)
		sp->screen->win = sp->screen->splitwin;
#endif
}


static void
fromchatline (sp)
struct session *sp;
{
#ifdef TNOS_68K
	if (UseCurses)
#endif
	{
		cputs ("_");
		backspace ();
#ifdef TNOS_68K
		sp->screen->win = sp->screen->textwin;
#else
	}
	sp->bsavex = wherex ();
	sp->bsavey = wherey ();
	window (1, 1 + sp->screen->statline, SCREENwidth, SCREENlength - 2);
	switchattr ();
	gotoxy (sp->tsavex, sp->tsavey);
#endif
}


void
clrchatline (sp)
struct session *sp;
{
	tochatline (sp, 0);
#ifdef TNOS_68K
	if (UseCurses) {
		werase (sp->screen->win);
		gotoxy (0, 0);
	}
#else
	clrscr ();
#endif
	fromchatline (sp);
}


/* Accept characters from the incoming tty buffer and process them
 * (if in cooked mode) or just pass them directly (if in raw mode).
 *
 * Echoing (if enabled) is direct to the raw terminal. This requires
 * recording (if enabled) of locally typed info to be done by the session
 * itself so that edited output instead of raw input is recorded.
 * Control-W added by g1emm again.... for word delete.
 * Control-B/Function key 3 added by g1emm for line repeat.
 */

struct mbuf *
ttydriv (sp, c)
struct session *sp;
int c;
{
struct mbuf *bp;
char *cp, *rp;
int cnt;

	switch (sp->ttystate.edit) {
		case OFF:
			bp = ambufw (1);
			*bp->data = uchar (c);
			bp->cnt = 1;
			if (sp->ttystate.echo) {
				if (sp->split) {
					tochatline (sp, 1);
#if defined(TNOS_68K) || defined(UNIX)
					putch (c);
#else
					cprintf ("%c", c);
#endif
					fromchatline (sp);
				} else {
#if defined(TNOS_68K) || defined(UNIX)
					putch (c);
#else
					cprintf ("%c", c);
#endif
				}
			}
#ifdef TNOS_68K
			rflush ();
#endif
			return bp;
		case ON:
			if (sp->ttystate.line == NULLBUF)
				sp->ttystate.line = ambufw (LINESIZE);

			bp = sp->ttystate.line;
			cp = (char *) (bp->data + bp->cnt);
			/* Perform cooked-mode line editing */
			/* Allow for international character sets - WG7J */
			switch (c) {
				case '\r':	/* CR and LF both terminate the line */
#ifdef TNOS_68K
				case '\l':	/* CR and LF both terminate the line */
#else
				case '\n':
#endif
					if (sp->ttystate.crnl)
						*cp = '\n';
					else
						*cp = (char) c;
					if (sp->ttystate.echo) {
						if (sp->split) {
#ifdef TNOS_68K
							if (UseCurses) {
#endif
#ifdef PATCURSES

								cputs("\033[1m"); /* bold on */

#else

								highvideo ();
#endif

								rp = (char *) bp->data;
								while (rp < cp)
#if defined(TNOS_68K) || defined(UNIX)
									putch (*rp++);
#else
									cprintf ("%c", *rp++);
#endif
#ifdef UNIX
#ifdef PATCURSES

								cputs("\033[21m"); /* bold off */

#else						

								normvideo ();
#endif /* PATCURSES */

#else
								lowvideo ();
#endif
								clreol ();
#ifdef TNOS_68K
							}
#endif
							cputs (Eol);
							clreol ();
							clrchatline (sp);
						} else {
#if defined(TNOS_68K) || defined(UNIX)
							cputs (Eol);
#else
							cputs (Eol);
#endif
						}
					}
					bp->cnt += 1;
					sp->ttystate.line = NULLBUF;
					Lastsize = bp->cnt;
					memcpy (Lastline, bp->data, (size_t) Lastsize);
					return bp;
				case DEL:
				case '\b':	/* Character delete */
					if (bp->cnt != 0) {
						bp->cnt--;
						if (sp->ttystate.echo) {
							if (sp->split) {
								tochatline (sp, 1);
								space ();
								backspace ();
								backspace ();
								fromchatline (sp);
							} else {
								backspace ();
								space ();
								backspace ();
							}
						}
					}
					break;
				case CTLR:	/* print line buffer */
					if (sp->ttystate.echo) {
						if (sp->split) {
							tochatline (sp, 1);
#ifdef TNOS_68K
							if (UseCurses)
								werase (sp->screen->win);
#else
							clrscr ();
#endif
							rp = (char *) bp->data;
							while (rp < cp)
#if defined(TNOS_68K) || defined(UNIX)
								putch (*rp++);
#else
								cprintf ("%c", *rp++);
#endif
							fromchatline (sp);
						} else {
#ifdef UNIX
							cputs ("^R");
							cputs (Eol);
							rp = (char *) bp->data;
							while (rp < cp)
#if defined(TNOS_68K) || defined(UNIX)
								putch (*rp++);
#else
								cprintf ("%c", *rp++);
#endif
#else
#ifdef TNOS_68K
							cnt = bp->cnt;
							while (cnt != 0) {
								cnt--;
								if (sp->ttystate.echo) {
									backspace ();
									space ();
									backspace ();
								}
							}
#else
#if defined(UNIX)
							fprintf (Rawterm, "^R%s", Eol);
#else
							cprintf ("^R%s", Eol);
#endif
#endif
							rp = (char *) bp->data;
							while (rp < cp)
#if defined(TNOS_68K) || defined(UNIX)
								putch (*rp++);
#else
								cprintf ("%c", *rp++);
#endif
#endif
						}
					}
					break;
				case CTLU:	/* Line kill */
					if (sp->split) {
						clrchatline (sp);
						bp->cnt = 0;
					} else {
						while (bp->cnt != 0) {
							bp->cnt--;
							if (sp->ttystate.echo) {
								backspace ();
								space ();
								backspace ();
							}
						}
					}
					break;
				case CTLB:	/* Use last line to finish current */
					if (!sp->split) {
						cnt = bp->cnt;	/* save count so far */

						while (bp->cnt != 0) {
							bp->cnt--;
							if (sp->ttystate.echo) {
								backspace ();
								space ();
								backspace ();
							}
						}
						bp->cnt = (int16) cnt;
					}
					if (bp->cnt < (Lastsize - 1)) {
						memcpy (bp->data + bp->cnt, &Lastline[bp->cnt], (size_t) (Lastsize - 1) - bp->cnt);
						bp->cnt = (int16) Lastsize - 1;
					}
					*(bp->data + bp->cnt) = '\0';	/* make it a string */
					if (sp->ttystate.echo)	{
						if (sp->split) {
							tochatline (sp, 1);	/* was 0	*/
#ifdef TNOS_68K
							if (UseCurses)
								werase (sp->screen->win);
#else
							clrscr ();
#endif
							cputs ((char *) bp->data);
							fromchatline (sp);
						} else
#if defined(TNOS_68K) || defined(UNIX)
							cputs ((char *) bp->data);
#else
							cputs ((char *) bp->data);
#endif
					}
					break;
				case CTLW:	/* erase word */
					if (sp->split)
						if (sp->ttystate.echo) {
							tochatline (sp, 1);
							space ();
							backspace ();
						}
					cnt = 0;	/* we haven't seen a printable char yet */
					while (bp->cnt != 0) {
						*(bp->data + bp->cnt--) = '\n';
						if (sp->ttystate.echo)	{
							if (sp->split) {
								backspace ();
								space ();
								backspace ();
							} else {
								backspace ();
								space ();
								backspace ();
							}
						}
						if (isspace ((int) *(bp->data + bp->cnt))) {
							if (cnt)
								break;
						} else {
							cnt = 1;
						}
					}
					if (sp->split && sp->ttystate.echo)
						fromchatline (sp);
					break;
				case UPARROW:	/* Recall previous command - WG7J */
				case DNARROW:	/* Recall next command - WG7J */
					if (Histry) {
						/* Blank out what's already there */
						while (bp->cnt != 0 && sp->ttystate.echo) {
							bp->cnt--;
							cputs ("\b \b");
						}
						if (c == DNARROW)
							/* Adjust history */
							Histry = Histry->next;
						/* Recall last command */
						strcpy ((char *) bp->data, Histry->cmd);
						bp->cnt = (int16) strlen (Histry->cmd);
						if (c == UPARROW)
							/* Adjust history */
							Histry = Histry->prev;
						/* repaint line */
						if (sp->ttystate.echo)
							cputs ((char *) bp->data);
					}
					break;
				default:	/* Ordinary character */
					*cp = (char) c;
					bp->cnt++;

					/* ^Z apparently hangs the terminal emulators under
							 * DoubleDos and Desqview. I REALLY HATE having to patch
							 * around other people's bugs like this!!!
							 */
					if (sp->ttystate.echo &&
					    c != CTLZ &&
					    bp->cnt < LINESIZE - 1) {
						if (sp->split) {
							tochatline (sp, 1);
#if defined(TNOS_68K) || defined(UNIX)
							putch (c);
#else
							cprintf ("%c", c);
#endif
							fromchatline (sp);
						} else {
#if defined(TNOS_68K) || defined(UNIX)
							putch (c);
#else
							cprintf ("%c", c);
#endif
						}

					} else if (bp->cnt >= LINESIZE - 1) {
#if defined(TNOS_68K)
						putch ('\007');	/* Beep */
#else
#ifdef UNIX
						write (1, "\007", 1);
#else
						cputs ("\007");	/* Beep */
#endif
#endif
						bp->cnt--;
					}
					break;
			}
			break;
		default:
			break;
	}
#ifdef TNOS_68K
	rflush ();
#endif
	return NULLBUF;
}

#else


/* Accept characters from the incoming tty buffer and process them
 * (if in cooked mode) or just pass them directly (if in raw mode).
 *
 * Echoing (if enabled) is direct to the raw terminal. This requires
 * recording (if enabled) of locally typed info to be done by the session
 * itself so that edited output instead of raw input is recorded.
 */
struct mbuf *
ttydriv (sp, c)
struct session *sp;
int c;
{
struct mbuf *bp;
char *cp, *rp;

	switch (sp->ttystate.edit) {
		case OFF:
			bp = ambufw (1);
			*bp->data = c;
			bp->cnt = 1;
			if (sp->ttystate.echo)
#if defined(TNOS_68K) || defined(UNIX)
				putch (c);
#else
				putc (c, Rawterm);
#endif
			return bp;
		case ON:
			if (sp->ttystate.line == NULLBUF)
				sp->ttystate.line = ambufw (LINESIZE);

			bp = sp->ttystate.line;
			cp = bp->data + bp->cnt;
			/* Allow for international character sets - WG7J */
			/* Perform cooked-mode line editing */
			/*        switch(c & 0x7f){ */
			switch (c) {
				case '\r':	/* CR and LF both terminate the line */
				case '\n':
					if (sp->ttystate.crnl)
						*cp = '\n';
					else
						*cp = c;
					if (sp->ttystate.echo)
#if defined(TNOS_68K) || defined(UNIX)
						cputs (Eol);
#else
						fputs (Eol, Rawterm);
#endif

					bp->cnt += 1;
					sp->ttystate.line = NULLBUF;
					return bp;
				case DEL:
				case '\b':	/* Character delete */
					if (bp->cnt != 0) {
						bp->cnt--;
						if (sp->ttystate.echo)
#if defined(TNOS_68K) || defined(UNIX)
							cputs ("\b \b");
#else
							fputs ("\b \b", Rawterm);
#endif
					}
					break;
				case CTLR:	/* print line buffer */
					if (sp->ttystate.echo) {
#if defined(TNOS_68K) || defined(UNIX)
						cputs ("^R");
						cputs (Eol);
						rp = bp->data;
						while (rp < cp)
							putch (*rp++);
#else
						fprintf (Rawterm, "^R%s", Eol);
						rp = bp->data;
						while (rp < cp)
							putc (*rp++, Rawterm);
#endif
					}
					break;
				case CTLU:	/* Line kill */
					while (bp->cnt != 0) {
						bp->cnt--;
						if (sp->ttystate.echo) {
#if defined(TNOS_68K) || defined(UNIX)
							cputs ("\b \b");
#else
							fputs ("\b \b", Rawterm);
#endif
						}
					}
					break;
				case UPARROW:	/* Recall previous command - WG7J */
				case DNARROW:	/* Recall next command - WG7J */
					if (Histry) {
						/* Blank out what's already there */
						while (bp->cnt != 0 && sp->ttystate.echo) {
							bp->cnt--;
							cputs ("\b \b");
						}
						if (c == DNARROW)
							/* Adjust history */
							Histry = Histry->next;
						/* Recall last command */
						strcpy (bp->data, Histry->cmd);
						bp->cnt = strlen (Histry->cmd);
						if (c == UPARROW)
							/* Adjust history */
							Histry = Histry->prev;
						/* repaint line */
						if (sp->ttystate.echo)
							cputs (bp->data);
					}
					break;
				default:	/* Ordinary character */
					*cp = c;
					bp->cnt++;

					/* ^Z apparently hangs the terminal emulators under
					 * DoubleDos and Desqview. I REALLY HATE having to patch
					 * around other people's bugs like this!!!
					 */
					if (sp->ttystate.echo &&
					    c != CTLZ &&
					    bp->cnt < LINESIZE - 1) {
#if defined(TNOS_68K) || defined(UNIX)
						putch (c);
#else
						putc (c, Rawterm);
#endif

					} else if (bp->cnt >= LINESIZE - 1) {
#if defined(TNOS_68K) || defined(UNIX)
						write (1, "\007", 1);
#else
						putc ('\007', Rawterm);	/* Beep */
#endif
						bp->cnt--;
					}
					break;
			}
			break;
	}
	return NULLBUF;
}

#endif /*ALLSERV*/
