V10/lbin/mailx/tty.c

Compare this file to the similar file:
Show the results in this format:

#ident "@(#)tty.c	1.4 'attmail mail(1) command'"
#ident	"@(#)mailx:tty.c	1.9.2.1"
/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"@(#)mailx:tty.c	1.9"
/*
 * mailx -- a modified version of a University of California at Berkeley
 *	mail program
 *
 * Generally useful tty stuff.
 */

#include "rcv.h"

#ifdef	USG_TTY
#include <sys/termio.h>

static void	Echo();
static int	countcol();
static void	outstr();
static char	*readtty();
static void	resetty();
static void	rubout();
static int	savetty();
static int	setty();

static	int	c_erase;		/* Current erase char */
static	int	c_kill;			/* Current kill char */
static	int	c_intr;			/* interrupt char */
static	int	c_quit;			/* quit character */
static	int	c_word;			/* Current word erase char */
static	int	Col;			/* current output column */
static	int	Pcol;			/* end column of prompt string */
static	int	Out;			/* file descriptor of stdout */
static	struct termio savtty, ttybuf;
static	char canonb[LINESIZE];		/* canonical buffer for input */
					/* processing */
static	int	erasing;		/* we are erasing characters */

#ifdef SIGCONT
# ifdef preSVr4
typedef int	sig_atomic_t;		/* syntax, please -- adb */
# endif
static	sig_atomic_t	hadcont;		/* Saw continue signal */
/*ARGSUSED*/
static void
ttycont(s)
{
	hadcont++;
}

/*ARGSUSED*/
static void
ttystop(s)
{
	resetty();
	kill(mypid, SIGSTOP);
}
#endif

/*
 * Read all relevant header fields.
 */

grabh(hp, gflags, subjtop)
register struct header *hp;
{
#ifdef SIGCONT
	void (*savecont)();
	void (*savestop)();
#endif
	if (savetty())
		return -1;
#ifdef SIGCONT
# ifdef preSVr4
	savecont = sigset(SIGCONT, ttycont);
	savestop = sigset(SIGTSTP, ttycont);
# else
	{
	struct sigaction nsig, osig;
	nsig.sa_handler = ttycont;
	sigemptyset(&nsig.sa_mask);
	nsig.sa_flags = 0;
	(void) sigaction(SIGCONT, &nsig, &osig);
	savecont = osig.sa_handler;
	nsig.sa_handler = ttystop;
	sigemptyset(&nsig.sa_mask);
	nsig.sa_flags = 0;
	(void) sigaction(SIGTSTP, &nsig, &osig);
	savestop = osig.sa_handler;
	}
# endif
#endif
	if (gflags & GSUBJECT && subjtop) {
		hp->h_subject = readtty("Subject: ", hp->h_subject);
		if (hp->h_subject != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GTO) {
		hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to));
		if (hp->h_to != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GCC) {
		hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc));
		if (hp->h_cc != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GBCC) {
		hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc));
		if (hp->h_bcc != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GSUBJECT && !subjtop) {
		hp->h_subject = readtty("Subject: ", hp->h_subject);
		if (hp->h_subject != NOSTR)
			hp->h_seq++;
	}
#ifdef SIGCONT
# ifdef preSVr4
	(void) sigset(SIGCONT, savecont);
	(void) sigset(SIGTSTP, savestop);
# else
	{
	struct sigaction nsig;
	nsig.sa_handler = savecont;
	sigemptyset(&nsig.sa_mask);
	nsig.sa_flags = SA_RESTART;
	(void) sigaction(SIGCONT, &nsig, (struct sigaction*)0);
	nsig.sa_handler = savestop;
	(void) sigaction(SIGTSTP, &nsig, (struct sigaction*)0);
	}
# endif
#endif
	return(0);
}

/*
 * Read up a header from standard input.
 * The source string has the preliminary contents to
 * be read.
 *
 */

