DataMuseum.dk

Presents historical artifacts from the history of:

Commodore CBM-900

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about Commodore CBM-900

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download

⟦6073813c1⟧ TextFile

    Length: 36289 (0x8dc1)
    Types: TextFile
    Notes: UNIX file
    Names: »cu.c«

Derivation

└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
    └─⟦0a3c255ba⟧ UNIX V7 Filesystem
        └─ ⟦this⟧ »cu_src/cu.c« 

TextFile


/* @(#)cu.c     1.4 */
#define ddt
/***************************************************************
 *      cu [-s baud] [-l line] [-h] [-d] [-p] [-t] [-o | -e] telno
 *
 *      legal baud rates: 110,134,150,300,600,1200,2400,4800,9600.
 *
 *      -l is for specifying a line unit from the file whose
 *              name is defined under LDEVS below.
 *      -h is for half-duplex (local echoing).
 *      -t is for adding CR to LF on output to remote (for terminals).
 *      -d can be used (with ddt) to get some tracing & diagnostics.
 *      -o or -e is for odd or even parity on transmission to remote.
 *      Telno is a telephone number with `=' for secondary dial-tone.
 *      If "-l dev" is used, speed is taken from LDEVS.
 *
 *      Escape with `~' at beginning of line.
 *      Silent output diversions are ~>:filename and ~>>:filename.
 *      Terminate output diversion with ~> alone.
 *      ~. is quit, and ~![cmd] gives local shell [or command].
 *      Also ~$ for canned local procedure pumping remote.
 *      Both ~%put from [to]  and  ~%take from [to] invoke built-ins.
 *      Also, ~%break or just ~%b will transmit a BREAK to remote.
 *      ~%nostop toggles on/off the DC3/DC1 input control from remote,
 *              (certain remote systems cannot cope with DC3 or DC1).
 *
 *      As a device-lockout semaphore mechanism, create an entry
 *      in the directory #defined as LOCK whose name is LCK..dev
 *      where dev is the device name taken from the "line" column
 *      in the file #defined as LDEVS.  Be sure to trap every possible
 *      way out of cu execution in order to "release" the device.
 *      This entry is `touched' from the dial() library routine
 *      every hour in order to keep uucp from removing it on
 *      its 90 minute rounds.  Also, have the system start-up
 *      procedure clean all such entries from the LOCK directory.
 *
 *      Modified by Sanford Zelkovitz to include Xmodem transfers
 *      and telephone directory lookup ( -p on the command line ).
 *      The file, /usr/lib/uucp/dirCU, has the following format
 *      per entry:
 *                  name baudrate telephone_number comments
 *      An example of an entry for me:
 *                  alphacm 2400 17148280288 XBBS
 *
 ***************************************************************/
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/termio.h>
#include <sys/errno.h>
#include <setjmp.h>
#include "dial.h"
#define LOW     1200            /* default speed for modems */
#define HIGH    2400            /* high speed for modems */
#define MID     BUFSIZ/2        /* mnemonic */
#define RUB     '\177'          /* mnemonic */
#define XON     '\21'           /* mnemonic */
#define XOFF    '\23'           /* mnemonic */
#define TTYIN   0               /* mnemonic */
#define TTYOUT  1               /* mnemonic */
#define TTYERR  2               /* mnemonic */
#define YES     1               /* mnemonic */
#define NO      0               /* mnemonic */
#define EQUALS  !strcmp         /* mnemonic */
#define EXIT    0               /* exit code */
#define CMDERR  1               /* exit code */
#define NODIAL  2               /* exit code */
#define HUNGUP  3               /* exit code */
#define IOERR   4               /* exit code */
#define SIGQIT  5               /* exit code */
#define NOFORK  6               /* exit code */
#define CRCR    "/usr/bin/crcCU r "
#define CRCS    "/usr/bin/crcCU s "
#define DIREC   "/usr/lib/uucp/dirCU"
extern int
        _debug,                 /* flag for more diagnostics */
        errno,                  /* supplied by system interface */
        optind,                 /* variable in getopt() */
        strcmp();               /* c-lib routine */

