Tip and 1.1.1

Doug Tody X217 tody at noao.edu
Sun Apr 8 18:08:06 AEST 1990


>From article <9890001 at hpislx.HP.COM>, by brent at hpislx.HP.COM (Brent Geske):
>> > Has anyone successfully used tip on A/UX 111?  No matter what I do, I can't
>> > seem to eliminate extra character echo.  hd is set to false as is el (le?).
>> > Any clues?
>> 
>> I had the same problem, again using 1.1.1.  cu works fine (though slow)
>> tip has the character echo problem.  I have not ruled out an echo problem
>> in the modem though (trailblazer plus).  I ended up writing my own program
>> to replace cu/tip (faster) so have not persued it further.
>> -- 
>> Doug Tody
> 
> I also have this problem, and I'm using a Multitech 9600 baud modem.  I would
> thus suspect that it is not a problem with the various modems in use.
> Would you be willing to post your replacement for cu/tip?  (Pretty please?)

Ok, I hope it is not too long to post in this fashion but here it is.  This
program is presented as-is, meaning that it was written for my own use and I
did not go to any effort to add features other than what I needed for my own
use.  The program defaults (baud rate, serial port used by the modem) are
merely what was convenient for me and may need to be changed by someone else.
Despite the fact the the trailblazer is error free and the use of flow control
between the modem and the computer I still see some occasional loss of data,
although for the most part the program works very well.  The main advantages
of this program over cu are speed, and the elimination of ~ as the escape
character.  There is also a useful feature for logging all terminal output
in a file; this feature is used, e.g., for making local copies of text files.
If tip is working again in 2.0 (or is working now and we have all been fooled)
then probably this program won't be needed anymore.

The program should be placed in a file modem.c and compiled with
"cc -O modem.c -o modem".  For some reason which I have not had time to
track down, when compiled with gcc with no special command line switches
the program fails at startup on an ioctl call.

Doug Tody, National Optical Astronomy Observatories, Tucson AZ, 602-325-9217
UUCP: {arizona,decvax,ncar}!noao!tody  or  uunet!noao.edu!tody 
Internet: tody at noao.edu             SPAN/HEPNET: NOAO::TODY (NOAO=5355)

----------------------------------- modem.c ------------------------------
#include <stdio.h>
#include <ctype.h>
#include <fcntl.h>
#include <termio.h>
#include <signal.h>
#include <sys/time.h>

/*
 * MODEM -- Kind of like CU.  Connects your terminal to the modem port.
 */

#define	TERM		"/dev/tty"	/* user terminal device */
#define	PORT		"/dev/tty0"	/* modem (serial) port */
#define	BAUD		19200		/* default baud rate */
#define	ESCAPE		'\001'		/* escape character */

#define	SZ_OBUF		4096		/* max output data to buffer */
#define	SZ_LINE		1024		/* max size line */
#define	SZ_DBUF		128		/* size of a data buffer for copies */
#define	SZ_FNAME	256		/* max size filename */
#define	NLWAIT		200		/* pause, msec/line, for copies */
#define	IOWAIT		50		/* delay between reads, heavy i/o */
#define	CMDWAIT		1000		/* wait after command, msec */
#define	EOFWAIT		200		/* wait after sending EOF, msec */
#define	NFAST		8		/* number of "fast" reads after type */
#define	TOP		128		/* flush output every TOP bytes  */

extern	int	errno;
static	int	rfd = 0;
static	int	debug = 0;
static	int	watch = 0;
static	int	top = TOP;
static	int	nonblock = 0;
static	int	binary = 0;
static	int	baud = BAUD;
static	char	cmd[SZ_LINE];
static	char	obuf[SZ_OBUF];
static	char	lbuf[SZ_LINE];
static	char	dbuf[SZ_DBUF];
static	char	infile[SZ_FNAME];
static	char	outfile[SZ_FNAME];
static	char	eofstr[2] = { 0x1a, 0 };
static	int	ringring, terminal, modem;
static	FILE	*logfile = NULL;


