
/*
 *  SYSDEP.C
 *
 *  $Header: Beta:src/uucp/src/uucico/RCS/sysdep.c,v 1.1 90/02/02 11:56:16 dillon Exp Locker: dillon $
 *
 * (C) Copyright 1987 by John Gilmore
 * Copying and use of this program are controlled by the terms of the Free
 * Software Foundation's GNU Emacs General Public License.
 *
 *  Amiga Changes Copyright 1988 by William Loftus. All rights reserved.
 *  Additional chgs Copyright 1989 by Matthew Dillon, All Rights Reserved.
 */

#include <exec/types.h>
#include <exec/exec.h>
#include "includes.h"
#include <hardware/cia.h>
#include <devices/timer.h>
#include <devices/serial.h>
#include <libraries/dos.h>
#include <pwd.h>
#include "uucp.h"
#include "version.h"

#define Assert(x)   if (!(x)) _assert_failed(__LINE__);

Prototype int openout(char *, int);
Prototype int sigint(void);
Prototype void cleanup(void);
Prototype int xdatardy(void);
Prototype int xgetc(int);
Prototype int xwrite(const void *, int);
Prototype int xwritea(const void *, int);
Prototype int xxwrite(const void *, int, int);
Prototype void SendBreak(void);
Prototype int CheckCarrier(void);
Prototype void *bzero(void *, long);
Prototype void *bcopy(const void *, void *, long);
Prototype void munge_filename(char *, char *);
Prototype int hangup(void);
Prototype int work_scan(char *);
Prototype char *work_next(void);
Prototype void amiga_setup(void);
Prototype void set_baud(int);
Prototype void OpenSerial(int);
Prototype void CloseSerial(int);
Prototype void xexit(int);
Prototype void printc(unsigned char);

static void _assert_failed(int);

IDENT(".03");

/*
 *  Split out of uuslave.c by John Gilmore, 8 August 1987.
 *  ported to the Amiga by William Loftus, 20 September 1987.
 *  rewritten by Matthew Dillon, October 1989
 */

/* FIXME -- System dependent defines (not really -- should be in a .h) */
/*
 * Timeout for raw characters -- if we don't hear a char within BYTE_TIMEOUT
 * seconds, we assume the other side has gone away.  Has nothing to do with
 * retransmission timeouts (if any!).
 */

extern int debug;
extern int SevenWire;

#define FAILURE  1

struct IOExtSer Iosr;
struct IOExtSer Iosw;
struct IOExtSer Ioss;
struct timerequest Iot0;
char   *OldTaskName;

char	*DeviceName = "serial.device";
long	DeviceUnit = 0;

unsigned char	XInBuf[256];	   /*  for xgetc() */
char	LockFileName[128];
short	XInIdx = 0;
short	XInLen = 0;
short	IoswIP = 0;
short	IosrIP = 0;
short	IotIP = 0;

short	InExitRoutine = 0;
short	debugRead = 0;

extern char path[];
extern int Getty;
extern int IgnoreCD;

int
openout(acu, baud)
char *acu;
int  baud;
{
    set_baud(baud);

    return SUCCESS;
}

/*
 * Basement level I/O routines
 *
 * xwrite() writes a character string to the serial port
 * xgetc() returns a character from the serial port, or an EOF for timeout.
 * sigint() restores the state of the serial port on exit.
 */

int
sigint()
{
    xexit(2);
    return(0);
}


void
cleanup()
{
    xexit(0);
}

int
xdatardy()
{
    if (XInIdx != XInLen)
	return(1);
    CheckCarrier();
    return (Ioss.IOSer.io_Actual > 0);
}