extern unsigned
        sleep();                /* c-lib routine */

extern char
        *optarg;                /* variable in getopt() */

static struct termio tv, tv0;   /* for saving, changing TTY atributes */
static struct termio lv;        /* attributes for the line to remote */
static CALL call;               /* from dial.h */

static char
        cxc,                    /* place into which we do character io*/
        tintr,                  /* current input INTR */
        tquit,                  /* current input QUIT */
        terase,                 /* current input ERASE */
        tkill,                  /* current input KILL */
        myeol,                  /* current input EOL */
        teol,                   /* current sencondary input EOL */
        myeof;                  /* current input EOF */

static int
        terminal=NO,            /* flag; remote is a terminal */
        echoe,                  /* save users ECHOE bit */
        echok,                  /* save users ECHOK bit */
        rlfd,                   /* fd for remote comm line */
        child,                  /* pid for recieve proccess */
        intrupt=NO,             /* interrupt indicator */
        duplex=YES,             /* half(NO), or full(YES) duplex */
        sstop=YES,              /* NO means remote can't XON/XOFF */
        rtn_code=0,             /* default return code */
        takeflag=NO,            /* indicates a ~%take is in progress */
        w_char(),               /* local io routine */
        r_char();               /* local io routine */

static
        onintrpt(),             /* interrupt routine */
        rcvdead(),              /* interrupt routine */
        hangup(),               /* interrupt routine */
        quit(),                 /* interrupt routine */
        bye();                  /* interrupt routine */

static void
        flush(),
        shell(),
        dopercen(),
        receive(),
        mode(),
        say(),
#ifdef ddt
        tdmp(),
#endif
        w_str();
char    *msg[]= {
/*  0*/ "usage: %s [-s baud] [-l line] [-h] [-t] [-p] [-d] [-m] [-o|-e] telno\n",
/*  1*/ "interrupt",
/*  2*/ "dialer hung",
/*  3*/ "no answer",
/*  4*/ "illegal baud-rate",
/*  5*/ "acu problem",
/*  6*/ "line problem",
/*  7*/ "can't open L-devices file",
/*  8*/ "Requested device not available\r\n",
/*  9*/ "Requested device not known\r\n",
/* 10*/ "No device available at %d baud\r\n",
/* 11*/ "No device known at %d baud\r\n",
/* 12*/ "Connect failed: %s\r\n",
/* 13*/ "Can not open: %s\r\n",
/* 14*/ "Line gone\r\n",
/* 15*/ "Can't execute shell\r\n",
/* 16*/ "Can't divert %s\r\n",
/* 17*/ "Use `~~' to start line with `~'\r\n",
/* 18*/ "character missed\r\n",
/* 19*/ "after %ld bytes\r\n",
/* 20*/ "%d lines/%ld characters\r\n",
/* 21*/ "Only digits & '-'s or '='s in telno\n",
/* 22*/ "File transmission interrupted\r\n",
/* 23*/ "Cannot fork -- try later\r\n",
/* 24*/ "\r\nCan't transmit special character `%#o'\r\n",
/* 25*/ "\nLine too long\r\n",
/* 26*/ "r\nIO error\r\n",
/* 27*/ "Use `~$'cmd \r\n",
/* 28*/ "Unable to locate phone directory\r\n",
/* 29*/ "Unable to locate requested entry in the telephone directory\n\r",
};

	char xstring[50];
	char crc_buff[100];
	char in_string[50];
	char CUname[20], CUbaud[6], CUtele[20];
	int result;
	FILE *xyz;
	jmp_buf mark;
	int markint;

/***************************************************************
 *      main: get command line args, establish connection, and fork.
 *      Child invokes "receive" to read from remote & write to TTY.
 *      Main line invokes "transmit" to read TTY & write to remote.
 ***************************************************************/

