
/*
 *  GETTY.C
 *
 *  $Header: Beta:src/uucp/src/getty/RCS/getty.c,v 1.1 90/02/02 12:13:30 dillon Exp Locker: dillon $
 *
 *  (C)Copyright 1989, Matthew Dillon, All Rights Reserved
 *
 *  Uses:
 *	GETTY:PASSWD
 *	GETTY:LOGFILE
 *	GETTY:Getty-Header
 *
 *  GETTY   <options>	<modem-commands>
 *
 *	-Sdevicenam Tells GETTY which serial.device to use, default
 *		    serial.device
 *
 *	-Uunitnum   Tells GETTY which unit number to use, default 0
 *
 *	-A	    Always talk to the modem at the first baud
 *		    rate in the -B list and ignore the baud rate
 *		    in the CONNECT message.
 *
 *	-7	    use SERF_7WIRE while online.
 *
 *	-Bn	    Set baud rate.  If specified multiple times the
 *		    first one is the default and remaining ones
 *		    switched to when a BREAK is received after a
 *		    connect.  Also determines how CONNECT messages
 *		    are interpreted.  A CONNECT with no number uses
 *		    the first -B entry while CONNECTs with numbers
 *		    use those numbers regardless of any -B entries.
 *
 *	-Mc	    Set modem type:
 *			c = m	= multimodem
 *			    h	= hays
 *			    d	= dumb (no AT or +++ cmds are ever sent),
 *				  normally used with only one specified
 *				  baud rate.
 *
 *	-m1	    Turn on the modem speaker during dialing/connect
 *		    (default is -m0)
 *
 *	-h0	    Ignore CD (carrier detect), default is to use
 *		    carrier detect.
 *
 *	-c0	    Ignore CONNECT message (i.e. you are not connected
 *		    to a modem, usually combined with -h0).  Default is
 *		    to expect a CONNECT message.
 *
 *	-d0	    do not use DTR to drop connection.	Default is
 *		    to drop DTR to drop a connection.  If this option
 *		    is chosen the +++ and ATH0 sequence will be used
 *		    to drop a connection.
 *
 *	-xn	    Set debug level.  Also causes log output to go
 *		    to stdout instead of GETTY:LOGFILE
 *
 *	-0	    QUIT - Kills any running getty for the specified
 *		    port.
 *
 *	Any fields specified without a '-' are considered modem commands
 *	used to initialize/reinitialize the modem.  Up to 16 fields may
 *	be specified (each is sent to the modem in 1 second intervals)
 */

#include <exec/types.h>
#include <exec/lists.h>
#include <exec/devices.h>
#include <devices/timer.h>
#include <devices/serial.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <hardware/cia.h>
#include <stdio.h>
#include <stdlib.h>
#include "protos.h"
#include <pwd.h>
#include "version.h"

#include "log.h"

IDENT(".02");

#ifndef IO_STATF_READBREAK
#define IO_STATF_READBREAK (IOSTF_READBREAK<<8)
#endif

#define arysize(ary)    (sizeof(ary)/sizeof((ary)[0]))

#define ST_WAITCD	0
#define ST_CONNECT	1
#define ST_LOGIN	2
#define ST_PASSWD	3
#define ST_RUN		4

typedef struct IORequest    IOR;
typedef struct timerequest  IOT;
typedef struct IOExtSer     IOSER;
typedef struct MsgPort	    PORT;
typedef struct List	    LIST;
typedef struct Node	    NODE;
typedef struct Message	    MSG;
typedef void (*FPTR)();

typedef struct GMsg {
    struct Message  Msg;
    short   Cmd;
    long    Data1;
    void    *Data2;
} GMsg;

extern struct ProcID *RunPasswdEntry();

char	*CopyRight = "(c)Copyright 1989, Matthew Dillon, All Rights Reserved\r\n";
char	*ComPortName;