int
xgetc(byteto)
int byteto;
{
    int ch, n;
    long smask;
    long tmask;
    short bytetimeout;

top:
    if (XInIdx != XInLen) {
	if (debug > 8) {
	    if (debugRead == 0)
		printf("\nREAD: ");
	    debugRead = 1;
	    printc(XInBuf[XInIdx]);
	}
	return((int)XInBuf[XInIdx++]);
    }

    XInIdx = 0;
    XInLen = 0;

    if (!CheckCarrier())                        /*  carrier lost?     */
	return(EOF);

    Assert(Iosr.IOSer.io_Device);

    if ((n = Ioss.IOSer.io_Actual) > 0) {       /*  at least one..    */
	Iosr.IOSer.io_Command = CMD_READ;
	Iosr.IOSer.io_Data = (APTR)XInBuf;
	if (n > sizeof(XInBuf))
	    n = sizeof(XInBuf);
	Iosr.IOSer.io_Length = n;
	DoIO(&Iosr);
	if (Iosr.IOSer.io_Actual > 0) {
	    if (debug > 8)
		printf("(r%d/%d)", n, Iosr.IOSer.io_Actual);
	    XInIdx = 0;
	    XInLen = Iosr.IOSer.io_Actual;
	    goto top;
	}
    }

    /*
     *	no bytes ready, byteto is 0 (no wait)
     */

    if (byteto == 0)
	return(EOF);

    /*
     *	no bytes ready, wait for one.
     *
     *	once every 3 seconds check carrier detect.
     */

    bytetimeout = byteto;
    Iot0.tr_time.tv_secs = 3;
    Iot0.tr_time.tv_micro= 0;
    SendIO(&Iot0);
    IotIP = 1;

    Iosr.IOSer.io_Command = CMD_READ;
    Iosr.IOSer.io_Data = (APTR)XInBuf;
    Iosr.IOSer.io_Length = 1;
    Iosr.IOSer.io_Actual = 0;	/*  trying to find a bug... */
    SendIO(&Iosr);
    IosrIP = 1;

    smask = 1L << Iosr.IOSer.io_Message.mn_ReplyPort->mp_SigBit;
    tmask = 1L << Iot0.tr_node.io_Message.mn_ReplyPort->mp_SigBit;

    for (;;) {
	long mask = Wait(tmask | smask | SIGBREAKF_CTRL_C);

	if (mask & SIGBREAKF_CTRL_C) {
	    AbortIO(&Iosr);
	    WaitIO(&Iosr);
	    IosrIP = 0;
	    AbortIO(&Iot0);
	    WaitIO(&Iot0);
	    IotIP = 0;
	    xexit(3);
	}
	if (CheckIO(&Iosr)) {
	    WaitIO(&Iosr);
	    IosrIP = 0;
	    AbortIO(&Iot0);
	    WaitIO(&Iot0);
	    IotIP = 0;

	    ch = (int)XInBuf[0];

	    if (debug > 8) {
		if (debugRead == 0)
		    printf("\nREAD ");
		debugRead = 1;
		printf("(waitc%d)", Iosr.IOSer.io_Actual);
		printc((unsigned char)ch);
	    }
	    return(ch);
	}
	if (CheckIO(&Iot0)) {
	    WaitIO(&Iot0);
	    IotIP = 0;

	    Iot0.tr_time.tv_secs = 3;
	    Iot0.tr_time.tv_micro= 0;

	    bytetimeout -= Iot0.tr_time.tv_secs;
	    if (bytetimeout > 0) {
		if (CheckCarrier() == 0) {
		    AbortIO(&Iosr);
		    WaitIO(&Iosr);
		    IosrIP = 0;
		    break;
		}
		SendIO(&Iot0);
		IotIP = 1;
	    } else {
		AbortIO(&Iosr);
		WaitIO(&Iosr);
		IosrIP = 0;
		if (Iosr.IOSer.io_Actual == 1)
		    return((int)XInBuf[0]);
		break;
	    }
	}
    }
    if (debug > 8)
	printf("\nRecv-EOF\n");
    return(EOF);
}

int
xwrite(buf, ctr)
const void *buf;
int ctr;
{
    return(xxwrite(buf, ctr, 0));
}

int
xwritea(buf, ctr)
const void *buf;
int ctr;
{
    xxwrite(buf, ctr, 1);
    return(ctr);
}

int
xxwrite(buf, ctr, async)
const void *buf;
int ctr;
int async;
{
    Assert(Iosr.IOSer.io_Device);
    if (debug > 8) {
	short i;
	if (debugRead)
	    printf("\nWRITE ");
	debugRead = 0;
	for (i = 0; i < ctr; ++i) {
	    printc(((unsigned char *)buf)[i]);
	}
	printf("\n");
    }
    if (IoswIP) {
	WaitIO(&Iosw);
	IoswIP = 0;
    }

    Iosw.IOSer.io_Command = CMD_WRITE;
    Iosw.IOSer.io_Length = ctr;
    Iosw.IOSer.io_Data = (APTR)buf;
    if (async) {
	SendIO(&Iosw);
	IoswIP = 1;
    } else {
	DoIO(&Iosw);
    }
    return ctr;
}

void
SendBreak()
{
    Assert(Iosr.IOSer.io_Device);
    Ioss.IOSer.io_Command = SDCMD_BREAK;
    DoIO(&Ioss);
}

