
/*
 *  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 <exec/io.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(".06");

#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	RefCnt	    = 0;	/*  take into account ourselves    */
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	  NoBugs	  = 0;*/
char	Wire7		= 0;		/*  use 7 wire while online */
long	Bauds[16] = { 9600 };		/*  up 16 baud rates	    */
char	*AtFields[16];
void	*GettyTask;

APTR	UnitPtr;

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

IOT	Iot;		/*  check-carrier	*/
IOSER	Ios;		/*  parameters req	*/
IOSER	Iosr;		/*  serial read-req	*/
IOSER	Ioss;		/*  synchronous 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;

long	GetStatus();
void	SerPuts();
void	RxStop();
void	RxStart();
void	xexit();
void	Disconnect();
void	ClearRecv();
void	ResetSP();
void	InitModem();
void	SetParams();
void	DoOptions();
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
     */

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

    InterceptDeviceVector(Ios.IOSer.io_Device);

    Iosr = Ios;
    Ioss = Ios;

    Iosr.IOSer.io_Command = CMD_READ;
    Ios.IOSer.io_Command = SDCMD_QUERY;
    DoIO(&Ios);

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

    SetParams(0);

    /*
     *	Run Operation
     */

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

    InitModem();

    GettyTask = FindTask(NULL);

    Signal(GettyTask, IoSinkMask);

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

	ulog(9, "State = %d %d %08lx %08lx", State, RefCnt, UnitPtr, Ios.IOSer.io_Unit);

	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;
		long status;

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

		/*
		 *  If # of references for unit is > 1 then device
		 *  is in use.	When refs fall <= 1 then reset the
		 *  serial port.
		 */

		diu = DeviceInUse();
		if (diu) {
		    if (GotOffPort == 0) {
			ulog(1, "Device in use");
			RxStop();
			State = ST_WAITCD;
		    }
		    GotOffPort = 2;
		    SendIO(&Iot);
		    IotIP = 1;
		    continue;
		}
		if (GotOffPort) {
		    /*
		     *	delay 5 seconds before getting back on port
		     */

		    if (--GotOffPort) {
			Iot.tr_time.tv_secs = 5;
			SendIO(&Iot);
			IotIP = 1;
		    } else {
			ulog(1, "Getty resetting");
			SendIO(&Iot);
			IotIP = 1;
			Disconnect(1);
		    }
		    continue;
		}
		if (State == ST_WAITCD)
		    RxStop();

		if (State != ST_WAITCD && IosrIP == 0) {
		    RxStart();
		    ulog(2, "Carrier, Getty getting on port");
		}

		status = GetStatus();

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

		if (State != ST_WAITCD) {
		    if ((status & CIAF_COMCD) != 0) {
			ulog(2, "Getty, Carrier lost");
			Disconnect(1);
		    } else {
			if (!IgnoreCD && --CountDown == 0) {
			    ulog(2, "Getty, Timeout, Disconnecting");
			    Disconnect(1);
			    status |= CIAF_COMCD;   /*	set lost carrier flag */
			}
		    }
		}

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

		    if (IgnoreConnect && HaveConnectMsg == 0) {
			SetParams(1);
			HaveConnectMsg = 1;
			ulog(2, "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;
			ClearRecv();
			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(2, "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.
			     */

			    RxStop();

			    /*
			     *	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(0, "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) {
		long status;

		IosrIP = 0;

		status = GetStatus();

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

		if (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;
			Ios.io_Baud = Bauds[BIndex];
			SetParams(1);
			ulog(1, "<BREAK> to %d baud", Ios.io_Baud);
			Delay(50);
			ClearRecv();
			Index = 0;
			State = ST_CONNECT;
		    }
		} else if (Iosr.IOSer.io_Actual == 1) {
		    char *ptr;
		    UBYTE c = (UBYTE)RxBuf[0];

		    ulog(9, "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(9, "Looking for CONNECT, got '%s'", ptr);
				break;
			    }
			    Delay(50);
			    HaveConnectMsg = 1;
			    if (BaudAdjust) {
				ulog(9, "Connect Auto-Baud %d", Ios.io_Baud);
			    } else {
				char *str = ptr + 7;
				while (*str && (*str == 9 || *str == ' '))
				    ++str;
				if (*str >= '0' && *str <= '9')
				    Ios.io_Baud = atoi(str);
				else
				    Ios.io_Baud = Bauds[0];
				ulog(9, "Connect at %d baud", Ios.io_Baud);
			    }
			    SetParams(1);
			    break;
			case ST_LOGIN:
			    HaveLogin = 1;
			    SerPuts("\r\n");
			    ulog(1, "Login: %s", ptr);
			    break;
			case ST_PASSWD:
			    HavePasswd = 1;
			    SerPuts("\r\n");
			    ulog(2, "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)
		    RxStart();
	    }
	}
    }
    xexit(0);
}

void
RxStart()
{
    Iosr.IOSer.io_Data = (APTR)RxBuf;
    Iosr.IOSer.io_Length = 1;
    Iosr.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
    IosrIP = 1;
    SendIO(&Iosr);
}

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

long
GetStatus()
{
    int error;
    long status = CIAF_COMCD;	/*  no carrier detect	*/

    if (!DeviceInUse()) {
	Ioss.IOSer.io_Command = SDCMD_QUERY;
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	if (error = DoIO(&Ioss))
	    ulog(0, "Query Error %d", error);
	status = Ioss.io_Status;
	if (IgnoreCD)
	    status &= ~CIAF_COMCD;
    }
    return(status);
}

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);

    RxStop();

    if (Ios.IOSer.io_Device) {
	RestoreDeviceVector(Ios.IOSer.io_Device);
	CloseDevice(&Ios);
    }

    if (IoSink)
	DeletePort(IoSink);

    if (NullFH)
	Close(NullFH);

    if (code)
	ulog(-1, "Getty Exiting with code %d", code);

    exit(code);
}