char	*DeviceName = "serial.device";
long	DeviceUnit  = 0;
long	NullFH;
char	SpeakerLevel	= 0;
char	AnswerRing	= 2;	/*  default, answer on second ring */
char	SpeakerOpt	= 0;
char	IgnoreCD	= 0;
char	IgnoreConnect	= 0;
char	IgnoreDTR	= 0;
char	BaudAdjust	= 0;
char	DropOnExit	= 1;
char	ModemType	= 'h';
char	ZeroOption	= 0;
char	Wire7		= 0;		/*  use 7 wire while online */
char	Locked		= 0;		/*  serial port is lcked    */
long	Bauds[16] = { 9600 };		/*  up 16 baud rates	    */
char	*AtFields[16];

PORT	*ComPort;
PORT	*IoSink;	/*  Sink for IO reqs.	*/
long	IoSinkMask;
long	ComPortMask;

IOT	Iot;		/*  check-carrier	*/
IOSER	Iosr;		/*  serial read-req	*/
IOSER	Iosw;		/*  serial write-req	*/

char	IotIP;		/*  Iot0 in progress	*/
char	IosrIP;

char	ScrBuf[256];
char	ConnectBuf[64];
char	LoginBuf[32];
char	PasswdBuf[32];
char	RxBuf[32];
char	HaveConnectMsg;
char	HaveLogin;
char	HavePasswd;

short	State;
short	Index;
short	BIndex;

short CountDown;
short GotOffPort;

void	SerPuts();
void	Set7Wire();
void	xexit();
void	Disconnect();
void	ClearRecv();
void	InitModem();
void	SetParams();
void	DoOptions();
void	SerialOff();
void	InterceptDeviceVector();
void	RestoreDeviceVector();

brk()
{
    return(0);
}

