home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
fifolib-38.3-src.tgz
/
tar.out
/
contrib
/
fifolib
/
fifo-handler.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-09-28
|
37KB
|
1,425 lines
/*
* FIFO-HANDLER.C V38.2
*
* Provide an interface to the fifo.library
* Provide remote shell support, including "*", interactive and 2.0 support.
*
* !!! MUST BE RUN, CANNOT USE MOUNT !!!
*
* FIFO:fifo_name/flags
* r for reading
* w for writing
* c cooked (else raw)
* e EOF on close (if a writer)
* k allows writer to close before reader opens without
* any data lost.
* K a reader MUST exist or write(s) will fail
* q QUIT (debugging)
* m master
* t tee off the read stream (no interference with other
* readers)
* s shell
*
* d debug mode (read by running 'RemCLI DBFifo')
* x exit debug mode
*/
#define CLI_START
#include "handler.h"
/* These packets are strictly internal to the handler */
#define FACTION_SCREEN_MODE ACTION_WRITE_RETURN
#define FACTION_WAIT_CHAR ACTION_READ_RETURN
#define ACTION_WAIT_TIMEOUT ACTION_TIMER
int myexit(int);
void Initialize(void);
void HandleRequestMsg(Message *);
void WaitMsg(Message *);
void SigHandles(char *, unsigned long);
void returnpacket(DosPacket *);
static inline char not_console(char *, unsigned char);
static inline short line1_length(FHan *);
void xprintf(char *, ...);
FHan *OpenHandle(char *, char *, long, long);
void CloseHandle(FHan *);
MsgPort *SpecPort(FHan *);
static inline void *DosAllocMem(long);
static inline void DosFree(void *);
void MkDevice(char *);
void DelDevice(void);
WaitTreq *AllocTimerequest(unsigned long timeout);
struct Library * FifoBase;
MsgPort *IoSink;
MsgPort *PktPort, *TimerPort;
long PortsMask; /* could be local ... */
#ifndef CLI_START
DeviceNode *DevNode;
#endif
WaitTreq *TimerIO;
List HanList;
#ifdef DEBUG
FifoHan DBFifo;
short DDebug;
#define D(x) if (DDebug) x ;
#define ebug xprintf
#else
#define D(x) ;
#endif
short Done; /* could be local, but DICE2.06 uses up a register */
const char verstring[] = "\0$VER: fifo-handler 38.3 (28.8.96)\r\n";
#ifdef DEBUG
#if defined(__GNUC__)
/* Do not use vsprintf from Libnix, it requires all of stdio.
* Builtin vsprintf has the advantage that htag.S can still be used.
*/
__asm(".even;_put1char:;"
"movel a3@,a0; moveb d0,a0@+; movel a0,a3@; rts");
static inline
int
romvsprintf(char *buf, char *ctl, void *args)
{
extern void put1char();
char *bufptr = buf;
RawDoFmt(ctl, args, put1char, &bufptr);
return bufptr - buf;
}
#define vsprintf romvsprintf
#define romvsprintf romvsprintf
#endif
void
xprintf(char *ctl, ...)
{
va_list va;
static char buf[256];
int n;
if (DBFifo) {
va_start(va, ctl);
n = vsprintf(buf, ctl, va);
if (n > 0)
WriteFifo(DBFifo, buf, n);
va_end(va);
}
}
#endif /* DEBUG */
static inline char
not_console(char *bas, unsigned char len)
{
short n = 7;
if (len == 1)
return ('*' - *bas);
if (len <= (unsigned char) n)
return n;
{
const char *console = "CONSOLE:";
char r1, r2;
while(!(r1=*console++ - (char)((r2=*bas++) >= 'a' ? r2 - ('a' - 'A') : r2))
/* && r2 */ && --n != -1) ;
return r1;
}
}
static inline short
line1_length(FHan *han)
{
short m = 0;
short n = han->ff_CookIdx;
if (n != 0) {
--n;
do { char c = han->ff_CookBuf[m];
if (c == CHAR_EOF) return m;
m++;
if (c == '\n') return m;
} while (--n != -1); /* make GCC generate dbra */
}
return -1;
}
#if !defined(__GNUC__)
__stkargs int /* changed from void (jch) */
_main(char *arg, long len)
#else
int _main(void) /* my special startup since libnix is >=2.0 */
#endif
{
DosPacket *packet;
NewList((MaxList *)&HanList);
Initialize();
if (FifoBase == NULL)
return myexit(RETURN_FAIL);
/*
* Main Loop
*/
while (!Done || HanList.mlh_TailPred != (Node *)&HanList) {
{
Message *msg;
while ((msg = GetMsg(IoSink)))
HandleRequestMsg(msg);
if ((msg = GetMsg(PktPort)) == NULL) {
/* handler depends on TimerPort being used last */
if ((msg = GetMsg(TimerPort)) == NULL) {
if (Wait(SIGS | PortsMask) & SIGBREAKF_CTRL_C)
Done = 1;
continue;
} else {
/* Got a timeout from timer.device */
packet = ((WaitTreq *)msg)->wt_packet;
((WaitTreq *)msg)->wt_packet = NULL; /* means timeout */
}
} else
packet = (DosPacket *)msg->mn_Node.ln_Name;
}
switch(packet->dp_Type) {
case ACTION_WRITE:
case FACTION_SCREEN_MODE:
case FACTION_WAIT_CHAR:
case ACTION_WAIT_TIMEOUT:
break; /* use dp_Res1 for temporary storage */
default:
packet->dp_Res1 = DOS_TRUE;
}
packet->dp_Res2 = DOS_FALSE;
D(ebug("packet %ld\n", packet->dp_Type));
switch(packet->dp_Type) {
case ACTION_DIE:
Done = 1;
break;
case ACTION_FINDUPDATE: /* FileHandle,Lock,Name Bool */
case ACTION_FINDINPUT: /* FileHandle,Lock,Name Bool */
case ACTION_FINDOUTPUT: /* FileHandle,Lock,Name Bool */
{
FileHandle *fh = BTOC(packet->dp_Arg1);
char fifo_name_m[128];
char fifo_name_s[128];
long han_flags = 0;
long opn_flags = FIFOF_NORMAL | FIFOF_NBIO;
short error = 0;
{
char *bas = BTOC(packet->dp_Arg3);
char *ptr;
unsigned char len = *(unsigned char *)bas++;
#if defined(DEBUG) && defined(romvsprintf)
/* RawDoFmt() does not understand the * flag */
if (DDebug) {
char format[18];
long args[2];
args[1] = args[0] = len;
romvsprintf(format, "open: %%%ld.%lds\n", args);
xprintf(format, bas);
}
#else
D(ebug("open: %*.*s\n", len, len, bas));
#endif
if (!not_console(bas, len)) { /* either "*" or "Console:" */
if (fh->fh_Port && (long)fh->fh_Port != -1) {
FHan *han;
/* assume IntCode() filled fh_Port with port */
/* ln_name was filled with FHan by SpecPort() */
han = (FHan *)fh->fh_Port->mp_Node.ln_Name;
++han->ff_Refs;
fh->fh_Arg1 = (LONG)han;
fh->fh_Port = (MsgPort *)DOS_TRUE; /* means interactive */
break;
}
/* fh->fh_Port = (MsgPort *)DOS_FALSE; */
packet->dp_Res2 = ERROR_OBJECT_NOT_FOUND;
break;
}
/* skip "fifo:" part */
for (ptr = bas; len && *ptr != ':'; ++ptr, --len) ;
if (len && *ptr == ':') {
bas = ptr + 1;
--len;
} else {
len = ptr - bas; /* restore len */
}
for (ptr = bas; len && *ptr != '/'; ++ptr, --len) ;
{
short i = ptr - bas;
if (/* i && */ i < 126) {
strncpy(fifo_name_m, bas, i);
strncpy(fifo_name_s, bas, i);
/* GCC can optimize these into MOVEB sequences */
strcpy(fifo_name_m + i, "_m");
strcpy(fifo_name_s + i, "_s");
} else
error = 1; /* nevertheless get flags */
}
/* get flags */
if (len && *ptr == '/') {
unsigned long sigs = 0;
for (++ptr, --len; len; ++ptr, --len) {
switch(*ptr) {
case 'q':
Done = 1;
break;
case 'e':
han_flags |= FHF_CLOSEEOF;
break;
case 'c':
han_flags |= FHF_COOKED|FHF_STARTCOOK;
break;
case 's':
han_flags |= FHF_SHELL;
break;
case 'r':
han_flags |= FHF_READ;
break;
case 'w':
han_flags |= FHF_WRITE;
break;
case 'k':
opn_flags |= FIFOF_KEEPIFD;
break;
case 'K':
opn_flags |= FIFOF_RREQUIRED;
break;
case 'm':
han_flags |= FHF_MASTER;
break;
case 't':
han_flags |= FHF_TEE;
break;
case 'd':
#ifdef DEBUG
if (DDebug == 0) {
DDebug = 1;
DBFifo = OpenFifo("DBFifo_s", 1024, FIFOF_WRITE | FIFOF_NORMAL);
}
#endif
break;
case 'x':
#ifdef DEBUG
if (DDebug) {
DDebug = 0;
if (DBFifo)
CloseFifo(DBFifo, FIFOF_EOF);
DBFifo = NULL;
}
#endif
break;
case 'C':
sigs |= SIGBREAKF_CTRL_C;
break;
case 'D':
sigs |= SIGBREAKF_CTRL_D;
break;
case 'E':
sigs |= SIGBREAKF_CTRL_E;
break;
case 'F':
sigs |= SIGBREAKF_CTRL_F;
break;
default:
error = 1;
break;
}
}
if (sigs)
SigHandles((han_flags & FHF_MASTER)
? fifo_name_m : fifo_name_s, sigs);
}
}
if (((han_flags & FHF_COOKED) &&
((han_flags & FHF_MASTER) || /* as documented */
!(han_flags & FHF_READ))) /* necessary */
|| !(han_flags & (FHF_READ | FHF_WRITE)))
error = 1;
/*
* To handle the stderr channel, "*", an interactive
* shell must be opened with the 's' flag, which causes
* a special message port to be created for the interactive
* shell (and thus the pr_ConsoleTask field)
*
* To handle COOKED data mode, FIFO: itself must processed
* received data before the slave device, echoing it back
* on the master channel and doing other cooked processing.
*/
if (!error) {
FHan *han;
if (NULL != (han = OpenHandle(fifo_name_s, fifo_name_m, han_flags, opn_flags))) {
if (han_flags & FHF_SHELL) {
fh->fh_Type = han->ff_Port; /* special port */
}
fh->fh_Arg1 = (LONG)han;
fh->fh_Port = (MsgPort *)DOS_TRUE; /* means interactive */
han->ff_SigPort = packet->dp_Port; /* for SIGBREAKF_CTRL_* */
break;
} else {
packet->dp_Res2 = ERROR_NO_FREE_STORE;
break;
}
}
packet->dp_Res2 = ERROR_BAD_STREAM_NAME;
}
break;
case ACTION_READ: /* FHArg1,CPTRBuffer,Length ActLength */
/*
* read from fifo. If we are in cooked mode this action is
* only able to read from the cooked buffer, and then only
* if a line is complete.
*/
{
FHan *han = (FHan *)packet->dp_Arg1;
long n;
if (!(han->ff_Flags & FHF_READ)) {
packet->dp_Res1 = -1;
packet->dp_Res2 = ERROR_READ_PROTECTED;
returnpacket(packet); /* or loose dp_Res1 */
packet = NULL;
break;
}
if (han->ff_Flags & FHF_REOF) {
han->ff_Flags &= ~FHF_REOF;
packet->dp_Res1 = 0;
break;
}
han->ff_Flags &= ~FHF_STARTCOOK; /* restarted below */
if (han->ff_CookBuf
&& ((han->ff_Flags & FHF_COOKED)
? (0 <= han->ff_CookLen1)
: han->ff_CookIdx)) {
/*
* cooked input is available
* do not read too much in raw mode, a line at a time
* this code will fail for dp_Arg3 (# to read) == 0 in raw mode
*/
n = han->ff_CookLen1; /* 0 means EOF */
D(ebug("Can read %ld chars\n", n));
if (n > packet->dp_Arg3)
n = packet->dp_Arg3;
{
long m = n;
/*
* CON: sends EOF in raw mode for a cooked EOF read
* I choose to transmit ctrl-\ because FIFO: is
* likely to receive pre-"typed", non-manual input
*/
if (m == 0 && !(han->ff_Flags & FHF_COOKED)) m = 1;
packet->dp_Res1 = m;
memmove((void *)packet->dp_Arg2, han->ff_CookBuf, m);
}
if (n == 0) n = 1; /* skip EOF marker */
han->ff_CookIdx -= n;
/* overlapping memory! */
memmove(han->ff_CookBuf, han->ff_CookBuf + n, han->ff_CookIdx);
if (n < han->ff_CookLen1) {
han->ff_CookLen1 -= n;
} else {
han->ff_CookLen1 = line1_length(han);
}
/*
* if we blocked on reading the fifo to cook more
* data, we unblock it here.
*/
han->ff_Flags &= ~FHF_COOKBFUL;
if (han->ff_Flags & FHF_COOKED &&
!(han->ff_Flags & FHF_RPEND)) {
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_RPEND);
han->ff_Flags |= FHF_RPEND;
}
break;
}
if (!(han->ff_Flags & FHF_COOKED)) {
char *ptr;
n = ReadFifo(han->ff_FifoR, &ptr, 0);
D(ebug("%ld chars available\n", n));
if (n < 0 /* || (n == 0 && (han->ff_Flags & FHF_REOF)--impossible)*/) {
han->ff_Flags &= ~FHF_REOF;
packet->dp_Res1 = 0;
break;
}
if (n > 0) {
/* Hans Verkuil introduced a patch to only read upto \n,
* however RAW: does not exhibit this behaviour.
*/
if (n > packet->dp_Arg3)
n = packet->dp_Arg3;
CopyMem(ptr, (void *)packet->dp_Arg2, n);
packet->dp_Res1 = n;
if (ReadFifo(han->ff_FifoR, &ptr, n) < 0)
han->ff_Flags |= FHF_REOF;
/*
* Concatenating data when last result is > 0 gives
* not much, as with the way packets are sequenced
* here, it is usually only possible once when
* unblocking from a full buffer.
*/
break;
}
}
/*
* blocked
*/
AddTail((MaxList *)&han->ff_RdWait, &packet->dp_Link->mn_Node);
if ((han->ff_Flags & FHF_RPEND) == 0) {
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_RPEND);
han->ff_Flags |= FHF_RPEND;
}
packet = NULL;
}
break;
case ACTION_WRITE: /* FHArg1,CPTRBuffer,Length ActLength */
{
FHan *han = (FHan *)packet->dp_Arg1;
long n;
long i;
if (!(han->ff_Flags & FHF_WRITE)) {
packet->dp_Res1 = -1;
packet->dp_Res2 = ERROR_WRITE_PROTECTED;
returnpacket(packet); /* or loose dp_Res1 */
packet = NULL;
break;
}
i = packet->dp_Arg3;
if (i < 0) { /* re-scan */
i = -i;
packet->dp_Arg3 = i;
} else { /* initial pkt */
packet->dp_Res1 = 0;
}
/*
* check for output stopped due to pending input line
*
* dp_Arg3 < 0 indicates a re-scan (so we do not clear
* our dp_Res1 field that is tracking the amnt written)
*/
if ((han->ff_Flags & FHF_COOKED) && (han->ff_Flags & FHF_WISTOP)) {
packet->dp_Arg3 = -packet->dp_Arg3;
AddTail((MaxList *)&han->ff_WrWait, &packet->dp_Link->mn_Node);
han->ff_Flags |= FHF_WIHOLD;
packet = NULL;
break;
}
/*
* limit size of writes to fifo to something the fifo can
* handle. If cooked mode writer, prepend CR to LF's.
*/
if (i > han->ff_FHBufSiz)
i = han->ff_FHBufSiz;
if (han->ff_Flags & FHF_COOKED) {
char *ptr = (char *)packet->dp_Arg2;
long j;
/* n is always initialized because i >= 0 */
for (j = 0; j < i; ++j) {
if (ptr[j] == '\n') {
if (j == 0) {
n = WriteFifo(han->ff_FifoW, "\r\n", 2);
if (n == 2)
n = 1; /* skip LF */
break;
}
n = WriteFifo(han->ff_FifoW, ptr, j);
break;
}
}
if (i == j)
n = WriteFifo(han->ff_FifoW, ptr, i);
} else
n = WriteFifo(han->ff_FifoW, (char *)packet->dp_Arg2, i);
/*
* object too large or broken pipe
*/
if (n < 0) { /* most probably for FIFO_RREQUIRED */
packet->dp_Res1 = -1;
packet->dp_Res2 = (n == -2)
? ERROR_OBJECT_TOO_LARGE /* can this really happen? */
: ERROR_WRITE_PROTECTED;
returnpacket(packet); /* or loose dp_Res1 */
packet = NULL;
break;
}
packet->dp_Res1 += n;
if (n == packet->dp_Arg3)
break;
packet->dp_Arg3 = -(packet->dp_Arg3 - n);
packet->dp_Arg2 += n;
/*
* blocked (n == 0)
* or splitted
*/
AddTail((MaxList *)&han->ff_WrWait, &packet->dp_Link->mn_Node);
if ((han->ff_Flags & FHF_WAVAIL) == 0) {
RequestFifo(han->ff_FifoW, &han->ff_WrMsg, FREQ_WAVAIL);
han->ff_Flags |= FHF_WAVAIL;
}
packet = NULL;
}
break;
case ACTION_REQUEST: /* FHArg1, msg, how Bool */
{
FHan *han = (FHan *)packet->dp_Arg1;
if ((unsigned short)packet->dp_Arg3 == FREQ_RPEND) {
if (han->ff_FifoR) {
RequestFifo(han->ff_FifoR, (void *)packet->dp_Arg2, packet->dp_Arg3);
break;
}
} else if ((unsigned short)packet->dp_Arg3 == FREQ_WAVAIL) {
if (han->ff_FifoW) {
RequestFifo(han->ff_FifoW, (void *)packet->dp_Arg2, packet->dp_Arg3);
break;
}
} else if ((unsigned short)packet->dp_Arg3 == FREQ_ABORT) {
if (han->ff_FifoR)
RequestFifo(han->ff_FifoR, (void *)packet->dp_Arg2, packet->dp_Arg3);
if (han->ff_FifoW)
RequestFifo(han->ff_FifoW, (void *)packet->dp_Arg2, packet->dp_Arg3);
break;
}
packet->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
}
break;
case ACTION_END: /* FHArg1 Bool:TRUE */
{
FHan *han = (FHan *)packet->dp_Arg1;
if (--han->ff_Refs == 0) {
if (han->ff_Flags & FHF_RPEND) {
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_ABORT);
WaitMsg(&han->ff_RdMsg);
}
if (han->ff_Flags & FHF_WAVAIL) {
RequestFifo(han->ff_FifoW, &han->ff_WrMsg, FREQ_ABORT);
WaitMsg(&han->ff_WrMsg);
}
/* no need to do bookkeeping for ACTION_WAIT_CHAR */
returnpacket(packet); /* before ff_Port goes away */
packet = NULL;
CloseHandle(han);
}
}
break;
/* Added for 37.5 and 38.0 */
case FACTION_WAIT_CHAR: /* timeout Bool */
/*
* as ACTION_WAIT_CHAR does not give a filehandle, we get it
* indirectly through the special port. Fold packet type for safety.
*/
packet->dp_Type = ACTION_WAIT_CHAR;
{
FHan *han = (FHan *) (((MsgPort *) packet->dp_Res1)->mp_Node.ln_Name);
unsigned long timeout = packet->dp_Arg1;
WaitTreq *treq;
if (!(han->ff_Flags & FHF_READ)) {
packet->dp_Res2 = ERROR_READ_PROTECTED;
break;
}
if (han->ff_Flags & FHF_REOF) {
packet->dp_Res1 = DOS_TRUE;
break;
}
if (han->ff_Flags & FHF_COOKED) {
if (0 <= han->ff_CookLen1) {
packet->dp_Res1 = DOS_TRUE;
break;
}
} else {
char *ptr;
if ((han->ff_CookBuf && han->ff_CookIdx)
/* cooked input is available */
|| ReadFifo(han->ff_FifoR, &ptr, 0)) {
packet->dp_Res1 = DOS_TRUE;
break;
}
}
/* always queue if cooked processing must be started */
if (timeout <= 50 /* microseconds */ && !(han->ff_Flags & FHF_STARTCOOK)) {
packet->dp_Res1 = DOS_FALSE;
break;
}
/*
* queue packet
*/
if (NULL != (treq = AllocTimerequest(timeout))) {
treq->wt_packet = packet;
packet->dp_Type = ACTION_WAIT_TIMEOUT;
packet->dp_Res1 = (LONG)treq;
han->ff_Flags &= ~FHF_STARTCOOK;
AddTail((MaxList *)&han->ff_RdWait, &packet->dp_Link->mn_Node);
if (!(han->ff_Flags & FHF_RPEND)) {
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_RPEND);
han->ff_Flags |= FHF_RPEND;
}
SendIO(&treq->wt_timereq.tr_node);
packet = NULL;
break;
} else
packet->dp_Res2 = ERROR_NO_FREE_STORE;
}
break;
case ACTION_WAIT_TIMEOUT:
packet->dp_Type = ACTION_WAIT_CHAR;
if (((WaitTreq *)packet->dp_Res1)->wt_packet) {
/* input is available */
struct IORequest *ior = (struct IORequest *)packet->dp_Res1;
if (!CheckIO(ior))
AbortIO(ior);
WaitIO(ior);
FreeMem((void *)ior, sizeof(WaitTreq));
packet->dp_Res1 = DOS_TRUE;
} else {
/* timer.device timeout */
/*
* we know that node is still in han->ff_RdWait
* because TimerPort is checked last
*/
Remove(&packet->dp_Link->mn_Node);
/* no need to try to abort the read request */
FreeMem((void *)packet->dp_Res1, sizeof(WaitTreq));
packet->dp_Res1 = DOS_FALSE;
}
break;
case FACTION_SCREEN_MODE: /* Mode Bool:TRUE */
/* As ACTION_SCREEN_MODE does not give a filehandle, we get it
* indirectly through the special port. Fold packet type for safety.
*/
packet->dp_Type = ACTION_SCREEN_MODE;
D(ebug("SCREEN_MODE %ld\n", packet->dp_Arg1));
{
/* special port passed in dp->Res1 */
FHan *han = (FHan *)(((MsgPort *)packet->dp_Res1)->mp_Node.ln_Name);
if (han->ff_CookBuf == NULL) { /* also NULL when !FHF_READ */
/* Normally a shell uses cooked mode and programs might
* also set cooked mode at exit. Thus if the
* application did not open FIFO: in cooked mode, it
* had good reasons to do so and we refuse any change.
* At least this is good for GNUEmacs (uses "rwesK").
*/
packet->dp_Res2 = ERROR_OBJECT_WRONG_TYPE;
break;
}
packet->dp_Res1 = DOS_TRUE;
if (DOSFALSE != packet->dp_Arg1) { /* RAW */
if (han->ff_Flags & FHF_COOKED) {
/*
* this differs from CON: which unblocks neither
* readers nor writers until the next character is read
*/
han->ff_Flags &= ~(FHF_COOKED|FHF_STARTCOOK|FHF_WIHOLD|FHF_WISTOP|FHF_COOKECHOBLK /*|FHF_COOKBFUL --still full */);
/* cooked input is available, unblock readers and timers */
if (han->ff_CookIdx && (han->ff_Flags & FHF_RPEND)) {
D(ebug("Restarting readers\n"));
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_ABORT);
/* han->ff_Flags |= FHF_RPEND; --already set */
}
/* unblock writers probably in FHF_WIHOLD */
if ((han->ff_Flags & FHF_WRITE) && !(han->ff_Flags & FHF_WAVAIL)) {
RequestFifo(han->ff_FifoW, &han->ff_WrMsg, FREQ_WAVAIL);
han->ff_Flags |= FHF_WAVAIL;
}
}
} else { /* COOKED */
if (!(han->ff_Flags & FHF_COOKED)) {
/* but ff->CookBuf is NULL if not opened with 'c' flag! */
han->ff_Flags |= FHF_COOKED|FHF_STARTCOOK;
/* cooked processing depends on a read request being active. */
/* Since 38.2, cooked processing is only started when
* ACTION_READ and ACTION_WAIT_CHAR actually occur, thus
* pretyped input is not mangled should the application
* switch to raw mode before reading
* (like GDB when controlled by dejagnu+expect)
*
if ((han->ff_Flags & FHF_READ) && !(han->ff_Flags & FHF_RPEND)) {
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_RPEND);
han->ff_Flags |= FHF_RPEND;
}
*/
}
}
}
break;
/* Added for 38.1 */
case ACTION_CHANGE_SIGNAL: /* Filehandle,port|NULL Bool,port */
{
FHan *han = (FHan *)packet->dp_Arg1;
packet->dp_Res2 = (LONG)han->ff_SigPort;
D(ebug("Signal Port change %08lx\n", packet->dp_Arg2));
if (packet->dp_Arg2)
han->ff_SigPort = (MsgPort *)packet->dp_Arg2;
returnpacket(packet); /* or loose dp_Res1 */
packet = NULL;
}
break;
/* Added for 38.1 */
case ACTION_IS_FILESYSTEM: /* Bool */
packet->dp_Res1 = DOS_FALSE;
break;
/* Added for 38.1 */
case ACTION_SEEK: /* FHArg1,Position,Mode OldPosition */
packet->dp_Res1 = -1; /* do not return 0 */
packet->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
returnpacket(packet); /* or loose dp_Res1 */
packet = NULL;
break;
default:
D(ebug("UNKNOWN packet\nArg1 %08lx Arg2 %08lx\n",
packet->dp_Arg1, packet->dp_Arg2));
packet->dp_Res2 = ERROR_ACTION_NOT_KNOWN;
break;
}
if (packet) {
if (packet->dp_Res2)
packet->dp_Res1 = DOS_FALSE;
returnpacket(packet);
}
}
return myexit(RETURN_OK);
}
FHan *
OpenHandle(r_name, w_name, han_flags, opn_flags)
char *r_name; /* slave name */
char *w_name; /* master name */
long han_flags;
long opn_flags;
{
FHan *han;
if (NULL == (han = (FHan *)AllocMem(sizeof(FHan) + strlen(r_name) +1,
MEMF_CLEAR | MEMF_PUBLIC)))
return NULL;
if (!(han_flags & FHF_MASTER)) {
char *swap = r_name;
r_name = w_name;
w_name = swap;
}
han->ff_Node.ln_Name = (char *)(han + 1);
strcpy(han->ff_Node.ln_Name, w_name);
if ((han_flags & FHF_TEE) == 0 && (han_flags & FHF_READ)) {
FHan *h2;
for (h2 = (FHan *)HanList.mlh_Head; h2->ff_Node.ln_Succ; h2 = (FHan *)h2->ff_Node.ln_Succ) {
if (strcmp(h2->ff_Node.ln_Name, han->ff_Node.ln_Name) == 0) {
if ((h2->ff_Flags & FHF_TEE) == 0 && h2->ff_SRead) {
han->ff_SRead = h2->ff_SRead;
++han->ff_SRead->sr_Refs;
break;
}
}
}
}
AddTail((MaxList *)&HanList, &han->ff_Node);
if ((han_flags & FHF_READ) && han->ff_SRead == NULL) {
if (NULL == (han->ff_SRead = AllocMem(sizeof(SharRead), MEMF_CLEAR | MEMF_PUBLIC)))
goto fail;
han->ff_SRead->sr_Refs = 1;
if (NULL == (han->ff_SRead->sr_FifoR = OpenFifo(r_name, FIFO_SIZE, opn_flags | FIFOF_READ)))
goto fail;
}
if (han_flags & FHF_WRITE) {
if (NULL == (han->ff_FifoW = OpenFifo(w_name, FIFO_SIZE, opn_flags | FIFOF_WRITE)))
goto fail;
han->ff_FHBufSiz = (BufSizeFifo(han->ff_FifoW) >> 1) -1;
}
if (han_flags & FHF_SHELL) {
if (NULL == (han->ff_Port = SpecPort(han)))
goto fail;
}
han->ff_Flags = han_flags;
han->ff_Refs = 1;
han->ff_RdMsg.mn_ReplyPort = IoSink;
han->ff_RdMsg.mn_Node.ln_Name = (char *)han;
han->ff_WrMsg.mn_ReplyPort = IoSink;
han->ff_WrMsg.mn_Node.ln_Name = (char *)han;
NewList((MaxList *)&han->ff_RdWait);
NewList((MaxList *)&han->ff_WrWait);
if (han_flags & FHF_COOKED) {
/* allocation need not be public, fifo-handler use only */
if (NULL == (han->ff_CookBuf = AllocMem(CB_SIZE, MEMF_CLEAR)))
goto fail;
han->ff_CookLen1 = -1;
/* lookahead for efficiency */
/* Don't start cooked processing, application may switch to raw mode
*
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_RPEND);
han->ff_Flags |= FHF_RPEND;
*/
}
return han;
fail:
CloseHandle(han);
return NULL;
}
void
CloseHandle(han)
FHan *han;
{
/* zero fields to protect against buggy programs */
Remove(&han->ff_Node);
if (han->ff_SRead) {
if (--han->ff_SRead->sr_Refs == 0) {
if (han->ff_FifoR) {
CloseFifo(han->ff_FifoR, 0);
/* han->ff_FifoR = NULL; */
}
FreeMem(han->ff_SRead, sizeof(SharRead));
}
han->ff_SRead = NULL;
}
if (han->ff_FifoW) {
CloseFifo(han->ff_FifoW, (han->ff_Flags & FHF_CLOSEEOF) ? FIFOF_EOF : 0);
han->ff_FifoW = NULL;
}
if (han->ff_Port) {
FreeMem(han->ff_Port, sizeof(MsgPort) + sizeof(Interrupt));
han->ff_Port = NULL;
}
if (han->ff_CookBuf) {
FreeMem(han->ff_CookBuf, CB_SIZE);
han->ff_CookBuf = NULL;
}
FreeMem(han, sizeof(FHan) + strlen(han->ff_Node.ln_Name) + 1);
}
/*
* handle cooked data by actually reading it from the fifo, echoing it
* to the return channel (if it exists), and processing it. If a <CR>
* is processed, handle any
*/
void
HandleRequestMsg(Message *msg)
{
FHan *han = (FHan *)msg->mn_Node.ln_Name;
if (msg == &han->ff_WrMsg) {
han->ff_Flags &= ~FHF_WAVAIL;
while ((msg = (Message *)RemHead((MaxList *)&han->ff_WrWait))) /* retry operation */
PutMsg(PktPort, msg);
/*
* if we were blocked trying to echo, then read data pending,
* make sure read-request is queued and will be retried.
*/
if (han->ff_Flags & FHF_COOKECHOBLK) {
if ((han->ff_Flags & FHF_RPEND) == 0) {
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_RPEND);
han->ff_Flags |= FHF_RPEND;
}
han->ff_Flags &= ~FHF_COOKECHOBLK;
}
} else if (msg == &han->ff_RdMsg) {
han->ff_Flags &= ~FHF_RPEND;
if (han->ff_Flags & FHF_COOKED) {
long n;
long i;
short rwakeup = 0;
short eof = 0;
char *ptr;
n = ReadFifo(han->ff_FifoR, &ptr, 0);
if (n < 0) {
han->ff_Flags |= FHF_REOF;
rwakeup = 1;
}
for (i = 0; i < n; ++i) {
switch(ptr[i]) {
case 28: /* 28 is ctrl-\ is EOF */
eof = 1;
/* special marker. Fall through to save some code */
case 13:
case 10:
if (han->ff_CookIdx >= CB_SIZE - 1) {
han->ff_Flags |= FHF_COOKBFUL;
n = --i;
break;
}
if (han->ff_FifoW) {
if (WriteFifo(han->ff_FifoW, "\r\n", 2) != 2) {
han->ff_Flags |= FHF_COOKECHOBLK;
n = --i;
break;
}
}
han->ff_CookBuf[han->ff_CookIdx++] = (eof ? CHAR_EOF : '\n');
if (0 > han->ff_CookLen1) {
/* first line is ready, include \n but don't count EOF */
han->ff_CookLen1 = han->ff_CookIdx - eof;
}
han->ff_Flags &= ~FHF_WISTOP;
eof = 0;
rwakeup = 1;
break;
case 8:
if (han->ff_CookIdx
&& han->ff_CookBuf[han->ff_CookIdx-1] != '\n'
&& han->ff_CookBuf[han->ff_CookIdx-1] != CHAR_EOF) {
if (han->ff_FifoW) {
if (WriteFifo(han->ff_FifoW, "\010 \010", 3) != 3) {
han->ff_Flags |= FHF_COOKECHOBLK;
n = --i;
break;
}
}
--han->ff_CookIdx;
if (han->ff_CookIdx==0
|| han->ff_CookBuf[han->ff_CookIdx-1] == '\n'
|| han->ff_CookBuf[han->ff_CookIdx-1] == CHAR_EOF)
han->ff_Flags &= ~FHF_WISTOP;
}
break;
#if 0
case 26: /* test signals */
SigHandles(han->ff_Node.ln_Name, SIGBREAKF_CTRL_C);
break;
#endif
#if SIGBREAKB_CTRL_F - SIGBREAKB_CTRL_C == 3
case 3: case 4: case 5: case 6:
SigHandles(han->ff_Node.ln_Name, 1L<<(ptr[i]-3+SIGBREAKB_CTRL_C));
break;
#else
/* who says the above is not portable? */
case 3:
SigHandles(han->ff_Node.ln_Name, SIGBREAKF_CTRL_C);
break;
case 4:
SigHandles(han->ff_Node.ln_Name, SIGBREAKF_CTRL_D);
break;
case 5:
SigHandles(han->ff_Node.ln_Name, SIGBREAKF_CTRL_E);
break;
case 6:
SigHandles(han->ff_Node.ln_Name, SIGBREAKF_CTRL_F);
break;
#endif
default:
if (han->ff_CookIdx >= CB_SIZE - 1) {
han->ff_Flags |= FHF_COOKBFUL;
n = --i;
break;
}
if (han->ff_FifoW) {
if (WriteFifo(han->ff_FifoW, ptr + i, 1) != 1) {
han->ff_Flags |= FHF_COOKECHOBLK;
n = --i;
break;
}
}
han->ff_CookBuf[han->ff_CookIdx++] = ptr[i];
han->ff_Flags |= FHF_WISTOP;
break;
}
}
/*
* if output was held due to cooked input pending, and the
* case is no longer true, then restart output
*/
if ((han->ff_Flags & FHF_WIHOLD) && !(han->ff_Flags & FHF_WISTOP)) {
han->ff_Flags &= ~FHF_WIHOLD;
while ((msg = (Message *)RemHead((MaxList *)&han->ff_WrWait)))
PutMsg(PktPort, msg);
}
if (i > 0) {
if (ReadFifo(han->ff_FifoR, &ptr, i) < 0) {
han->ff_Flags |= FHF_REOF;
rwakeup = 1;
}
}
if (n >= 0 && !(han->ff_Flags & (FHF_COOKECHOBLK|FHF_COOKBFUL|FHF_REOF))) {
RequestFifo(han->ff_FifoR, &han->ff_RdMsg, FREQ_RPEND);
han->ff_Flags |= FHF_RPEND;
}
if (!rwakeup)
return;
}
while ((msg = (Message *)RemHead((MaxList *)&han->ff_RdWait))) /* retry operation */
PutMsg(PktPort, msg);
}
}
void
WaitMsg(Message *msg)
{
while (msg->mn_Node.ln_Type == NT_MESSAGE)
Wait(1 << msg->mn_ReplyPort->mp_SigBit);
Forbid();
Remove(&msg->mn_Node);
Permit();
}
void
SigHandles(char *name, unsigned long sigs)
{
FHan *han;
for (han = (FHan *)HanList.mlh_Head; han->ff_Node.ln_Succ; han = (FHan *)han->ff_Node.ln_Succ) {
if (strcmp(han->ff_Node.ln_Name, name) == 0) {
MsgPort *mp = han->ff_SigPort;
D(ebug("Maybe signal port %08lx %s\n", mp, name));
if ((mp->mp_Flags & PF_ACTION) == PA_SIGNAL)
Signal(mp->mp_SigTask, sigs);
}
}
}
/*
* PACKET ROUTINES. Dos Packets are in a rather strange format as you
* can see by this and how the PACKET structure is extracted in the
* GetMsg() of the main routine.
*/
void
returnpacket(DosPacket *packet)
{
MsgPort *replyPort = packet->dp_Port;
Message *mess = packet->dp_Link;
packet->dp_Port = PktPort; /* not possibly special port? */
mess->mn_Node.ln_Name = (char *)packet;
D(ebug("Return type=%ld, res1=%ld, res2=%ld\n", packet->dp_Type, packet->dp_Res1, packet->dp_Res2));
PutMsg(replyPort, mess);
}
void
Initialize(void)
{
#ifndef CLI_START
Process *proc = FindTask(NULL);
DosPacket *packet;
#endif
/*
* Initialize port
*/
{
IoSink = CreatePort("FIFO-PORT", -10);
FreeSignal(IoSink->mp_SigBit);
IoSink->mp_SigBit = SIGBREAKB_CTRL_F; /* why? (jch) */
}
PktPort = CreatePort(NULL, 0);
TimerPort = CreatePort(NULL, 0);
TimerIO = (WaitTreq *) CreateExtIO(TimerPort, sizeof(WaitTreq));
OpenDevice(TIMERNAME, UNIT_MICROHZ, (struct IORequest *)TimerIO, 0);
TimerIO->wt_timereq.tr_node.io_Command = TR_ADDREQUEST;
PortsMask = (1 << PktPort->mp_SigBit) | (1 << TimerPort->mp_SigBit);
#ifdef CLI_START
/*
* create DOS node
*/
MkDevice("FIFO");
FifoBase = OpenLibrary(FIFONAME, 0);
#else
/*
* Handle initial message. We pull the message off the port before
* calling OpenLibrary() so if OpenLibrary() makes a DOS call it
* doesn't crash the machine (due to an unexpected packet). This
* will happen if the library needs to be loaded. There is no other
* safe time to do this.
*/
{
Message *msg;
WaitPort(&proc->pr_MsgPort);
msg = GetMsg(&proc->pr_MsgPort);
packet = (DosPacket *)msg->mn_Node.ln_Name;
}
/*
* Fifo Library
*/
FifoBase = OpenLibrary(FIFONAME, 0);
{
DeviceNode *dn;
DevNode = dn = BTOC(packet->dp_Arg3);
dn->dn_Task = PktPort;
packet->dp_Res1 = (FifoBase) ? DOS_TRUE : DOS_FALSE;
packet->dp_Res2 = 0;
returnpacket(packet);
}
#endif
}
int
myexit(int code)
{
#ifdef CLI_START
DelDevice();
#else
/*
* Device Node
*/
{
DeviceNode *dn = DevNode;
dn->dn_Task = NULL;
dn->dn_SegList = NULL;
}
#endif
/*
* delete ports
*/
CloseDevice((struct IORequest *) TimerIO);
DeleteExtIO((struct IORequest *) TimerIO);
DeletePort(TimerPort);
if (PktPort)
DeletePort(PktPort);
if (IoSink) {
IoSink->mp_SigBit = AllocSignal(-1);
DeletePort(IoSink);
}
#ifdef DEBUG
if (DBFifo) {
CloseFifo(DBFifo, FIFOF_EOF);
}
#endif
if (FifoBase) {
CloseLibrary(FifoBase);
/* FifoBase = NULL; */
}
_exit(code); /* some startups always return 0 from main */
return code;
}
MsgPort *
SpecPort(FHan *han)
{
MsgPort *port = AllocMem(sizeof(MsgPort) + sizeof(Interrupt), MEMF_CLEAR | MEMF_PUBLIC);
Interrupt *xint = (Interrupt *)(port + 1);
extern void AIntCode();
if (NULL == port) return NULL;
NewList(&port->mp_MsgList);
port->mp_Node.ln_Name = (char *)han; /* ln_name holds local data */
port->mp_Node.ln_Type = NT_MSGPORT;
port->mp_Flags = PA_SOFTINT;
port->mp_SigTask = (void *)xint;
xint->is_Node.ln_Type = NT_INTERRUPT;
xint->is_Node.ln_Pri = -32;
xint->is_Data = (APTR)port;
xint->is_Code = AIntCode;
return(port);
}
#if !defined(__GNUC__)
__geta4 __stkargs
#endif
void
IntCode(port)
MsgPort *port;
{
Message *msg;
DosPacket *packet;
FileHandle *fh;
while ((msg = GetMsg(port))) {
packet = (DosPacket *)msg->mn_Node.ln_Name;
D(ebug("Port %08lx, type %ld\n", port, packet->dp_Type));
switch(packet->dp_Type) {
case ACTION_FINDUPDATE:
case ACTION_FINDINPUT:
case ACTION_FINDOUTPUT:
fh = BTOC(packet->dp_Arg1);
fh->fh_Port = port; /* temporary storage */
break;
case ACTION_SCREEN_MODE:
packet->dp_Type = FACTION_SCREEN_MODE;
packet->dp_Res1 = (LONG)port; /* we are not allowed to change dp->Arg2 */
break;
case ACTION_WAIT_CHAR:
packet->dp_Type = FACTION_WAIT_CHAR;
packet->dp_Res1 = (LONG)port; /* we are not allowed to change dp->Arg2 */
break;
}
PutMsg(PktPort, msg);
}
}
#ifdef CLI_START
/*
* DEVICE CREATION AND DELETION
*/
static inline
void *
DosAllocMem(bytes)
long bytes;
{
long *ptr;
bytes += 4;
if ((ptr = AllocMem(bytes, MEMF_PUBLIC | MEMF_CLEAR))) {
*ptr++ = bytes;
return((void *)ptr);
}
Alert(AG_NoMemory|AT_DeadEnd);
return NULL; /* NOTREACHED */
}
static inline
void
DosFree(void *vptr)
{
long *ptr = vptr;
--ptr;
FreeMem(ptr, *ptr);
}
DosList *Dl;
void
MkDevice(char *devName)
{
DosList *dl;
RootNode *root;
DosInfo *info;
Dl = dl = (struct DosList *)DosAllocMem(sizeof(struct DosList)+strlen(devName)+2);
strcpy((char *)(dl+1) + 1, devName);
*(char *)(dl + 1) = strlen(devName);
dl->dol_Type = DLT_DEVICE;
dl->dol_Task = PktPort;
dl->dol_Name = MKBADDR((char *)(dl+1));
Forbid();
root = (struct RootNode *)DOSBase->dl_Root;
info = (struct DosInfo *)BADDR(root->rn_Info);
dl->dol_Next = info->di_DevInfo;
info->di_DevInfo = MKBADDR(dl);
Permit();
}
void
DelDevice(void)
{
DosList *dl;
DosInfo *info;
RootNode *root;
DosList *dls;
BPTR *bpp;
if ((dl = Dl)) {
Forbid();
root = (struct RootNode *)DOSBase->dl_Root;
info = (struct DosInfo *)BADDR(root->rn_Info);
for (bpp = &info->di_DevInfo; (dls = BADDR(*bpp)); bpp = &dls->dol_Next) {
if (dls == dl)
break;
}
if (dls == dl) {
*bpp = dls->dol_Next;
} else {
Alert(0x07AAAAAA|AT_Recovery);
}
Permit();
DosFree(dl);
Dl = NULL;
}
}
#endif
WaitTreq *
AllocTimerequest(unsigned long timeout)
{
struct WaitTreq *tr = (struct WaitTreq *)AllocMem(sizeof(WaitTreq), MEMF_PUBLIC);
if (NULL != tr) {
memmove(tr, TimerIO, sizeof(tr->wt_timereq));
tr->wt_timereq.tr_time.tv_secs = 0;
if (timeout < 10 && SysBase->LibNode.lib_Version < 36)
timeout = 10; /* work around bug in <2.0 timer.device */
while (timeout >= 1000000) {
tr->wt_timereq.tr_time.tv_secs++;
timeout -= 1000000;
}
tr->wt_timereq.tr_time.tv_micro = timeout;
}
return tr;
}