void
SerPuts(str)
char *str;
{
    int error;

    if (!DeviceInUse()) {
	Ioss.IOSer.io_Command = CMD_WRITE;
	Ioss.IOSer.io_Data = (APTR)str;
	Ioss.IOSer.io_Length = strlen(str);
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	if (error = DoIO(&Ioss))
	    ulog(0, "Write Error %d", error);
    }
}

static short RxDisableIP;

void
RxDisable()
{
    RxDisableIP = IosrIP;
    RxStop();
}

void
RxEnable()
{
    if (RxDisableIP && IosrIP == 0)
	RxStart();
}

void
ClearRecv()
{
    int error;

    if (!DeviceInUse()) {

	Ioss.IOSer.io_Command = CMD_CLEAR;
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;

	if (error = DoIO(&Ioss))
	    ulog(0, "Clear Error %d", error);

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

void
ResetSP()
{
    int error;

    if (!DeviceInUse()) {
	Ioss.IOSer.io_Command = CMD_RESET;
	Ioss.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;

	if (error = DoIO(&Ioss))
	    ulog(0, "Reset Error %d", error);
    }
}


void
SetParams(wire7)
{
    int error;

    /*if (NoBugs == 0)
	wire7 = Wire7;*/

    {

	RxStop();

	if (!DeviceInUse()) {
	    if (wire7 && Wire7)
		Ios.io_SerFlags |= SERF_7WIRE;
	    else
		Ios.io_SerFlags &= ~SERF_7WIRE;
	    Ios.IOSer.io_Command = SDCMD_SETPARAMS;
	    Ios.IOSer.io_Message.mn_Node.ln_Type = NT_MESSAGE;
	    if (error = DoIO(&Ios))
		ulog(0, "SetParams Error %d", error);
	}
    }
}

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

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

    if (IgnoreCD == 0) {
	while (dropdtr && DeviceInUse() == 0) {
	    short i;
	    long status = GetStatus();

	    if (status & CIAF_COMCD)        /*  no carrier  */
		break;

	    RxDisable();
	    SetParams(0);

	    if (IgnoreDTR) {
		if (ModemType != 'd') {
		    Delay(70);
		    if (DeviceInUse())
			break;
		    SerPuts("+++");
		    Delay(70);
		    if (DeviceInUse())
			break;
		    SerPuts("\010\010\r");
		    Delay(10);
		    if (DeviceInUse())
			break;
		    SerPuts("ATH0\r");
		    Delay(120);
		}
	    } else {
		RefCnt += 2;
		CloseDevice(&Ios);
		Ios.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.
		 */

		RefCnt -= 2;
		if (Wire7)
		    Ios.io_SerFlags |= SERF_7WIRE;
		else
		    Ios.io_SerFlags &= ~SERF_7WIRE;
		if (OpenDevice(DeviceName, DeviceUnit, &Ios, 0)) {
		    Ios.IOSer.io_Device = NULL;
		    xexit(22);
		}
		Iosr.IOSer.io_Device = Ios.IOSer.io_Device;
		Ioss.IOSer.io_Device = Ios.IOSer.io_Device;
		Iosr.IOSer.io_Unit = Ios.IOSer.io_Unit;
		Ioss.IOSer.io_Unit = Ios.IOSer.io_Unit;
		SetParams(1);
	    }

	    /*
	     *	Loop until carrier lost
	     */

	    RxEnable();

	    if (--retry == 0) {
		if (IgnoreDTR == 0)
		    puts("Getty: unable to disconnect!");
		break;
	    }
	}
    }
    if (!DeviceInUse()) {
	ResetSP();
	InitModem();
    }
}

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

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

    switch(ModemType) {
    case 'm':               /*  Multi Modem     */
	SerPuts("\010\010\r");
	Delay(10);
	if (DeviceInUse())
	    break;
	SerPuts("AT\r");
	Delay(50);
	if (DeviceInUse())
	    break;
	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);
	if (DeviceInUse())
	    break;
	SerPuts("ATZ\r");
	Delay(120);
	if (DeviceInUse())
	    break;
	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);
	if (DeviceInUse())
	    break;
	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;

    LogLevel = 1;

    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 >= 9);
	    break;
	case 'n':
	    /*NoBugs = 1;*/
	    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(RefCnt > 0);
}

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