int
CheckCarrier()
{
    Assert(Iosr.IOSer.io_Device);
    Ioss.IOSer.io_Command = SDCMD_QUERY;
    DoIO(&Ioss);
    if (IgnoreCD)
	return(1);
    if (Ioss.io_Status & CIAF_COMCD)    /*  non-zero == no carrier */
	return(0);
    return(1);
}

void *
bzero(s, cnt)
void   *s;
long   cnt;
{
    setmem(s, cnt, 0);
    return(s);
}

void *
bcopy(from, to, cnt)
const void   *from;
void   *to;
long   cnt;
{
    movmem(from, to, cnt);
    return(to);
}

/*
 * Transform a filename from a uucp packet (in Unix format) into a local
 * filename that will work in the local file system.
 */

void
munge_filename(s, d)
char *s, *d;
{
    if (*s != '~') {
	if (s != d)
	    strcpy(d, s);
	return;
    }

    /*
     *	~/ ...	convert to UUPUB:
     *	~user/...   convert to <homedir>/...
     */

    {
	short i;
	short c;
	char *t;
	struct passwd *pw;

	for (i = 1; s[i] && s[i] != '/'; ++i);
	c = s[i];

	s[i] = 0;
	if (i == 1)
	    pw = NULL;
	else
	    pw = getpwnam(s + 1);
	s[i] = c;

	if (c == '/')
	    ++i;

	if (pw) {
	    t = malloc(strlen(pw->pw_dir) + strlen(s + i) + 1);
	    strcpy(t, pw->pw_dir);
	} else {
	    t = malloc(strlen(s + i) + 32);
	    strcpy(t, GetConfigDir(UUPUB));
	}
	strcat(t, s + i);
	strcpy(d, t);
	free(t);
    }
}

int
hangup()
{
    static char buf[128];

    reset_modem();

    sprintf(buf, "run >nil: <nil: %s", GetConfigProgram(UUXQT));

    if (PriMode)
	SetTaskPri(FindTask(NULL), OldPri - 1);

    if (Execute(buf, NULL, NULL) == 0)
	puts("Unable to run UUXQT");

    if (PriMode)
	SetTaskPri(FindTask(NULL), OldPri + 1);

    return SUCCESS;
}

static char names[MAXFILES*16];
static char *pointers[MAXFILES];
static int file_pointer;

int
bp_strcmp(s1, s2)
char **s1;
char **s2;
{
    return(strcmp(*s1, *s2));
}

int
work_scan(system_name)
char *system_name;
{
    static char name[128];
    int count;

    file_pointer = 0;

    if (strlen(system_name) > 7)
	system_name[7] = '\0';

    sprintf(name, "%sC.%s#?", MakeConfigPath(UUSPOOL, ""), system_name);

    if (debug > 2)
	printf("Looking for %s\n",name);

    count = getfnl(name,names,sizeof(names),0);

    if (count > 0) {
	if (strbpl(pointers,MAXFILES,names) != count) {
	    printf("Too many command files for %s.\n",system_name);
	    return(0);
	}
    } else {
	return(0);
    }
    qsort(pointers, count, sizeof(char *), bp_strcmp);
    if (debug > 2)
	printf("Found -> %s\n", pointers[file_pointer]);
    return(1);
}

char *
work_next()
{
    char *ptr;

    if (ptr = pointers[file_pointer]) {
	if (debug > 2)
	    printf("Found -> %s\n", ptr);
	++file_pointer;
    }
    return(ptr);
}

void
amiga_setup()
{
    mountrequest(0);        /*  disallow requesters */

    OpenSerial(1);

    if (OpenDevice(TIMERNAME, UNIT_VBLANK, &Iot0, 0))  {
	Iot0.tr_node.io_Device = NULL;
	printf("Can't open timer device.");
	xexit(4);
    }

    Iot0.tr_node.io_Message.mn_ReplyPort = (struct MsgPort *)CreatePort("UUCICO-Timer", 0L);
    Iot0.tr_node.io_Command = TR_ADDREQUEST;
    Iot0.tr_node.io_Error = 0;

    {
	struct Task *task = (struct Task *)FindTask(NULL);
	OldTaskName = task->tc_Node.ln_Name;
	task->tc_Node.ln_Name = "uucico";
    }
}

