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