main(ac, av)
char *av[];
{
    extern int IAmGetty;
    char termCr = 1;
    char termLf = 1;

    IAmGetty = 1;	/*  for LockSerialPort()/UnLockSerialPort()   */

    puts(CopyRight);
    fflush(stdout);
    onbreak(brk);

    LogProgram = "Getty";
    LogWho = LoginBuf;
    LogFile = "Getty:LOGFILE";
    PasswdFile = "Getty:Passwd";

    DoOptions(ac, av);

    IoSink = CreatePort(NULL, 0);
    IoSinkMask = 1 << IoSink->mp_SigBit;

    ComPortName = malloc(strlen(DeviceName) + 20);
    sprintf(ComPortName, "Getty.%s.%ld", DeviceName, DeviceUnit);

    Forbid();
    if (ComPort = FindPort(ComPortName)) {
	GMsg msg;
	msg.Cmd = 'O';
	msg.Data1 = ac;
	msg.Data2 = (void *)av;
	msg.Msg.mn_ReplyPort = IoSink;
	PutMsg(ComPort, &msg.Msg);
	WaitPort(IoSink);
	Remove(&msg.Msg.mn_Node);
	Permit();
	puts("Options updated");
	xexit(0);
    }
    ComPort = CreatePort(ComPortName, 0L);
    Permit();

    ComPortMask = 1L << ComPort->mp_SigBit;

    NullFH = Open("NULL:", 1006);
    if (NullFH == NULL) {
	ulog(-1, "GETTY REQUIRES NULL: HANDLER!");
	puts("Requires NULL: handler!");
	xexit(1);
    }
    if (LogToStdout == 0) {
	freopen("NULL:", "r", stdin);
	freopen("NULL:", "w", stdout);
	freopen("NULL:", "w", stderr);
    }


    /*
     *	Timer Device
     */

    Iot.tr_node.io_Message.mn_ReplyPort = IoSink;
    if (OpenDevice("timer.device", UNIT_VBLANK, &Iot, 0L)) {
	Iot.tr_node.io_Device = NULL;
	xexit(20);
    }
    Iot.tr_node.io_Command = TR_ADDREQUEST;

    /*
     *	SERIAL.DEVICE
     */

    Iosr.IOSer.io_Message.mn_ReplyPort = IoSink;
    Iosr.io_SerFlags = SERF_XDISABLED | SERF_7WIRE | SERF_RAD_BOOGIE | SERF_SHARED;
    if (OpenDevice(DeviceName, DeviceUnit, &Iosr, 0L)) {
	Iosr.IOSer.io_Device = NULL;
	xexit(21);
    }

    InterceptDeviceVector(Iosr.IOSer.io_Device);

    Iosw = Iosr;

    Iosw.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE | SERF_SHARED;
    Iosw.io_ExtFlags = 0;
    Iosw.io_Baud = Bauds[0];
    Iosw.io_ReadLen = 8;
    Iosw.io_WriteLen = 8;
    Iosw.io_StopBits = 1;

    SetParams(&Iosw);

    /*
     *	Run Operation
     */

    State = ST_WAITCD;
    Iot.tr_time.tv_secs = 1;
    Iot.tr_time.tv_micro= 0;
    IotIP = 1;
    SendIO(&Iot);

    InitModem();

    Signal(FindTask(NULL), IoSinkMask);

    for (;;) {
	long mask;
	IOR  *ior;
	GMsg *msg;
	mask = Wait(SIGBREAKF_CTRL_C | IoSinkMask | ComPortMask);

	ulog(2, "State = %d", State);

	if (mask & SIGBREAKF_CTRL_C)
	    break;

	if (msg = (GMsg *)GetMsg(ComPort)) {
	    do {
		switch(msg->Cmd) {
		case 'O':
		    DoOptions(msg->Data1, msg->Data2);
		    break;
		}
		ReplyMsg((MSG *)msg);
		if (ZeroOption)
		    xexit(0);
	    } while (msg = (GMsg *)GetMsg(ComPort));
	    if (State == ST_WAITCD && !GotOffPort)
		Disconnect(0);
	}

	while (ior = (IOR *)GetMsg(IoSink)) {
	    if (ior == (IOR *)&Iot) {
		short diu;

		IotIP = 0;
		Iot.tr_time.tv_secs = 1;
		Iot.tr_time.tv_micro= 0;

		/*
		 *  If OpenCnt > 1, disable all processing.
		 *
		 *  When returns to <= 1 reset serial port.
		 */

		diu = DeviceInUse();
		if (diu) {
		    if (GotOffPort == 0) {
			ulog(-1, "Device in use");
			if (Locked)
			    ulog(-1, "getting off port");
			SerialOff();
			GotOffPort = 1;
			State = ST_WAITCD;
		    }
		    SendIO(&Iot);
		    IotIP = 1;
		    continue;
		}
		if (GotOffPort) {
		    if (Locked == 0) {
			LockSerialPort(DeviceName, DeviceUnit);
			Locked = 1;
		    }
		    GotOffPort = 0;
		    ulog(-1, "Getty resetting");
		    SendIO(&Iot);
		    IotIP = 1;
		    Disconnect(1);
		    continue;
		}
		if (State == ST_WAITCD)
		    SerialOff();

		if (State != ST_WAITCD && IosrIP == 0) {
		    if (Locked == 0)
			LockSerialPort(DeviceName, DeviceUnit);
		    Locked = 1;
		    ulog(-1, "Serial Port Locked");

		    Iosr.IOSer.io_Command = CMD_READ;
		    Iosr.IOSer.io_Data = (APTR)RxBuf;
		    Iosr.IOSer.io_Length = 1;
		    SendIO(&Iosr);
		    IosrIP = 1;
		    ulog(1, "Carrier, Getty getting on port");
		}

		Iosw.IOSer.io_Command = SDCMD_QUERY;
		DoIO(&Iosw);
		if (IgnoreCD)
		    Iosw.io_Status &= ~CIAF_COMCD;

		/*
		 *  If state connected and we loose carrier, disconnect.
		 *
		 *  If state connected and timeout occurs disconnect.
		 */

		if (State != ST_WAITCD) {
		    if ((Iosw.io_Status & CIAF_COMCD) != 0) {
			ulog(1, "Getty, Carrier lost");
			Disconnect(0);
		    } else {
			if (--CountDown == 0) {
			    ulog(1, "Getty, Timeout, Disconnecting");
			    Disconnect(1);
			    Iosw.io_Status |= CIAF_COMCD; /* for below */
			}
		    }
		}

		switch(State) {
		case ST_WAITCD:
		    if ((Iosw.io_Status & CIAF_COMCD) == 0) {
			State = ST_CONNECT;
			CountDown = 60;     /*	allow 60 seconds */
			ulog(-1, "Carrier Detect");
		    } else {
			Iot.tr_time.tv_secs = 2;
		    }
		    break;
		case ST_CONNECT:
		    /*
		     *	Wait for CONNECT message, then send Login:
		     */

		    if (IgnoreConnect && HaveConnectMsg == 0) {
			if (Wire7)
			    Set7Wire(&Iosw);
			HaveConnectMsg = 1;
			ulog(-1, "Connect");
		    }

		    if (HaveConnectMsg) {
			FILE *fi;

			Delay(50);
			ClearRecv();
			if (fi = fopen("Getty:Getty-Header", "r")) {
			    while (fgets(ScrBuf, sizeof(ScrBuf), fi)) {
				SerPuts(ScrBuf);
				SerPuts("\r");
			    }
			    fclose(fi);
			}
			termCr = termLf = 1;
			SerPuts("Login: ");
			ulog(-1, "Getty, Connect, Login");
			State = ST_LOGIN;
			Index = 0;
			HaveLogin = 0;
			LoginBuf[0] = 0;
		    }
		    break;
		case ST_LOGIN:	    /*	wait Login: response	*/
		    if (HaveLogin) {
			if (LoginBuf[0] == 0) {
			    State = ST_CONNECT;
			    break;
			}
			ClearRecv();
			PasswdBuf[0] = 0;

			/*
			 *  If no password required, else request
			 *  password.
			 */

			if (CheckLoginAndPassword()) {
			    HavePasswd = 1;
			    Index = 0;
			    State = ST_PASSWD;
			} else {
			    SerPuts("Password: ");
			    ulog(1, "Getty, Passwd");
			    State = ST_PASSWD;
			    HavePasswd = 0;
			    Index = 0;
			}
		    }
		    break;
		case ST_PASSWD:     /*	wait Password: response */
		    if (HavePasswd) {
			if (CheckLoginAndPassword()) {
			    ulog(-1, "login %s", LoginBuf);

			    /*
			     *	Disable read requests but leave serial
			     *	port locked.
			     */

			    if (IosrIP) {
				AbortIO(&Iosr);
				WaitIO(&Iosr);
				IosrIP = 0;
			    }

			    /*
			     *	If run successful, leave read req and
			     *	timer disabled.
			     */

			    RunPasswdEntry();

			    if (DropOnExit)
				Disconnect(1);
			    else
				State = ST_CONNECT;
			    ulog(-1, "disconnect");
			} else {
			    SerPuts("Login Failed.\r\n\n");
			    State = ST_CONNECT;
			    ulog(-1, "LoginFailed user=%s pass=%s", LoginBuf, PasswdBuf);
			}
			HaveLogin = 0;
			HavePasswd= 0;
			LoginBuf[0] = 0;
		    }
		    break;
		}

		/*
		 *  Make no read requests while running 3rd party
		 *  program, else resend read request.
		 */

		if (IotIP == 0) {
		    IotIP = 1;
		    SendIO(&Iot);
		}
	    }

	    /*
	     *	RECEIVED SERIAL READ DATA
	     */

	    if (ior == (IOR *)&Iosr) {
		IosrIP = 0;

		Iosw.IOSer.io_Command = SDCMD_QUERY;
		DoIO(&Iosw);
		if (IgnoreCD)
		    Iosw.io_Status &= ~CIAF_COMCD;

		/*
		 *  BREAK used to switch baud rates between allowed
		 *  values
		 */

		if (Iosw.io_Status & IO_STATF_READBREAK) {
		    if (BaudAdjust == 0 && (State == ST_LOGIN || State == ST_PASSWD)) {
			if (++BIndex == arysize(Bauds))
			    BIndex = 0;
			if (Bauds[BIndex] == 0)
			    BIndex = 0;
			Iosw.io_Baud = Bauds[BIndex];
			SetParams(&Iosw);
			if (Wire7)
			    Set7Wire(&Iosw);
			ulog(1, "<BREAK> to %d baud", Iosw.io_Baud);
			Delay(100);
			ClearRecv();
			Index = 0;
			State = ST_CONNECT;
		    }
		} else
		if (Iosr.IOSer.io_Actual == 1) {
		    char *ptr;
		    UBYTE c = (UBYTE)RxBuf[0];

		    ulog(2, "Rx %02x %c", c, (c < 32) ? ' ' : c);
		    c &= 0x7F;

		    switch(State) {
		    case ST_WAITCD:	/*  looking for CONNECT msg */
		    case ST_CONNECT:	/*  looking for CONNECT msg */
			ptr = ConnectBuf;
			break;
		    case ST_LOGIN:	/*  looking for login name  */
			ptr = LoginBuf;
			break;
		    case ST_PASSWD:	/*  looking for password    */
			ptr = PasswdBuf;
			break;
		    }
		    if (State == ST_LOGIN && HaveLogin)
			c = 0;
		    if (State == ST_PASSWD && HavePasswd)
			c = 0;

		    switch(c) {
		    case 0:
			break;
		    case 8:
			if (State == ST_LOGIN && HaveLogin)
			    break;
			if (Index) {
			    if (State == ST_LOGIN)
				SerPuts("\010 \010");
			    --Index;
			}
			break;
		    case 10:
			if (termLf == 0)
			    break;
			termCr = 0;
		    case 13:
			if (c == 13) {
			    if (termCr == 0)
				break;
			    else
				termLf = 0;
			}
			ptr[Index] = 0;
			Index = 0;
			switch(State) {
			case ST_WAITCD:
			case ST_CONNECT:
			    if (strncmp(ptr, "CONNECT", 7)) {
				ulog(1, "Looking for CONNECT, got '%s'", ptr);
				break;
			    }
			    Delay(50);
			    HaveConnectMsg = 1;
			    if (BaudAdjust) {
				ulog(-1, "Connect Auto-Baud %d", Iosw.io_Baud);
			    } else {
				char *str = ptr + 7;
				while (*str && (*str == 9 || *str == ' '))
				    ++str;
				if (*str >= '0' && *str <= '9')
				    Iosw.io_Baud = atoi(str);
				else
				    Iosw.io_Baud = Bauds[0];
				SetParams(&Iosw);
				ulog(-1, "Connect at %d baud", Iosw.io_Baud);
			    }
			    if (Wire7)
				Set7Wire(&Iosw);
			    break;
			case ST_LOGIN:
			    HaveLogin = 1;
			    SerPuts("\r\n");
			    ulog(1, "Login: %s", ptr);
			    break;
			case ST_PASSWD:
			    HavePasswd = 1;
			    SerPuts("\r\n");
			    ulog(1, "Password: %s", ptr);
			    break;
			}
			break;
		    default:
			if (Index == 31)
			    break;
			if (State == ST_LOGIN) {
			    char cc[2];
			    cc[0] = c;
			    cc[1] = 0;
			    SerPuts(cc);
			}
			ptr[Index++] = c;
			break;
		    }
		}
		if (IosrIP == 0) {
		    Iosr.IOSer.io_Command = CMD_READ;
		    Iosr.IOSer.io_Data = (APTR)RxBuf;
		    Iosr.IOSer.io_Length = 1;
		    SendIO(&Iosr);
		    IosrIP = 1;
		}
	    }
	}
    }
    xexit(0);
}