void
set_baud(baud)
int baud;
{
    Assert(Iosr.IOSer.io_Device);

    /*
     *	only modify serial parameters if not run from a Getty.	This is
     *	to get around a bug in the A2232 serial.device (possibly also
     *	the normal serial.device) having to do with hanging semaphores.
     *
     *	it is also possible that this might be desirable for operation.
     */

    if (Getty == 0) {
	Iosr.IOSer.io_Command = SDCMD_SETPARAMS;
	Iosr.io_SerFlags =  SERF_SHARED | SERF_XDISABLED;
	Iosr.io_Baud = baud;
	Iosr.io_ReadLen = 8L;
	Iosr.io_WriteLen = 8L;
	Iosr.io_CtlChar = 0x11130000L;
	Iosr.io_RBufLen = 4096;

	if (SevenWire)
	    Iosr.io_SerFlags |= SERF_7WIRE;

	DoIO(&Iosr);
    }
}

void
OpenSerial(lock)
int lock;
{
    if (Iosr.IOSer.io_Device) {
	puts("softerror, OpenSerial: Serial Port already open!");
	return;
    }

    Iosr.io_SerFlags = SERF_SHARED | SERF_XDISABLED;
    Iosr.IOSer.io_Message.mn_ReplyPort = (struct MsgPort *)CreatePort("Read_RS",0);

    if (SevenWire)
	Iosr.io_SerFlags |= SERF_7WIRE;

    if (lock && Getty == 0)
	LockSerialPort(DeviceName, DeviceUnit);

    if (OpenDevice(DeviceName, DeviceUnit, &Iosr, NULL)) {
	Iosr.IOSer.io_Device = NULL;
	DeletePort(Iosr.IOSer.io_Message.mn_ReplyPort);
	Iosr.IOSer.io_Message.mn_ReplyPort = NULL;
	printf("Can not open serial port for read.\n");
	xexit(TRUE);
    }

    Iosw = Iosr;
    Iosw.IOSer.io_Message.mn_ReplyPort = (struct MsgPort *)CreatePort("Write_RS", 0);
    Ioss = Iosw;

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

    set_baud(Iosr.io_Baud);
}

void
CloseSerial(lock)
int lock;
{
    if (IosrIP) {
	AbortIO(&Iosr);
	WaitIO(&Iosr);
	IosrIP = 0;
    }
    if (IotIP) {
	AbortIO(&Iot0);
	WaitIO(&Iot0);
	IotIP = 0;
    }
    if (IoswIP) {
	WaitIO(&Iosw);
	IoswIP = 0;
    }
    if (Iosr.IOSer.io_Device) {
	CloseDevice(&Iosr);
	Iosr.IOSer.io_Device = NULL;
	Iosw.IOSer.io_Device = NULL;
	Ioss.IOSer.io_Device = NULL;
    } else {
	puts("softerr, CloseSerial: Serial Port already closed!");
    }
    if (Iosr.IOSer.io_Message.mn_ReplyPort) {
	DeletePort(Iosr.IOSer.io_Message.mn_ReplyPort);
	Iosr.IOSer.io_Message.mn_ReplyPort = NULL;
    }
    if (Iosw.IOSer.io_Message.mn_ReplyPort) {
	DeletePort(Iosw.IOSer.io_Message.mn_ReplyPort);
	Iosw.IOSer.io_Message.mn_ReplyPort = NULL;
    }
    if (lock && Getty == 0)
	UnLockSerialPort(DeviceName, DeviceUnit);
}

void
xexit(code)
int code;
{
    signal(SIGINT, SIG_IGN);

    ++InExitRoutine;

    if (InExitRoutine == 1 && code && Iosr.IOSer.io_Device && CheckCarrier())
	reset_modem();

    CloseSerial(1);

    {
	struct Task *task = (struct Task *)FindTask(NULL);
	if (OldTaskName)
	    task->tc_Node.ln_Name = OldTaskName;
    }

    if (Iot0.tr_node.io_Device) {
	CloseDevice(&Iot0);
	Iot0.tr_node.io_Device = NULL;
    }
    if (Iot0.tr_node.io_Message.mn_ReplyPort) {
	DeletePort(Iot0.tr_node.io_Message.mn_ReplyPort);
	Iot0.tr_node.io_Message.mn_ReplyPort = NULL;
    }
    chdir(path);
    if (code)
	printf("\nAbnormal Termination, code %d.\n", code);

    mountrequest(1);

    UnLockFiles();      /*  unlock any hanging locks */

    exit(code);
}

void
printc(c)
unsigned char c;
{
    c &= 0x7F;

    if (c < 32)
	printf("^%c", c | 0x40);
    else if (c == 32)
	printf("_");
    else if (c < 128)
	printf("%c", c);
    else
	printf("(%02x)", c);
}

void
_assert_failed(line)
{
    static short reent;

    if (reent == 0) {
	++reent;
	printf("Software Error line %d sysdep.c (uucico)\n", line);
	xexit(5);
    }
}