static char *
readtty(pr, src)
	char pr[], src[];
{
	int c;
	register char *cp, *cp2;

	erasing = 0;
	fflush(stdout);
	Col = 0;
	outstr(pr);
	Pcol = Col;
	if (src != NOSTR && strlen(src) > LINESIZE - 2) {
		printf("too long to edit\n");
		return(src);
	}
	if (setty())
		return(src);
	cp2 = src==NOSTR ? "" : src;
	for (cp=canonb; *cp2; cp++, cp2++)
		*cp = *cp2;
	*cp = '\0';
	outstr(canonb);

	for (;;) {
		fflush(stdout);
#ifdef SIGCONT
		hadcont = 0;
#endif
		c = getc(stdin);

		if (c==c_erase) {
			if (cp > canonb)
				if (cp[-1]=='\\' && !erasing) {
					*cp++ = (char)c;
					Echo(c);
				} else {
					rubout(--cp);
				}
		} else if (c==c_kill) {
			if (cp > canonb && cp[-1]=='\\') {
				*cp++ = (char)c;
				Echo(c);
			} else while (cp > canonb) {
				rubout(--cp);
			}
		} else if (c==c_word) {
			if (cp > canonb)
				if (cp[-1]=='\\' && !erasing) {
					*cp++ = (char)c;
					Echo(c);
				} else {
					while (--cp >= canonb)
						if (!isspace(*cp))
							break;
						else
							rubout(cp);
					while (cp >= canonb)
						if (!isspace(*cp))
							rubout(cp--);
						else
							break;
					if (cp < canonb)
						cp = canonb;
					else if (*cp)
						cp++;
				}
		} else if (c==EOF || ferror(stdin) || c==c_intr || c==c_quit) {
#ifdef SIGCONT
			if (hadcont) {
				(void) setty();
				outstr("(continue)\n");
				Col = 0;
				outstr(pr);
				*cp = '\0';
				outstr(canonb);
				clearerr(stdin);
				continue;
			}
#endif
			resetty();
			savedead(c==c_quit? SIGQUIT: SIGINT);
		} else switch (c) {
			case '\n':
			case '\r':
				resetty();
				putchar('\n');
				fflush(stdout);
				if (canonb[0]=='\0')
					return(NOSTR);
				return(savestr(canonb));
			default:
				erasing = 0;
				*cp++ = (char)c;
				*cp = '\0';
				Echo(c);
		}
	}
}

static int
savetty()
{
	Out = fileno(stdout);
	if (ioctl(Out, TCGETA, &savtty) < 0)
	{	perror("ioctl");
		return(-1);
	}
	c_erase = savtty.c_cc[VERASE];
	c_kill = savtty.c_cc[VKILL];
	c_intr = savtty.c_cc[VINTR];
	c_quit = savtty.c_cc[VQUIT];
	c_word = 'W' & 037;	/* erase word character */
	ttybuf = savtty;
#ifdef	u370
	ttybuf.c_cflag &= ~PARENB;	/* disable parity */
	ttybuf.c_cflag |= CS8;		/* character size = 8 */
#endif	/* u370 */
	ttybuf.c_cc[VTIME] = 0;
	ttybuf.c_cc[VMIN] = 1;
	ttybuf.c_iflag &= ~(BRKINT);
	ttybuf.c_lflag &= ~(ICANON|ISIG|ECHO);
	return 0;
}

static int
setty()
{
	if (ioctl(Out, TCSETAW, &ttybuf) < 0) {
		perror("ioctl");
		return(-1);
	}
	return(0);
}

static void
resetty()
{
	if (ioctl(Out, TCSETAW, &savtty) < 0)
		perror("ioctl");
}

static void
outstr(s)
	register char *s;
{
	while (*s)
		Echo(*s++);
}

static void
rubout(cp)
	register char *cp;
{
	register int oldcol;
	register int c = *cp;

	erasing = 1;
	*cp = '\0';
	switch (c) {
	case '\t':
		oldcol = countcol();
		do
			putchar('\b');
		while (--Col > oldcol);
		break;
	case '\b':
		if (isprint(cp[-1]))
			putchar(*(cp-1));
		else
			putchar(' ');
		Col++;
		break;
	default:
		if (isprint(c)) {
			fputs("\b \b", stdout);
			Col--;
		}
	}
}