/* MODEM -- Open a virtual terminal connection to a modem port.  The port is
 * assumed to already be active, i.e., this program does not do any dialing.
 * On my system I just type "modem" and I am talking to the modem, or if I
 * have already dialed into a remote system, to the remote system.  One can
 * suspend or exit/restart the modem program without losing the connection.
 *
 * Usage:
 *
 *	modem [-b baud]
 *
 * Baud rates of up to 19200 are supported.  The default baud rate is 19200.
 * The escape character is ctrl/a.  The following escapes are recognized:
 *
 *	ctrl/a ctrl/a		exit
 *	ctrl/a ctrl/x		suspend
 *	ctrl/a ctrl/p		put ascii file (can lose data)
 *	ctrl/a ctrl/l		log [stop logging] output to a file
 *	ctrl/a ctrl/d		toggle debug output (to file "modem.io")
 *
 * Edit the program if you want a different default baud rate, escape char,
 * or modem port.
 */
main (argc, argv)
int	argc;
char	**argv;
{
	register char	*op, *otop;
	register int	fast;

	int	efd, bits, stat, arg, nchars;
	int	kb, bytes, tflag, mflag;
	struct	termio tty, o_tty;
	struct	termio mty, o_mty;
	struct	timeval delay;
	char	*argp, *ip, *cp;
	FILE	*db, *fp;
	char	ch;

	errno = 0;

	/* Process the argument list. */
	for (arg=1;  arg < argc;  arg++)
	    if (argp = argv[arg]) {
		if (!strcmp (argp, "-d")) {		/* debug output */
		    debug++;
		} else if (!strcmp (argp, "-w")) {	/* local echo */
		    watch++;
		} else if (!strcmp (argp, "-nb")) {	/* nonblocking */
		    nonblock++;
		} else if (!strcmp (argp, "-b")) {	/* baud rate */
		    if (argp = argv[++arg])
			baud = atoi(argp);
		} else if (!strcmp (argp, "-obuf")) {	/* output buf size */
		    if (argp = argv[++arg]) {
			top = atoi(argp);
			if (top > SZ_OBUF)
			    top = SZ_OBUF;
		    }
		}
	    }

	/* Open the modem port. */
	if ((modem = open (PORT, 2)) < 0) {
	    fprintf (stderr, "cannot open %s\n", PORT);
	    exit (1);
	}

	/* Set raw mode no echo on modem port. */
	if (ioctl (modem, TCGETA, &o_mty) < 0) {
	    fprintf (stderr, "cannot read modem port status\n");
	    exit (2);
	} else {
	    mty = o_mty;
	    mty.c_iflag &= ~(INLCR|ICRNL|IUCLC|BRKINT);
	    mty.c_iflag |= (IGNBRK|IGNPAR|IXON|IXOFF);
	    mty.c_oflag &= ~(OPOST|ONLCR|TAB3);
	    mty.c_lflag &= ~(ICANON|ISIG|ECHO|ECHOK);
	    mty.c_cflag &= ~(PARENB|CSTOPB|HUPCL|CLOCAL);
	    mty.c_cc[4]  = 0;  /* min */
	    mty.c_cc[5]  = 0;  /* time */

	    switch (baud) {
	    case 1200:
		mty.c_cflag = ((mty.c_cflag & ~CBAUD) | B1200);
		break;
	    case 4800:
		mty.c_cflag = ((mty.c_cflag & ~CBAUD) | B4800);
		break;
	    case 19200:
		mty.c_cflag = ((mty.c_cflag & ~CBAUD) | B19200);
		break;
	    default:
		mty.c_cflag = ((mty.c_cflag & ~CBAUD) | B9600);
		break;
	    }

	    if (ioctl (modem, TCSETAF, &mty) < 0) {
		fprintf (stderr, "cannot set modem port mode\n");
		perror ("modem");
		exit (3);
	    }
	}

	/* Open the user terminal. */
	if ((terminal = open (TERM, 2)) < 0) {
	    fprintf (stderr, "cannot open %s\n", TERM);
	    exit (1);
	}

	/* Set raw mode no echo on user terminal. */
	if (ioctl (terminal, TCGETA, &o_tty) < 0) {
	    fprintf (stderr, "cannot read terminal status\n");
	    exit (2);
	} else {
	    tty = o_tty;
	    tty.c_iflag &= ~(INLCR|ICRNL|IUCLC|ISTRIP|BRKINT);
	    tty.c_oflag &= ~OPOST;
	    tty.c_lflag &= ~(ICANON|ISIG|ECHO);
	    tty.c_cc[4]  = 0;  /* min */
	    tty.c_cc[5]  = 0;  /* time */
	    if (ioctl (terminal, TCSETAF, &tty) < 0) {
		fprintf (stderr, "cannot set terminal mode\n");
		exit (3);
	    }
	}

	/* Enable nonblocking i/o for terminal ouptut.
	do_fcntl (terminal, F_SETFD, O_NDELAY|O_RDWR);

	if (debug) {
	    if ((db = fopen ("modem.io", "a")) == NULL) {
		ioctl (terminal, TCSETAF, &o_tty);
		fprintf (stderr, "cannot open debug output file\n");
		exit (6);
	    }
	    fprintf (db, "------ terminal on %d, modem on %d\n",
		terminal, modem);
	    fflush (db);
	}

	/* Check for any errors. */
	if (errno) {
	    ioctl (terminal, TCSETAF, &o_tty);
	    perror ("Warning");
	    ioctl (terminal, TCSETAF, &tty);
	}

	/* Compute the select bitmask. */
	tflag = (1 << terminal);
	mflag = (1 << modem);
	bits = (tflag|mflag);
	delay.tv_sec = 0;
	delay.tv_usec = 10000;		/* 10 milliseconds */
	otop = &obuf[SZ_OBUF];
	errno = 0;
	op = obuf;
	fast = 0;

	/* Read successive characters from the terminal and copy to the remote
	 * job, and read output from the remote job and copy to the local
	 * terminal.
	 */
	do {
	    if ((stat = select (32, (rfd=bits,&rfd), 0, 0, &delay)) < 0) {
		fprintf (stderr, "modem: select error\n");
		fflush (db);
		break;
	    } else if (stat == 0) {	/* timeout */
		if (op > obuf)
		    goto flush;
		else if ((stat = select (32, (rfd=bits,&rfd), 0,0,NULL)) < 0) {
		    fprintf (stderr, "modem: select error\n");
		    fflush (db);
		    break;
		}
	    }

	    if (debug)
		fprintf (db, "select returns %08o\n", rfd);

	    /* Read from the user terminal. */
	    if (rfd & tflag) {
		if (read (terminal, &ch, 1) < 1)
		    break;
		if (debug)
		    fprintf (db, "read %03x from %d\n", ch, terminal);

		if (ch == ESCAPE) {			/* ctrl/a */
		    if (debug)
			fprintf (db, "ctrl/a seen\n");

		    /* Get the escape command. */
		    if (select (32, (efd=tflag,&efd), 0,0,NULL) < 0)
			goto quit;
		    if (read (terminal, &ch, 1) < 1)
			goto quit;

		    /* Process the escape. */
		    switch (ch) {
		    case 'A' - 64:			/* ctrl/a ctrl/a */
			goto quit;
			break;
		    case 'X' - 64:			/* ctrl/a ctrl/x */
			/* Suspend. */
			if (logfile)
			    fflush (logfile);
			ioctl (terminal, TCSETAF, &o_tty);
			kill (getpid(), SIGSTOP);
			ioctl (terminal, TCSETAF, &tty);
			break;

		    case 'D' - 64:			/* ctrl/a ctrl/d */
			/* Toggle debug output. */
			if (debug) {
			    fprintf (stderr, "[debug off] ");
			    fflush (stderr);
			    fclose (db);
			    debug = 0;
			    db = NULL;
			} else {
			    if ((db = fopen ("modem.io", "a")) == NULL) {
				ioctl (terminal, TCSETAF, &o_tty);
				fprintf (stderr,
				    "cannot open debug output file\n");
				exit (6);
			    }
			    fprintf (db, "------ terminal on %d, modem on %d\n",
				terminal, modem);
			    fflush (db);
			    debug = 1;
			    fprintf (stderr, "[debug on] ");
			    fflush (stderr);
			}
			break;

		    case 'B' - 64:			/* ctrl/a ctrl/b */
			binary = 0;			/* (disabled) */
			goto putfile;

		    case 'P' - 64:			/* ctrl/a ctrl/p */
			/* Put a file. */
			binary = 0;
putfile:
			ioctl (terminal, TCSETAF, &o_tty);
			do_fcntl (modem, F_SETFD, O_RDWR);

			/* Get the file names. */
			printf ("put: ");  fflush (stdout);
			if (fgets (lbuf, SZ_LINE, stdin) != NULL) {
			    for (ip=lbuf;  isspace(*ip);  ip++)
				;
			    for (cp=infile;  *ip;  ip++)
				if (isspace (*ip))
				    break;
				else
				    *cp++ = *ip;
			    *cp = '\0';

			    for (;  isspace(*ip);  ip++)
				;
			    for (cp=outfile;  *ip;  ip++)
				if (isspace (*ip))
				    break;
				else
				    *cp++ = *ip;
			    *cp = '\0';
			}
			if (!infile[0] || ((fp = fopen(infile,"r")) == NULL)) {
			    printf ("cannot open %s\n", infile);
			    ioctl (terminal, TCSETAF, &tty);
			    do_fcntl (modem, F_SETFD, O_NDELAY|O_RDWR);
			    goto next;
			}

			if (!outfile[0])
			    strcpy (outfile, infile);

			/* Send the file. */
			sprintf (cmd, "stty %s ; cat - >%s; stty %s\n",
			    binary ? "raw -echo" : "-echo",
			    outfile,
			    binary ? "-raw echo" : "echo"
			    );
			puttext (modem, cmd, strlen(cmd));
			wmsec (CMDWAIT);

			kb = bytes = 0;
			if (binary) {
			    while ((nchars=fread(dbuf,1,SZ_DBUF,fp)) != NULL) {
				puttext (modem, dbuf, nchars);
				bytes += nchars;
				if (!watch && bytes/1024 > kb) {
				    printf ("\r%d", ++kb);
				    fflush (stdout);
				}
			    }
			} else {
			    while ((fgets (dbuf, SZ_DBUF, fp)) != NULL) {
				puttext (modem, dbuf, nchars=strlen(dbuf));
				bytes += nchars;
				if (!watch && bytes/1024 > kb) {
				    printf ("\r%d", ++kb);
				    fflush (stdout);
				}
			    }
			}

			fclose (fp);
			puttext (modem, eofstr, 1);
			wmsec (EOFWAIT);
			ioctl (terminal, TCSETA, &tty);
			do_fcntl (modem, F_SETFD, O_NDELAY|O_RDWR);
			op = obuf;
			break;

		    case 'L' - 64:			/* ctrl/a ctrl/l */
			/* Toggle logging of output to a file. */

			if (logfile) {
			    fclose (logfile);
			    logfile = NULL;
			    fprintf (stderr, "[logging off] ");
			    fflush (stderr);

			} else {
			    ioctl (terminal, TCSETAF, &o_tty);

			    /* Get the logfile name. */
			    printf ("logfile: ");  fflush (stdout);
			    if (fgets (lbuf, SZ_LINE, stdin) != NULL) {
				for (ip=lbuf;  isspace(*ip);  ip++)
				    ;
				for (cp=outfile;  *ip;  ip++)
				    if (isspace (*ip))
					break;
				    else
					*cp++ = *ip;
				*cp = '\0';
			    }
			    ioctl (terminal, TCSETAF, &tty);

			    if (outfile[0]) {
				if ((logfile = fopen (outfile, "a")) == NULL) {
				    fprintf (stderr,
					"cannot append to %s\r\n", outfile);
				    goto next;
				}
			    }
			}

			op = obuf;
			break;

		    default:
			goto next;
			break;
		    }

		} else {
		    stat = write (modem, &ch, 1);
		    if (debug)
			fprintf (db, "write %03x to %d: stat=%d\n",
			    ch, modem, stat);
		    if (stat != 1)
			goto quit;

		    /* Cancel the output buffer if ctrl/c. */
		    if (ch == '\003')
			op = obuf;
		}

		/* Typing causes the time delays to be adjusted to increase
		 * responsiveness at the expense of shorter i/o transfers and
		 * decreased bandwidth.  If the result is a lot of output the
		 * delays will quickly adjust back to the greater data volume.
		 */
		fast = NFAST;
	    }
next:
	    /* Read from the remote job. */
	    if (rfd & mflag) {
		if (!fast)
		    wmsec (IOWAIT);
		if ((nchars = read (modem, op, otop-op)) < 0)
		    break;
		else if (nchars > 0)
		    op += nchars;

		if (debug) {
		    char    cbuf[80], *ii, *oo;
		    int     n = (nchars < 8) ? nchars : 8;

		    /* Dump the first 8 character values in printable form. */
		    for (ii=(op-nchars), oo=cbuf;  --n >= 0;  ii++) {
			*oo++ = '\'';
			if (isprint (*ii))
			    *oo++ = *ii;
			else {
			    *oo++ = '^';
			    *oo++ = *ii + 'A' - 1;
			}
			*oo++ = '\'';
			*oo++ = ' ';
		    }
		    *oo++ = '\0';
		    fprintf (db, "read %03d chars from %d\t\t%s\n",
			nchars, modem, cbuf);
		}
	    }

	    /* Flush the output buffer periodically or if it fills. */
	    if (op > obuf && (fast || op - obuf > top)) {
flush:		if (debug)
		    fprintf (db, "flush output buffer, %d chars\n", op-obuf);
		if ((stat = write (terminal, obuf, op - obuf)) != op-obuf) {
		    if (stat < 0) {
			fprintf (stderr,
			    "modem write error, errno=%d\n", errno);
			break;
		    } else {
			op += stat;
			wmsec (IOWAIT);
			goto flush;
		    }
		}
		putlog (obuf, op-obuf);
		if (fast)
		    --fast;
		op = obuf;
	    }
	} while (1);
quit:
	/* Shutdown. */
	while (op > obuf) {
	    stat = write (terminal, obuf, op - obuf);
	    if (stat < 0) {
		fprintf (stderr,
		    "modem write error, errno=%d\n", errno);
		break;
	    } else {
		op += stat;
		wmsec (IOWAIT);
	    }
	}
	if (logfile) {
	    putlog (obuf, op-obuf);
	    fclose (logfile);
	}
	if (debug)
	    fclose (db);

	ioctl (terminal, TCSETA, &o_tty);
	ioctl (modem, TCSETA, &o_mty);
	close (terminal);
	close (modem);

	printf ("\n");
}