extern void AsmOpenIntercept();
extern void AsmCloseIntercept();
/*extern void A2232BeginIOFix();*/

FPTR OldOpenVector;
FPTR OldCloseVector;
/*FPTR OldBeginIOVector;*/

void
InterceptDeviceVector(dev)
struct Library *dev;
{
    Forbid();
    OldOpenVector = SetFunction((struct Library *)dev, LIB_OPEN, AsmOpenIntercept);
    OldCloseVector = SetFunction((struct Library *)dev, LIB_CLOSE, AsmCloseIntercept);
    /*OldBeginIOVector = SetFunction((struct Library *)dev, DEV_BEGINIO, A2232BeginIOFix);*/
    Permit();
}

void
RestoreDeviceVector(dev)
struct Library *dev;
{
    FPTR oldFunc;
    int error = 0;

    /*
     *	must restore in same order as had openned
     */

    for (;;) {
	Forbid();
	oldFunc = (FPTR)SetFunction((struct Library *)dev, LIB_OPEN, OldOpenVector);
	if (oldFunc != (FPTR)AsmOpenIntercept) {
	    SetFunction((struct Library *)dev, LIB_OPEN, oldFunc);
	    Permit();
	    if (error == 0) {
		error = 1;
		ulog(-1, "UNABLE TO REVERSE SETVECTOR, YOU MUST DEINSTALL");
		ulog(-1, "GETTYs ON THE SAME DEVICE IN REVERSE ORDER!");
	    }
	    Delay(50 * 5);
	    continue;
	}
	break;
    }
    SetFunction((struct Library *)dev, LIB_CLOSE, OldCloseVector);
    /*SetFunction((struct Library *)dev, DEV_BEGINIO, OldBeginIOVector);*/
    Permit();
}