void
SerialOff()
{
    if (IosrIP) {
	AbortIO(&Iosr);
	WaitIO(&Iosr);
	IosrIP = 0;
    }
    if (Locked) {
	UnLockSerialPort(DeviceName, DeviceUnit);
	Locked = 0;
	ulog(1, "Serial Port UnLocked");
    }
}

void
xexit(code)
{
    if (ComPortMask) {
	GMsg *msg;
	Forbid();
	while (msg = (GMsg *)GetMsg(ComPort))
	    ReplyMsg((MSG *)msg);
	DeletePort(ComPort);
	Permit();
    }
    if (IotIP) {
	AbortIO(&Iot);
	WaitIO(&Iot);
    }
    if (Iot.tr_node.io_Device)
	CloseDevice(&Iot);

    if (IosrIP) {
	AbortIO(&Iosr);
	WaitIO(&Iosr);
    }
    if (Iosr.IOSer.io_Device) {
	RestoreDeviceVector(Iosr.IOSer.io_Device);
	CloseDevice(&Iosr);
    }

    if (IoSink)
	DeletePort(IoSink);

    if (NullFH)
	Close(NullFH);

    if (Locked)
	UnLockSerialPort(DeviceName, DeviceUnit);

    exit(code);
}

void
SerPuts(str)
char *str;
{
    Iosw.IOSer.io_Command = CMD_WRITE;
    Iosw.IOSer.io_Data = (APTR)str;
    Iosw.IOSer.io_Length = strlen(str);
    DoIO(&Iosw);
}