main(argc, argv)
char *argv[];
{
        char *string, *tstring;
        int i, errflag=0;


        lv.c_iflag = (IGNPAR | IGNBRK | ISTRIP | IXON | IXOFF);
        lv.c_cc[VMIN] = '\1';

        call.attr = &lv;
        call.baud = LOW;
        call.speed = LOW;
        call.line = NULL;
        call.telno = NULL;
        call.modem = 0;

	strcpy(xstring,"ATDT");

        while((i = getopt(argc, argv, "dhteoms:p:l:")) != EOF)
                switch(i) {
                        case 'd':
#ifdef  ddt
                                _debug = YES;
#else
                                ++errflag;
#endif
                                break;
                        case 'h':
                                duplex ^= YES;
                                lv.c_iflag &= ~(IXON | IXOFF);
                                sstop = NO;
                                break;
                        case 't':
                                terminal = YES;
                                lv.c_oflag |= (OPOST | ONLCR);
                                break;
                        case 'e':
                                if(lv.c_cflag & PARENB)
                                        ++errflag;
                                else
                                        goto PAROUT;
                                break;
                        case 'o':
                                if(lv.c_cflag & PARENB)
                                        ++errflag;
                                else
                                        lv.c_cflag = PARODD;
                        PAROUT:
                                        lv.c_cflag |= (CS7 | PARENB);
                                break;
                        case 's':
                                call.baud = atoi(optarg);
                                if(call.baud > LOW)
                                        call.speed = HIGH;
                                break;
			case 'p':
				strcpy(in_string,optarg);
				if((xyz = fopen(DIREC,"r")) == NULL)
				{
					say(msg[28]);
					exit (1);
				}
				while ((fscanf(xyz,"%s%s%s",CUname,CUbaud,CUtele)) != EOF)
				{
					if((strcmp(CUname,in_string)) == 0)
					{
						call.baud = atoi(CUbaud);
						if(call.baud > LOW)
							call.speed = HIGH;
						strcat(xstring,CUtele);
						goto around;
					}
					while ((fgetc(xyz)) != '\n');
				}
				say(msg[29]);
				exit (1);
                        case 'l':
                                call.line = optarg;
                                break;
                        case 'm':
                                call.modem = 1;         /* override modem control for direct lines */
                                break;
                        case '?':
                                ++errflag;
                }

        if(optind < argc && optind > 0) {
                tstring = argv[optind];
		strcat(xstring,argv[optind]);
around:
		strcat(xstring,"\r");
	 	string = xstring;


                if(strlen(string) == strspn(string, "ATDZ0123456789=-\r"))
                        call.telno = string;
                else {
                        if(EQUALS(tstring, "dir")) {
                                if(call.line == NULL)
                                        ++errflag;
                        } else
                                ++errflag;
                }
        } else
                if(call.line == NULL)
                        ++errflag;

        if(errflag) {
                say(msg[0], argv[0]);
                exit(1);
        }

        (void)ioctl(TTYIN, TCGETA, &tv0); /* save initial tty state */
        tintr = tv0.c_cc[VINTR]? tv0.c_cc[VINTR]: '\377';
        tquit = tv0.c_cc[VQUIT]? tv0.c_cc[VQUIT]: '\377';
        terase = tv0.c_cc[VERASE]? tv0.c_cc[VERASE]: '\377';
        tkill = tv0.c_cc[VKILL]? tv0.c_cc[VKILL]: '\377';
        teol = tv0.c_cc[VEOL]? tv0.c_cc[VEOL]: '\377';
        myeol = (tv0.c_iflag & ICRNL)? '\r': '\n';
        myeof = tv0.c_cc[VEOF]? tv0.c_cc[VEOF]: '\04';
        echoe = tv0.c_lflag & ECHOE;
        echok = tv0.c_lflag & ECHOK;

        (void)signal(SIGHUP, hangup);
        (void)signal(SIGQUIT, hangup);
        (void)signal(SIGINT, onintrpt);

        if((rlfd = dial(call)) < 0) {
                if(rlfd == NO_BD_A || rlfd == NO_BD_K)
                        say(msg[-rlfd], call.baud);
                else
                        say(msg[12], msg[-rlfd]);
                exit(NODIAL);
        }

        /* When we get this far we have an open communication line */

        mode(1);                        /* put terminal in `raw' mode */


	xyz = fopen("/tmp/dev.CU","w");
	fprintf(xyz, "%d", rlfd);
	fclose(xyz);
	markint = setjmp(mark);
        child = dofork();
        if(child == 0) {
                (void)signal(SIGHUP, rcvdead);
                (void)signal(SIGQUIT, SIG_IGN);
                (void)signal(SIGINT, SIG_IGN);
		(void)signal(SIGCLD, SIG_IGN);
                receive();      /* This should run until killed */
                /*NOTREACHED*/
        } else if(child > 0) {
                (void)signal(SIGUSR1, bye);
                (void)signal(SIGHUP, SIG_IGN);
                (void)signal(SIGQUIT, onintrpt);
                rtn_code = transmit();
                quit(rtn_code);
        } else {
                hangup(rlfd);
                exit(NOFORK);
        }
}
\f