static int
countcol()
{
	register int col;
	register char *s;

	for (col=Pcol, s=canonb; *s; s++)
		switch (*s) {
		case '\t':
			while (++col % 8)
				;
			break;
		case '\b':
			col--;
			break;
		default:
			if (isprint(*s))
				col++;
		}
	return(col);
}

static void
Echo(cc)
{
	char c = (char)cc;

	switch (c) {
	case '\t':
		do
			putchar(' ');
		while (++Col % 8);
		break;
	case '\b':
		if (Col > 0) {
			putchar('\b');
			Col--;
		}
		break;
	case '\r':
	case '\n':
		Col = 0;
		fputs("\r\n", stdout);
		break;
	default:
		if (isprint(c)) {
			Col++;
			putchar(c);
		}
	}
}

getbaud()					/* for compatibility with V9 -- adb */
{
	struct termio tbuf;
	int baud;

	outtty = isatty(1);
	if (ioctl(1, TCGETA, &tbuf)==0)
		baud = tbuf.c_cflag & CBAUD;
	else
		baud = B9600;
	if( baud < B1200 ) return(5);
	else if (baud == B1200) return(10);
	else return(20);
}

/* end USG_TTY */
#else
#include <sys/ttyio.h>
static	int	c_erase;		/* Current erase char */
static	int	c_kill;			/* Current kill char */
static	int	hadcont;		/* Saw continue signal */
static	jmp_buf	rewrite;		/* Place to go when continued */
#ifndef TIOCSTI
static	int	ttyset;			/* We must now do erase/kill */
#endif
static char	*readtty();							/* adb */

/*
 * Read all relevant header fields.
 */

grabh(hp, gflags, subjtop)
	struct header *hp;
{
	struct sgttyb ttybuf;
	void ttycont(), signull();
	void (*savecont)();
	register int s;
	int errs;
#ifndef TIOCSTI
	void (*savesigs[2])();
#endif

#ifdef SIGCONT
	savecont = sigset(SIGCONT, signull);
#endif
	errs = 0;
#ifndef TIOCSTI
	ttyset = 0;
#endif
	if (gtty(fileno(stdin), &ttybuf) < 0) {
		perror("gtty");
		return(-1);
	}
	c_erase = ttybuf.sg_erase;
	c_kill = ttybuf.sg_kill;
#ifndef TIOCSTI
	ttybuf.sg_erase = 0;
	ttybuf.sg_kill = 0;
	for (s = SIGINT; s <= SIGQUIT; s++)
		if ((savesigs[s-SIGINT] = sigset(s, SIG_IGN)) == (void (*)())SIG_DFL)	/* adb */
			sigset(s, SIG_DFL);
#endif
	if (gflags & GSUBJECT && subjtop) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_subject != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_subject = readtty("Subject: ", hp->h_subject);
		if (hp->h_subject != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GTO) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_to != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_to = addto(NOSTR, readtty("To: ", hp->h_to));
		if (hp->h_to != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GCC) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_cc != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_cc = addto(NOSTR, readtty("Cc: ", hp->h_cc));
		if (hp->h_cc != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GBCC) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_bcc != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_bcc = addto(NOSTR, readtty("Bcc: ", hp->h_bcc));
		if (hp->h_bcc != NOSTR)
			hp->h_seq++;
	}
	if (gflags & GSUBJECT && !subjtop) {
#ifndef TIOCSTI
		if (!ttyset && hp->h_subject != NOSTR)
			ttyset++, stty(fileno(stdin), &ttybuf);
#endif
		hp->h_subject = readtty("Subject: ", hp->h_subject);
		if (hp->h_subject != NOSTR)
			hp->h_seq++;
	}
#ifdef SIGCONT
	sigset(SIGCONT, savecont);