/* PUTTEXT -- Put a line of text to the output device, then pause to allow
 * the system on the receiving end to process the line of input.
 */
puttext (fd, lbuf, nchars)
int	fd;			/* output file */
char	*lbuf;			/* line of text */
int	nchars;			/* nchars to be output */
{
	if (watch) {
	    write (1, lbuf, nchars);
	    write (1, "\r", 1);
	}

	write (fd, lbuf, nchars);
	wmsec (NLWAIT + nchars * 2);
}


/* PUTLOG -- Put a line of text to the log file.  Convert CRLF into LF in
 * the process.
 */
putlog (lbuf, nchars)
char	*lbuf;			/* line of text */
int	nchars;			/* nchars to be written */
{
	register char *ip;
	register int n;

	if (logfile)
	    for (n=nchars, ip=lbuf;  --n >= 0 && *ip;  ip++) {
		if (*ip == '\r' && (!n || *(ip+1) == '\n'))
		    continue;
		fputc (*ip, logfile);
	    }
}


/* WMSEC -- Suspend task execution (sleep) for the specified number
 * of milliseconds.
 */
wmsec (msec)
int	msec;
{
	struct	itimerval itv, oitv;
	register struct itimerval *itp = &itv;
	int	(*old)();
	static	int napmsx();
	int	stat;

	if (msec <= 0)
	    return;

	timerclear (&itp->it_interval);
	timerclear (&itp->it_value);
	if (setitimer (ITIMER_REAL, itp, &oitv) < 0)
	    return;

	itp->it_value.tv_usec = (msec % 1000) * 1000;
	itp->it_value.tv_sec  = (msec / 1000);

	if (timerisset (&oitv.it_value)) {
	    if (timercmp(&oitv.it_value, &itp->it_value, >))
		oitv.it_value.tv_sec -= itp->it_value.tv_sec;
	    else {
		itp->it_value = oitv.it_value;
		oitv.it_value.tv_sec  = 1;
		oitv.it_value.tv_usec = 0;
	    }
	}

	old = signal (SIGALRM, napmsx);

	ringring = 0;
	setitimer (ITIMER_REAL, itp, (struct itimerval *)0);
	while (!ringring)
	    sigpause (0);
	setitimer (ITIMER_REAL, &oitv, (struct itimerval *)0);

	signal (SIGALRM, old);
}


static int
napmsx()
{
	ringring = 1;
}


/* DO_FCNTL -- Conditionally execute fcntl.
 */
do_fcntl (fd, flag, arg)
int	fd;
int	flag;
int	arg;
{
	if (nonblock)
	    fcntl (fd, flag, arg);
}



More information about the Comp.unix.aux mailing list