/***************************************************************
 *      transmit: copy stdin to rlfd, except:
 *      ~.      terminate
 *      ~!      local login-style shell
 *      ~!cmd   execute cmd locally
 *      ~$proc  execute proc locally, send output to line
 *      ~%cmd   execute builtin cmd (put, take, or break)
 *      ~X      xmodem interface
 ****************************************************************/

int
transmit()
{
        char b[BUFSIZ];
        register char *p;
        register int escape;

#ifdef  ddt
        if(_debug == YES) say("transmit started\n\r");
#endif
        while(1) {
                p = b;
                while(r_char(TTYIN) == YES) {
                        if(p == b)      /* Escape on leading  ~    */
                                escape = (cxc == '~');
                        if(p == b+1)    /* But not on leading ~~   */
                                escape &= (cxc != '~');
                        if(escape) {
                                if(cxc == myeol || cxc == teol) {
                                        *p = '\0';
                                        if(tilda(b+1) == YES)
                                                return(EXIT);
                                        break;
                                }
                                if(cxc == tintr || cxc == tkill || cxc == tquit ||
                                            (intrupt && cxc == '\0')) {
                                        if(!(cxc == tkill) || echok)
                                                say("\r\n");
                                        break;
                                }
                                if(cxc == terase) {
                                        p = (--p < b)? b:p;
                                        if(p > b)
                                                if(echoe)
                                                        say("\b \b");
                                                else
                                                 (void)w_char(TTYOUT);
                                } else {
                                        (void)w_char(TTYOUT);
                                        if(p-b < BUFSIZ)
                                                *p++ = cxc;
                                        else {
                                                say(msg[25]);
                                                break;
                                        }
                                }
                        } else {
                                if(intrupt && cxc == '\0') {
#ifdef  ddt
                                        if(_debug == YES)
                                                say("got break in transmit\n\r");
#endif
                                        intrupt = NO;
                                        (void)ioctl(rlfd, TCSBRK, 0);
                                        flush();
                                        break;
                                }
                                if(w_char(rlfd) == NO) {
                                        say(msg[14]);
                                        return(IOERR);
                                }
                                if(duplex == NO)
                                        if(w_char(TTYERR) == NO)
                                                return(IOERR);
                                if ( (cxc == tintr) || (cxc == tquit) || ( (p==b) && (cxc == myeof) ) ) {
#ifdef  ddt
                                        if(_debug == YES) say("got a tintr\n\r");
#endif
                                        flush();
                                        break;
                                }
                                if(cxc == myeol || cxc == teol ||
                                                        cxc == tkill) {
                                        takeflag = NO;
                                        break;
                                }
                                p = (char*)0;
                        }
                }
        }
}
/***************************************************************
 *      routine to halt input from remote and flush buffers
 ***************************************************************/
static void
flush()
{
        (void)ioctl(TTYOUT, TCXONC, 0); /* stop tty output */
        (void)ioctl(rlfd, TCFLSH, 0);           /* flush remote input */
        (void)ioctl(TTYOUT, TCFLSH, 1); /* flush tty output */
        (void)ioctl(TTYOUT, TCXONC, 1); /* restart tty output */
        if(takeflag == NO) {
                return;         /* didn't interupt file transmission */
        }
        say(msg[22]);
        (void)sleep(3);
        w_str("echo '\n~>\n';mesg y;stty echo\n");
        takeflag = NO;
}