static short RxDisableIP;

void
RxDisable()
{
    RxDisableIP = IosrIP;
    if (IosrIP) {
	AbortIO(&Iosr);
	WaitIO(&Iosr);
	IosrIP = 0;
    }
}

void
RxEnable()
{
    if (RxDisableIP && IosrIP == 0) {
	Iosr.IOSer.io_Command = CMD_READ;
	Iosr.IOSer.io_Data = (APTR)RxBuf;
	Iosr.IOSer.io_Length = 1;
	SendIO(&Iosr);
	IosrIP = 1;
    }
}

void
ClearRecv()
{
    RxDisable();
    ulog(1, "Clear beg");
    for (;;) {
	Iosr.IOSer.io_Command = SDCMD_QUERY;
	DoIO(&Iosr);
	if ((Iosr.IOSer.io_Length = Iosr.IOSer.io_Actual) <= 0)
	    break;
	if (Iosr.IOSer.io_Length > sizeof(RxBuf))
	    Iosr.IOSer.io_Length = sizeof(RxBuf);
	Iosr.IOSer.io_Data = (APTR)RxBuf;
	Iosr.IOSer.io_Command = CMD_READ;
	DoIO(&Iosr);
    }
    ulog(1, "Clear end");
    RxEnable();
}

void
SetParams(ior)
IOSER *ior;
{
    int error;

    if (IosrIP)
	AbortIO(&Iosr);
    ior->IOSer.io_Command = SDCMD_SETPARAMS;
    error = DoIO(ior);
    if (error)
	printf("SetParams, error %d\n", error);
}