#endif
#ifndef TIOCSTI
	ttybuf.sg_erase = c_erase;
	ttybuf.sg_kill = c_kill;
	if (ttyset)
		stty(fileno(stdin), &ttybuf);
	for (s = SIGINT; s <= SIGQUIT; s++)
		sigset(s, savesigs[s-SIGINT]);
#endif
	return(errs);
}

/*
 * Read up a header from standard input.
 * The source string has the preliminary contents to
 * be read.
 *
 */

static char *
readtty(pr, src)
	char pr[], src[];
{
	char canonb[LINESIZE];
	int c, ch;								/* adb */
	void signull();								/* adb */
	register char *cp, *cp2;

	fputs(pr, stdout);
	fflush(stdout);
	if (src != NOSTR && strlen(src) > LINESIZE - 2) {
		printf("too long to edit\n");
		return(src);
	}
#ifndef TIOCSTI
	if (src != NOSTR)
		cp = copy(src, canonb);
	else
		cp = copy("", canonb);
	fputs(canonb, stdout);
	fflush(stdout);
#else
	cp = src == NOSTR ? "" : src;
	while (c = *cp++) {
		if (c == c_erase || c == c_kill) {
			ch = '\\';
			ioctl(0, TIOCSTI, &ch);
		}
		ioctl(0, TIOCSTI, &c);
	}
	cp = canonb;
	*cp = 0;
#endif
	cp2 = cp;
	while (cp2 < canonb + LINESIZE)
		*cp2++ = 0;
	cp2 = cp;
	if (setjmp(rewrite))
		goto redo;
#ifdef SIGCONT
	sigset(SIGCONT, ttycont);
#endif
	while (cp2 < canonb + LINESIZE) {
		c = getc(stdin);
		if (c == EOF || c == '\n')
			break;
		*cp2++ = c;
	}
	*cp2 = 0;
#ifdef SIGCONT
	sigset(SIGCONT, signull);
#endif
	if (c == EOF && ferror(stdin) && hadcont) {
redo:
		hadcont = 0;
		cp = strlen(canonb) > 0 ? canonb : NOSTR;
		clearerr(stdin);
		return(readtty(pr, cp));
	}
#ifndef TIOCSTI
	if (cp == NOSTR || *cp == '\0')
		return(src);
	cp2 = cp;
	if (!ttyset)
		return(strlen(canonb) > 0 ? savestr(canonb) : NOSTR);
	while (*cp != '\0') {
		c = *cp++;
		if (c == c_erase) {
			if (cp2 == canonb)
				continue;
			if (cp2[-1] == '\\') {
				cp2[-1] = c;
				continue;
			}
			cp2--;
			continue;
		}
		if (c == c_kill) {
			if (cp2 == canonb)
				continue;
			if (cp2[-1] == '\\') {
				cp2[-1] = c;
				continue;
			}
			cp2 = canonb;
			continue;
		}
		*cp2++ = c;
	}
	*cp2 = '\0';
#endif
	if (equal("", canonb))
		return(NOSTR);
	return(savestr(canonb));
}

#ifdef SIGCONT
/*
 * Receipt continuation.
 */
void
ttycont(s)
{

	hadcont++;
	sigrelse(SIGCONT);
	longjmp(rewrite, 1);
}
#endif

/* following two functions added for V9 by adb */
getbaud()
{
	struct ttydevb tbuf;
	int baud;

	outtty = isatty(1);
	if (ioctl(1, TIOCGDEV, &tbuf)==0)
		baud = tbuf.ospeed;
	else
		baud = B9600;
	if( baud < B1200 ) return(5);
	else if( baud == B1200) return(10);
	else return(20);
}

#ifdef TIOCGETP
gtty(i,buf)
int i;
struct sgttyb *buf;
{
	return(ioctl(i,TIOCGETP,buf));
}
stty(i,buf)
int i;
struct sgttyb *buf;
{
	return(ioctl(i,TIOCSETP,buf));
}
#endif  /* TIOCGETP */


/*
 * Null routine to allow us to hold SIGCONT
 */
void
signull(s)
{}
#endif	/* USG_TTY */