/**************************************************************
 *      command interpreter for escape lines
 **************************************************************/
int
tilda(cmd)
char    *cmd;
{
	char resp[50];
        say("\r\n");
#ifdef  ddt
        if(_debug == YES) say("call tilda(%s)\r\n", cmd);
#endif
        switch(cmd[0]) {
                case '.':
                        if(call.telno == NULL)
                                if(cmd[1] != '.')
                                        w_str("\04\04\04\04\04");
                        return(YES);
                case '!':
                        shell(cmd);     /* local shell */
                        say("\r%c\r\n", *cmd);
                        break;
                case '$':
                        if(cmd[1] == '\0')
                                say(msg[27]);
                        else {
                                shell(cmd);     /* Local shell */
                                say("\r%c\r\n", *cmd);
                        }
                        break;
                case '%':
                        dopercen(++cmd);
                        break;
#ifdef ddt
                case 't':
                        tdmp(TTYIN);
                        break;
                case 'l':
                        tdmp(rlfd);
                        break;
#endif
/*  xmodem section */
		case 'X':
			mode(0);
			(void)kill(child,SIGKILL);
			printf("\n\rReceive or Send (R S)?   ");
			scanf("%s",resp);
			switch (resp[0]){
				case 's' :
				case 'S' :	
					printf("\n\rInput file name to send: ");
					scanf("%s",in_string);
					printf("\n\r");
					strcpy(crc_buff,CRCS);
					strcat(crc_buff,in_string);
					result = system(crc_buff);
					mode(1);
					longjmp(mark, -1);
				case 'r' :
				case 'R' :
					printf("\n\rInput file name to receive:  ");
					scanf("%s",in_string);
					printf("\n\r");
					strcpy(crc_buff,CRCR);
					strcat(crc_buff,in_string);
					result = system(crc_buff);
					mode(1);
					longjmp(mark, -1);
				default  :
					printf("\n\rIllegial Xmodem Option \n\r");
					mode(1);
					longjmp(mark, -1);
				}
			
                default:
                        say(msg[17]);
        }
        return(NO);
}
/***************************************************************
 *      The routine "shell" takes an argument starting with
 *      either "!" or "$", and terminated with '\0'.
 *      If $arg, arg is the name of a local shell file which
 *      is executed and its output is passed to the remote.
 *      If !arg, we escape to a local shell to execute arg
 *      with output to TTY, and if arg is null, escape to
 *      a local shell and blind the remote line.  In either
 *      case, RUBout or '^D' will kill the escape status.
 **************************************************************/

static void
shell(str)
char    *str;
{
        int     fk, (*xx)(), (*yy)();

#ifdef  ddt
        if(_debug == YES) say("call shell(%s)\r\n", str);
#endif
        fk = dofork();
        if(fk < 0)
                return;
        mode(0);        /* restore normal tty attributes */
        xx = signal(SIGINT, SIG_IGN);
        yy = signal(SIGQUIT, SIG_IGN);
        if(fk == 0) {
                /***********************************************
                 * Hook-up our "standard output"
                 * to either the tty or the line
                 * as appropriate for '!' or '$'
                 ***********************************************/
                (void)close(TTYOUT);
                (void)fcntl((*str == '$')? rlfd:TTYERR,F_DUPFD,TTYOUT);
                (void)close(rlfd);
                (void)signal(SIGINT, SIG_DFL);
                (void)signal(SIGHUP, SIG_DFL);
                (void)signal(SIGQUIT, SIG_DFL);
                (void)signal(SIGUSR1, SIG_DFL);
                if(*++str == '\0')
                        (void)execl("/bin/sh","",(char*)0,(char*)0,0);
                else
                        (void)execl("/bin/sh","sh","-c",str,0);
                say(msg[15]);
                exit(0);
        }
        /* our hourly alarm clock (for uucp) can interupt this wait */
        while(wait((int*)0) != fk);
        (void)signal(SIGINT, xx);
        (void)signal(SIGQUIT, yy);
        mode(1);
}