void
Set7Wire(ior)
IOSER *ior;
{
    short error;

    if (IosrIP)
	AbortIO(&Iosr);
    ior->IOSer.io_Command = SDCMD_SETPARAMS;
    ior->io_SerFlags |= SERF_7WIRE;
    error = DoIO(ior);
    ior->io_SerFlags &= ~SERF_7WIRE;
    if (error)
	printf("SetParams, error %d\n", error);
}

void
Disconnect(dropdtr)
{
    short retry = (IgnoreDTR) ? 2 : 10;

    ulog(1, "Disconnect drop=%d", dropdtr);
    HaveConnectMsg = 0;
    HaveLogin	= 0;
    HavePasswd	= 0;
    LoginBuf[0] = 0;
    PasswdBuf[0] = 0;
    Index = 0;
    State = ST_WAITCD;

    while (dropdtr && DeviceInUse() == 0) {
	short i;

	RxDisable();

	if (IgnoreDTR) {
	    if (ModemType != 'd') {
		Delay(70);
		SerPuts("+++");
		Delay(70);
		SerPuts("\010\010\r");
		Delay(10);
		SerPuts("ATH0\r");
		Delay(120);
	    }
	} else {
	    CloseDevice(&Iosr);
	    Iosr.IOSer.io_Device = NULL;    /*	so xexit doesn't reclose */
	    for (i = 0; i < 5; ++i) {       /*  5 seconds   */
		Delay(50);
		if (SetSignal(SIGBREAKF_CTRL_C, 0) & SIGBREAKF_CTRL_C)
		    xexit(23);
	    }

	   /*
	    *  Use Iosr to re-open serial device so we don't loose
	    *  our config.
	    */

	    if (OpenDevice(DeviceName, DeviceUnit, &Iosr, 0)) {
		Iosr.IOSer.io_Device = NULL;
		xexit(22);
	    }
	    Iosw.IOSer.io_Device = Iosr.IOSer.io_Device;
	    Iosw.IOSer.io_Unit = Iosr.IOSer.io_Unit;
	    SetParams(&Iosw);
	}

	/*
	 *  Loop until carrier lost
	 */

	Iosw.IOSer.io_Command = SDCMD_QUERY;
	DoIO(&Iosw);
	if (IgnoreCD)
	    Iosw.io_Status &= ~CIAF_COMCD;

	RxEnable();

	if ((Iosw.io_Status & CIAF_COMCD) != 0)
	    break;
	if (--retry == 0) {
	    if (IgnoreDTR == 0)
		puts("Getty: unable to disconnect!");
	    break;
	}
    }
    if (DeviceInUse() == 0)
	InitModem();
}

