/* exp_tty.c - tty support routines */

#include <stdio.h>
#include "exp_conf.h"
#include "tcl.h"
#include "exp_global.h"
#include "exp_rename.h"
#include "exp_tty.h"
#include "exp_log.h"
#include "exp_command.h"
#include "exp_main.h"

static int is_raw = FALSE;
static int is_noecho = FALSE;

int exp_ioctled_devtty = FALSE;

exp_tty tty_current, tty_cooked;
exp_tty exp_tty_original;


int
exp_israw()
{
	return is_raw;
}

int
exp_isecho()
{
	return !is_noecho;
}

/* if set == 1, set it to raw, else unset it */
void
exp_tty_raw(set)
int set;
{
	if (set == 1) {
		is_raw = TRUE;
#if defined(HAVE_TERMIOS) || defined(HAVE_TERMIO) /* had POSIX too */
		tty_current.c_iflag = 0;
		tty_current.c_oflag = 0;
		tty_current.c_lflag &= ECHO; 	/* disable everything except echo */
		tty_current.c_cc[VMIN] = 1;
		tty_current.c_cc[VTIME] = 0;
	} else {
		tty_current.c_iflag = tty_cooked.c_iflag;
		tty_current.c_oflag = tty_cooked.c_oflag;
		tty_current.c_lflag = tty_cooked.c_lflag;
		tty_current.c_cc[VMIN] = tty_cooked.c_cc[VMIN];
		tty_current.c_cc[VTIME] = tty_cooked.c_cc[VTIME];
#else
#  if defined(HAVE_SGTTYB)
		tty_current.sg_flags |= RAW;
	} else {
		tty_current.sg_flags = tty_cooked.sg_flags;
#  endif
#endif
		is_raw = FALSE;
	}
}
	
void
exp_tty_echo(set)
int set;
{
	if (set == 1) {
		is_noecho = FALSE;
#if defined(HAVE_TERMIOS) || defined(HAVE_TERMIO) /* had POSIX too */
		tty_current.c_lflag |= ECHO;
	} else {
		tty_current.c_lflag &= ~ECHO;
#else
		tty_current.sg_flags |= ECHO;
	} else {
		tty_current.sg_flags &= ~ECHO;
#endif
		is_noecho = TRUE;
	}
}

/* returns 0 if nothing changed */
/* if something changed, the out parameters are changed as well */
int
exp_tty_raw_noecho(interp,tty_old,was_raw,was_echo)
Tcl_Interp *interp;
exp_tty *tty_old;
int *was_raw, *was_echo;
{
	if (exp_disconnected) return(0);
	if (is_raw && is_noecho) return(0);
	if (exp_dev_tty == -1) return(0);

	*tty_old = tty_current;		/* save old parameters */
	*was_raw = is_raw;
	*was_echo = !is_noecho;
	debuglog("tty_raw_noecho: was raw = %d  echo = %d\r\n",is_raw,!is_noecho);

	exp_tty_raw(1);
	exp_tty_echo(-1);

#ifdef POSIX
 	if (tcsetattr(exp_dev_tty, TCSADRAIN, &tty_current) == -1) {
#else
        if (ioctl(exp_dev_tty, TCSETSW, &tty_current) == -1) {
/***
	if (ioctl(exp_dev_tty, TIOCSETP, &tty_current) == -1) {
***/
#endif
		errorlog("ioctl(raw): %s\r\n",sys_errlist[errno]);
		exp_exit(interp,-1);
	}

	exp_ioctled_devtty = TRUE;
	return(1);
}

/* returns 0 if nothing changed */
/* if something changed, the out parameters are changed as well */
int
exp_tty_cooked_echo(interp,tty_old,was_raw,was_echo)
Tcl_Interp *interp;
exp_tty *tty_old;
int *was_raw, *was_echo;
{
	if (exp_disconnected) return(0);
	if (!is_raw && !is_noecho) return(0);
	if (exp_dev_tty == -1) return(0);

	*tty_old = tty_current;		/* save old parameters */
	*was_raw = is_raw;
	*was_echo = !is_noecho;
	debuglog("tty_cooked_echo: was raw = %d  echo = %d\r\n",is_raw,!is_noecho);

	exp_tty_raw(-1);
	exp_tty_echo(1);

#ifdef POSIX
 	if (tcsetattr(exp_dev_tty, TCSADRAIN, &tty_current) == -1) {
#else
        if (ioctl(exp_dev_tty, TCSETSW, &tty_current) == -1) {
/***
	if (ioctl(exp_dev_tty, TIOCSETP, &tty_current) == -1) {
***/
#endif
		errorlog("ioctl(noraw): %s\r\n",sys_errlist[errno]);
		exp_exit(interp,-1);
	}
	exp_ioctled_devtty = TRUE;

	return(1);
}

void
exp_tty_set(interp,tty,raw,echo)
Tcl_Interp *interp;
exp_tty *tty;
int raw;
int echo;
{
#ifdef POSIX
 	if (tcsetattr(exp_dev_tty, TCSADRAIN, tty) == -1) {
#else
        if (ioctl(exp_dev_tty, TCSETSW, tty) == -1) {
/***
	if (ioctl(exp_dev_tty, TIOCSETP, tty) == -1) {
***/
#endif
		errorlog("ioctl(set): %s\r\n",sys_errlist[errno]);
		exp_exit(interp,-1);
	}
	is_raw = raw;
	is_noecho = !echo;
	tty_current = *tty;
	debuglog("tty_set: raw = %d, echo = %d\r\n",is_raw,!is_noecho);
	exp_ioctled_devtty = TRUE;
}	

void
exp_init_tty()
{
	extern exp_tty exp_tty_original;

	/* save original user tty-setting in 'cooked', just in case user */
	/* asks for it without earlier telling us what cooked means to them */
	tty_cooked = exp_tty_original;

	/* save our current idea of the terminal settings */
	tty_current = exp_tty_original;

	setbuf(stdout,(char *)0);	/* unbuffer stdout */
}

/* take strings with newlines and insert carriage-returns.  This allows user */
/* to write send_user strings without always putting in \r. */
/* If len == 0, use strlen to compute it */
/* NB: if terminal is not in raw mode, nothing is done. */
char *
exp_cook(s,len)
char *s;
int *len;	/* current and new length of s */
{
	static int destlen = 0;
	static char *dest = 0;
	char *d;		/* ptr into dest */
	unsigned int need;

	if (s == 0) return("<null>");

	if (!is_raw) return(s);

	/* worst case is every character takes 2 to represent */
	need = 1 + 2*(len?*len:strlen(s));
	if (need > destlen) {
		if (dest) free(dest);
		dest = ckalloc(need);
		destlen = need;
	}

	for (d = dest;*s;s++) {
		if (*s == '\n') {
			*d++ = '\r';
			*d++ = '\n';
		} else {
			*d++ = *s;
		}
	}
	*d = '\0';
	if (len) *len = d-dest;
	return(dest);
}