/***************************************************************
 *      This function implements the 'put', 'take', 'break', and
 *      'nostop' commands which are internal to cu.
 ***************************************************************/

static void
dopercen(cmd)
register char *cmd;
{
        char    *arg[5];
        int     narg;

#ifdef  ddt
        if(_debug == YES) say("call dopercen(\"%s\")\r\n", cmd);
#endif
        arg[narg=0] = strtok(cmd, " \t\n");
        /* following loop breaks out the command and args */
        while((arg[++narg] = strtok((char*) NULL, " \t\n")) != NULL) {
                if(narg < 5)
                        continue;
                else
                        break;
        }

        if(EQUALS(arg[0], "take")) {
                if(narg < 2 || narg > 3) {
                        say("usage: ~%%take from [to]\r\n");
                        return;
                }
                if(narg == 2)
                        arg[2] = arg[1];
                w_str("stty -echo;mesg n;echo '~>':");
                w_str(arg[2]);
                w_str(";cat ");
                w_str(arg[1]);
                w_str(";echo '~>';mesg y;stty echo\n");
                takeflag = YES;
                return;
        }
        else if(EQUALS(arg[0], "put")) {
                FILE    *file;
                char    ch, buf[BUFSIZ], spec[NCC+1], *b, *p, *q;
                int     i, j, len, tc=0, lines=0;
                long    chars=0L;

                if(narg < 2 || narg > 3) {
                        say("usage: ~%%put from [to]\r\n");
                        goto R;
                }
                if(narg == 2)
                        arg[2] = arg[1];
                if((file = fopen(arg[1], "r")) == NULL) {
                        say(msg[13], arg[1]);
R:
                        w_str("\n");
                        return;
                }
                w_str("stty -echo; cat - > ");
                w_str(arg[2]);
                w_str("; stty echo\n");
                intrupt = NO;
                for(i=0,j=0; i < NCC; ++i)
                        if((ch=tv0.c_cc[i]) != '\0')
                                spec[j++] = ch;
                spec[j] = '\0';
                mode(2);
                (void)sleep(5);
                while(intrupt == NO &&
                                fgets(b= &buf[MID],MID,file) != NULL) {
                        len = strlen(b);
                        chars += len;           /* character count */
                        p = b;
                        while(q = strpbrk(p, spec)) {
                                if(*q == tintr || *q == tquit ||
                                                        *q == teol) {
                                        say(msg[24], *q);
                                        (void)strcpy(q, q+1);
                                        intrupt = YES;
                                }
                                b = strncpy(b-1, b, q-b);
                                *(q-1) = '\\';
                                p = q+1;
                        }
                        if((tc += len) >= MID) {
                                (void)sleep(1);
                                tc = len;
                        }
                        if(write(rlfd, b, (unsigned)strlen(b)) < 0) {
                                say(msg[26]);
                                intrupt = YES;
                                break;
                        }
                        ++lines;                /* line count */
                }
                mode(1);
                (void)fclose(file);
                if(intrupt == YES) {
                        intrupt = NO;
                        say(msg[22]);
                        w_str("\n");
                        say(msg[19], ++chars);
                } else
                        say(msg[20], lines, chars);
                w_str("\04");
                (void)sleep(3);
                return;
        }
        else if(EQUALS(arg[0], "b") || EQUALS(arg[0], "break")) {
                (void)ioctl(rlfd, TCSBRK, 0);
                return;
        }
        else if(EQUALS(arg[0], "nostop")) {
                (void)ioctl(rlfd, TCGETA, &tv);
                if(sstop == NO)
                        tv.c_iflag |= IXOFF;
                else
                        tv.c_iflag &= ~IXOFF;
                (void)ioctl(rlfd, TCSETAW, &tv);
                sstop = !sstop;
                mode(1);
                return;
        }
        say("~%%%s unknown to cu\r\n", arg[0]);
}
/***************************************************************
 *      receive: read from remote line, write to fd=1 (TTYOUT)
 *      catch:
 *      ~>[>]:file
 *      .
 *      . stuff for file
 *      .
 *      ~>      (ends diversion)
 ***************************************************************/

