/* 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
 */
#include <stdio.h>
#ifdef __TURBOC__
#include <conio.h>
#endif
#include <ctype.h>
#include "global.h"
#include "config.h"
#include "mbuf.h"
#include "session.h"
#include "tty.h"
#include "socket.h"

extern FILE *Rawterm;
extern unsigned char SCREENwidth, SCREENlength;
#ifdef TNOS_68K
extern short UseCurses;
#endif

#define	OFF	0
#define	ON	1

#define	LINESIZE	256

#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
#define DEL	0x7f

extern int Numrows,Numcols;

#ifdef ALLSERV

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(80, 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
#ifdef UNIX
	normvideo();
#endif
}


static void
tochatline(sp, gothere)
struct session *sp;
int gothere;
{
#ifndef TNOS_68K
	sp->tsavex = wherex();
	sp->tsavey = wherey();
#ifdef UNIX
	window(1,Numrows-1,Numcols,Numrows);
#else
	window(1,SCREENlength - 1,SCREENwidth,SCREENlength);
#endif
	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();
#ifdef UNIX
/*	window(1,1 + sp->screen->statline,Numcols,Numrows-2);	*/
	window(1,1,Numcols,Numrows-2);
#else
	window(1,1 + sp->screen->statline,SCREENwidth,SCREENlength - 2);
#endif
	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 = c;
		bp->cnt = 1;
		if(sp->ttystate.echo){
			if(sp->split){
				tochatline (sp, 1);
				putch(c);
				fromchatline (sp);
			} else {
#if defined(TNOS_68K) || defined(UNIX)
				putch(c);
#else
				putc(c,Rawterm);
#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 = bp->data + bp->cnt;
		/* Perform cooked-mode line editing */
#ifdef notdef
        switch(c & 0x7f){
#endif
        /* 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 = c;
			if(sp->ttystate.echo){
				if(sp->split){
#ifdef TNOS_68K
					if (UseCurses)	{
#endif
						highvideo();
						rp = bp->data;
						while(rp < cp)
							putch(*rp++);
#ifdef UNIX
						normvideo();
#else
						lowvideo();
#endif
						clreol();
#ifdef TNOS_68K
					}
#endif
					cputs(Eol);
					clreol();
					clrchatline(sp);
				} else {
#if defined(TNOS_68K) || defined(UNIX)
					cputs(Eol);
#else
					fputs(Eol,Rawterm);
#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 = bp->data;
					while (rp < cp)
						putch(*rp++) ;
					fromchatline (sp);
				} else {
#ifdef UNIX
					cputs("^R");
					cputs(Eol);
					rp = bp->data;
					while (rp < cp)
						putch(*rp++);
#else
#ifdef TNOS_68K
				cnt = bp->cnt;
				while(cnt != 0){
					cnt--;
					if(sp->ttystate.echo)	{
						backspace ();
						space ();
						backspace ();
					}
				}
#else
					fprintf(Rawterm,"^R%s",Eol) ;
#endif
					rp = bp->data;
					while (rp < cp)
#ifdef TNOS_68K
						putch(*rp++) ;
#else
						putc(*rp++,Rawterm) ;
#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 = cnt;
			}

                        if(bp->cnt < (Lastsize-1)){
                                memcpy(bp->data+bp->cnt, &Lastline[bp->cnt], (size_t)(Lastsize-1) - bp->cnt);
                                bp->cnt = 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 (bp->data);
					fromchatline (sp);
				} else 
#if defined(TNOS_68K) || defined(UNIX)
					cputs (bp->data);
#else
					fputs(bp->data, Rawterm);	/* repaint line */
#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(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 &&
#ifndef	AMIGA
			 c != CTLZ &&
#endif
			 bp->cnt < LINESIZE-1){
				if(sp->split) {
					tochatline (sp, 1);
					putch(c);
					fromchatline (sp);
				} else {
#if defined(TNOS_68K) || defined(UNIX)
					putch(c);
#else
					putc(c,Rawterm);
#endif
				}

			} else if(bp->cnt >= LINESIZE-1){
#if defined(TNOS_68K)
				putch('\007');	/* Beep */
#else
#ifdef UNIX
				write(1, "\007", 1);
#else
				putc('\007',Rawterm);	/* Beep */
#endif
#endif
				bp->cnt--;
			}
			break;
		}
		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 &&
#ifndef	AMIGA
			 c != CTLZ &&
#endif
			 bp->cnt < LINESIZE-1){
#if defined(TNOS_68K) || define(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*/