void
InitModem()
{
    char buf[64];
    short i;

    RxDisable();
    ulog(1, "Init Modem");
    Iosw.io_Baud = Bauds[0];	/*  reset baud rate */
    BIndex = 0;
    SetParams(&Iosw);
    RxEnable();

    switch(ModemType) {
    case 'm':               /*  Multi Modem     */
	SerPuts("\010\010\r");
	Delay(10);
	SerPuts("AT\r");
	Delay(50);
	sprintf(buf, "ATM%dS0=%dX4$BA%d&E%d\r",
	    SpeakerLevel,
	    AnswerRing,
	    !BaudAdjust,
	    (Wire7) ? 4 : 3
	);
	SerPuts(buf);
	break;
    case 'h':
	SerPuts("\010\010\r");
	Delay(10);
	SerPuts("ATZ\r");
	Delay(120);
	strcpy(buf, "AT");
	if (SpeakerOpt)
	    sprintf(buf + strlen(buf), "M%d", SpeakerLevel);
	sprintf(buf + strlen(buf), "S0=%d", AnswerRing);
	strcat(buf, "\r");
	SerPuts(buf);
	break;
    case 'd':
	SerPuts("\010\010\r");
	break;
    }
    for (i = 0; i < arysize(AtFields) && AtFields[i]; ++i) {
	Delay(50);
	SerPuts(AtFields[i]);
	SerPuts("\r");
    }
    Delay(20);
    ClearRecv();
    Index = 0;
}

void
DoOptions(ac, av)
char *av[];
{
    short i;
    short bi = 0;
    short fi = 0;
    long v;

    for (i = 1; i < ac; ++i) {
	char *ptr = av[i];
	if (*ptr != '-') {
	    if (fi != arysize(AtFields))
		AtFields[fi++] = ptr;
	    else
		puts("AT field overflow");
	    continue;
	}
	if (*++ptr)             /*  skip -      */
	    ++ptr;		/*  and option	*/
	v = atoi(ptr);
	switch(ptr[-1]) {
	case '0':
	    ZeroOption = 1;
	    break;
	case '7':
	    Wire7 = 1;
	    break;
	case 'S':
	    DeviceName = ptr;
	    break;
	case 'U':
	    DeviceUnit = v;
	    break;
	case 'M':
	    ModemType = *ptr;
	    break;
	case 'A':
	    BaudAdjust = 1;
	    break;
	case 'B':
	    if (bi != arysize(Bauds))
		Bauds[bi++] = v;
	    else
		puts("-B field overflow");
	    break;
	case 'm':
	    SpeakerOpt = 1;
	    SpeakerLevel = v;
	    break;
	case 'r':
	    AnswerRing = v;
	    break;
	case 'h':
	    IgnoreCD = !v;
	    break;
	case 'c':
	    IgnoreConnect = !v;
	    break;
	case 'd':
	    IgnoreDTR = !v;
	    break;
	case 'x':
	    LogLevel = v;
	    LogToStdout = (v >= 0);
	    break;
	default:
	    printf("Warning, Bad option: -%s\n", ptr);
	    break;
	}
    }
    if (fi && fi != arysize(AtFields))
	AtFields[fi] = NULL;
    if (bi && bi != arysize(Bauds))
	Bauds[bi] = 0;
}

DeviceInUse()
{
    return(Iosr.IOSer.io_Device->dd_Library.lib_OpenCnt > 1);
}

/*
 *  Device Vector Intercept, used to force SERF_SHARED on device open.
 */

extern void AsmIntercept();
extern void AsmRoute();
FPTR OldVector;

void
InterceptDeviceVector(dev)
struct Device *dev;
{
    OldVector = SetFunction((struct Library *)dev, LIB_OPEN, AsmIntercept);
    AsmRoute(OldVector);
}

void
RestoreDeviceVector(dev)
struct Device *dev;
{
    SetFunction((struct Library *)dev, LIB_OPEN, OldVector);
}