static void
receive()
{
        register silent=NO, file;
        register char *p;
        int     tic;
        char    b[BUFSIZ];
        long    lseek(), count;

#ifdef  ddt
        if(_debug == YES) say("receive started\r\n");
#endif
        file = -1;
        p = b;
        while(r_char(rlfd) == YES) {
                if(silent == NO)
                        if(w_char(TTYOUT) == NO)
                                rcvdead(IOERR); /* this will exit */
                /* remove CR's and fill inserted by remote */
                if(cxc == '\0' || cxc == '\177' || cxc == '\r')
                        continue;
                *p++ = cxc;
                if(cxc != '\n' && (p-b) < BUFSIZ)
                        continue;
                /***********************************************
                 * The rest of this code is to deal with what
                 * happens at the beginning, middle or end of
                 * a diversion to a file.
                 ************************************************/
                if(b[0] == '~' && b[1] == '>') {
                        /****************************************
                         * The line is the beginning or
                         * end of a diversion to a file.
                         ****************************************/
                        if((file < 0) && (b[2] == ':' || b[2] == '>')) {
                                /**********************************
                                 * Beginning of a diversion
                                 *********************************/
                                int     append;

                                *(p-1) = '\0'; /* terminate file name */
                                append = (b[2] == '>')? 1:0;
                                p = b + 3 + append;
                                if(append && (file=open(p,O_WRONLY))>0)
                                        (void)lseek(file, 0L, 2);
                                else
                                        file = creat(p, 0666);
                                if(file < 0) {
                                        say(msg[16], p);
                                        perror("");
                                        (void)sleep(10);
                                } else {
                                        silent = YES; 
                                        count = tic = 0;
                                }
                        } else {
                                /*******************************
                                 * End of a diversion (or queer data)
                                 *******************************/
                                if(b[2] != '\n')
                                        goto D;         /* queer data */
                                if(silent = close(file)) {
                                        say(msg[16], b);
                                        silent = NO;
                                }
                                say("~>\r\n");
                                say(msg[20], tic, count);
                                file = -1;
                        }
                } else {
                        /***************************************
                         * This line is not an escape line.
                         * Either no diversion; or else yes, and
                         * we've got to divert the line to the file.
                         ***************************************/
D:
                        if(file > 0) {
                                (void)write(file, b, (unsigned)(p-b));
                                count += p-b;   /* tally char count */
                                ++tic;          /* tally lines */
                        }
                }
                p = b;
        }
        rcvdead(IOERR);
}
/***************************************************************
 *      change the TTY attributes of the users terminal:
 *      0 means restore attributes to pre-cu status.
 *      1 means set `raw' mode for use during cu session.
 *      2 means like 1 but accept interrupts from the keyboard.
 ***************************************************************/
static void
mode(arg)
{
#ifdef  ddt
        if(_debug == YES) say("call mode(%d)\r\n", arg);
#endif
        if(arg == 0) {
                (void)ioctl(TTYIN, TCSETAW, &tv0);
        } else {
                (void)ioctl(TTYIN, TCGETA, &tv);
                if(arg == 1) {
                        tv.c_iflag &= ~(INLCR | ICRNL | IGNCR |
                                                IXOFF | IUCLC);
                        tv.c_iflag |= ISTRIP;
                        tv.c_oflag |= OPOST;
                        tv.c_oflag &= ~(OLCUC | ONLCR | OCRNL | ONOCR
                                                | ONLRET);
                        tv.c_lflag &= ~(ICANON | ISIG | ECHO);
                        if(sstop == NO)
                                tv.c_iflag &= ~IXON;
                        else
                                tv.c_iflag |= IXON;
                        if(terminal) {
                                tv.c_oflag |= ONLCR;
                                tv.c_iflag |= ICRNL;
                        }
                        tv.c_cc[VEOF] = '\01';
                        tv.c_cc[VEOL] = '\0';
                }
                if(arg == 2) {
                        tv.c_iflag |= IXON;
                        tv.c_lflag |= ISIG;
                }
                (void)ioctl(TTYIN, TCSETAW, &tv);
        }
}
static int
dofork()
{
        register int x, i=0;

        while(++i < 6)
                if((x = fork()) >= 0)
                        return(x);
#ifdef  ddt
        if(_debug == YES) perror("dofork");
#endif
        say(msg[23]);
        return(x);
}

static int
r_char(fd)
{
        int rtn;

        while((rtn = read(fd, &cxc, 1)) < 0)
                if(errno == EINTR)
                        if(intrupt == YES) {
                                cxc = '\0';     /* got a BREAK */
                                return(YES);
                        } else
                                continue;       /* alarm went off */
                else {
#ifdef  ddt
                        if(_debug == YES)
                                say("got read error, not EINTR\n\r");
#endif
                        break;                  /* something wrong */
                }
        return(rtn == 1? YES: NO);
}

static int
w_char(fd)
{
        int rtn;

        while((rtn = write(fd, &cxc, 1)) < 0)
                if(errno == EINTR)
                        if(intrupt == YES) {
                                say("\ncu: Output blocked\r\n");
                                quit(IOERR);
                        } else
                                continue;       /* alarm went off */
                else
                        break;                  /* bad news */
        return(rtn == 1? YES: NO);
}
/*VARARGS1*/
static void
say(fmt, arg1, arg2, arg3, arg4, arg5)
char    *fmt;
{
        (void)fprintf(stderr, fmt, arg1, arg2, arg3, arg4, arg5);
}

static void
w_str(string)
register char *string;
{
        int len;

        len = strlen(string);
        if(write(rlfd, string, (unsigned)len) != len)
                say(msg[14]);
}

static
onintrpt()
{
        (void)signal(SIGINT, onintrpt);
        (void)signal(SIGQUIT, onintrpt);
        intrupt = YES;
}

static
rcvdead(arg)    /* this is executed only in the receive proccess */
int arg;
{
#ifdef ddt
        if(_debug == YES) say("call rcvdead(%d)\r\n", arg);
#endif
        (void)kill(getppid(), SIGUSR1);
        exit((arg == SIGHUP)? SIGHUP: arg);
        /*NOTREACHED*/
}

static
quit(arg)       /* this is executed only in the parent proccess */
int arg;
{
#ifdef ddt
        if(_debug == YES) say("call quit(%d)\r\n", arg);
#endif
        (void)kill(child, SIGKILL);
        bye(arg);
        /*NOTREACHED*/
}

static
bye(arg)        /* this is executed only in the parent proccess */
int arg;
{
        int status;
#ifdef ddt
        if(_debug == YES) say("call bye(%d)\r\n", arg);
#endif
        (void)wait(&status);
        say("\r\nDisconnected\r\n");
        hangup((arg == SIGUSR1)? (status >>= 8): arg);
        /*NOTREACHED*/
}
static
hangup(arg)     /* this is executed only in the parent proccess */
int arg;
{
#ifdef ddt
        if(_debug == YES) say("call hangup(%d)\r\n", arg);
#endif
        undial(rlfd);
        mode(0);        /* restore users prior tty status */
        exit(arg);
        /*NOTREACHED*/
}
#ifdef ddt
static void
tdmp(arg)
{
        struct termio xv;
        int i;

        say("\rdevice status for fd=%d\n", arg);
        say("\rF_GETFL=%o,", fcntl(arg, F_GETFL,1));
        if(ioctl(arg, TCGETA, &xv) < 0) {
                char    buf[100];

                i = errno;
                (void)sprintf(buf, "\rtdmp for fd=%d", arg);
                errno = i;
                perror(buf);
                return;
        }
        say("iflag=`%o',", xv.c_iflag);
        say("oflag=`%o',", xv.c_oflag);
        say("cflag=`%o',", xv.c_cflag);
        say("lflag=`%o',", xv.c_lflag);
        say("line=`%o'\r\n", xv.c_line);
        say("cc[0]=`%o',", xv.c_cc[0]);
        for(i=1; i<8; ++i)
                say("[%d]=`%o',", i, xv.c_cc[i]);
        say("\r\n");
}
#endif