home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
ckc190.tar.gz
/
ckc190.tar
/
ckmtio.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-19
|
53KB
|
1,819 lines
char *ckxv = "Communications I/O, 5A(082), 17 Aug 94";
/* C K M T I O -- interrupt, console, and port functions for Mac Kermit */
/*
COPYRIGHT NOTICE:
Copyright (C) 1985, 1994, Trustees of Columbia University in the City of New
York. The C-Kermit software may not be, in whole or in part, licensed or
sold for profit as a software product itself, nor may it be included in or
distributed with commercial products or otherwise distributed by commercial
concerns to their clients or customers without written permission of the
Office of Kermit Development and Distribution, Columbia University. This
copyright notice must not be removed, altered, or obscured.
*/
/*
Variables:
dftty -- Pointer to default tty name string, like "/dev/tty".
dfloc -- 0 if dftty is console, 1 if external line.
dfprty -- Default parity
dfflow -- Default flow control
ckxech -- Flag for who echoes console typein:
1 - The program (system echo is turned off)
0 - The system (or front end, or terminal).
functions that want to do their own echoing should check this flag
before doing so.
Functions for assigned communication line (either external or console tty):
ttopen(ttname,local,mdmtyp,timo) -- Open the named tty for exclusive access.
ttclos() -- Close & reset the tty, releasing any access lock.
ttpkt(speed,flow) -- Put the tty in packet mode and set the speed.
ttvt(speed,flow) -- Put the tty in virtual terminal mode.
ttinl(dest,max,timo) -- Timed read line from the tty.
ttinc(timo) -- Timed read character from tty.
ttchk() -- See how many characters in tty input buffer.
ttxin(n,buf) -- Read n characters from tty (untimed).
ttol(string,length) -- Write a string to the tty.
ttoc(c) -- Write a character to the tty.
ttflui() -- Flush tty input buffer.
Functions for console terminal:
congm() -- Get console terminal modes.
concb() -- Put the console in single-character wakeup mode with no echo.
conbin() -- Put the console in binary (raw) mode.
conres() -- Restore the console to mode obtained by congm().
conoc(c) -- Unbuffered output, one character to console.
conol(s) -- Unbuffered output, null-terminated string to the console.
conxo(n,s) -- Unbuffered output, n characters to the console.
conchk() -- Check if characters available at console.
coninc() -- Get a character from the console.
conint() -- Enable terminal interrupts on the console.
connoi() -- Disable terminal interrupts on the console.
Time functions
msleep(m) -- Millisecond sleep
ztime(&s) -- Return pointer to date/time string
*/
#include <StdArg.h>
#include "ckcdeb.h" /* Formats for debug() */
#include "ckcker.h" /* kermit defs */
#include "ckmdef.h" /* macintosh defs */
#include "ckcasc.h"
#include "ckmwin.h"
#include "ckmcon.h"
#include "ckmptp.h" /* ckm* Prototypes */
#include "ckuusr.h"
char *dftty = "Modem";
/* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */
int dfloc = 1;
/* Other defaults */
int dfprty = 0; /* Default parity */
int ttprty = 0; /* Parity in use. */
long ttspeed = -1; /* For saving speed */
int dfflow = 1; /* Xon/Xoff flow control */
int ttflow = -9; /* For saving flow */
int fDTR = 0; /* DTR input flow control */
int fCTS = 0; /* CTS output flow control */
int fInX = 0; /* send xoff when buffer almost full */
int backgrd = 0; /* a Mac is allways in foreground */
/* Local variables */
int drop_dtr = 0; /* drop DTR on Quit */
/* buffer and pointer for input processing */
static char *my_input_buf; /* we give this to the serial driver queue */
/* Private buffer for myread() and its companions. Not for use by anything
* else. ttflui() is allowed to reset them to initial values. ttchk() is
* allowed to read my_count.
*
* my_item is an index into mybuf[]. Increment it *before* reading mybuf[].
*
* A global parity mask variable could be useful too. We could use it to
* let myread() strip the parity on its own, instead of stripping sign
* bits as it does now.
*/
/* #define MYBUFLEN 256 */
#define MYBUFLEN 2048
static unsigned char *mybufp; /* Buffer, including push back */
static int my_count = 0; /* Number of chars still in mybuf */
static int my_item = -1; /* Last index read from mybuf[] */
#ifdef COMMENT
#define TTBUFL 200 /* good size (it's RBUFL guys!) */
static unsigned char *ttbuf;
#endif
static char *tto_buf; /* output buffer for ttol() */
static ParamBlockRec iopb; /* paramater block for ttol() */
static long tto_startticks; /* when we started the output */
#define XOFFTIMEO 480L /* a timeout of 8 seconds (in ticks) */
short dfltVol; /* Volume for Take- and Log-File */
/* true if using RAM serial driver (usually TRUE) */
extern Boolean usingRAMdriver,
have_128roms; /* True if we are a Plus or better */
extern char ttname[];
extern SIGTYP (*alarmfunc)();
extern SIGTYP (*intfunc)();
typedef unsigned long ulong;
extern ulong alarmtime; /* Global alarm time */
extern int pflag;
static void ucharout (char c);
/****************************************************************************/
/* S Y S I N I T -- System-dependent program initialization. */
/****************************************************************************/
sysinit() {
ParamBlockRec pb;
mac_init (); /* Set up the Mac */
/* Get the default volume reference number for Take- and Log-Files */
pb.volumeParam.ioNamePtr = NIL;
PBGetVol (&pb, FALSE);
dfltVol = pb.volumeParam.ioVRefNum;
findfinderfiles(); /* See if file was selected */
mac_post_load_init(); /* Show the terminal window etc */
return(0);
} /* sysinit */
/* init terminal I/O buffers */
inittiobufs()
{
#ifdef COMMENT
if ((ttbuf = (unsigned char *) NewPtr(TTBUFL + 1)) == NIL)
macfatal("Can't allocate ttbuf", 0);
#endif
if ((mybufp = (unsigned char *) NewPtr(MYBUFLEN + 4)) == NIL)
macfatal("Can't allocate mybufp", 0);
my_count = 0; /* Number of chars still in mybuf */
my_item = -1; /* Last index read from mybuf[] */
if ((tto_buf = (char *) NewPtr(MAXSP)) == NIL)
macfatal("Can't allocate tto_buf", 0);
if ((my_input_buf = (char *) NewPtr(MYBUFSIZE)) == NIL)
macfatal("Can't allocate my_input_buf", 0);
}
/****************************************************************************/
/* P O R T _ O P E N -- Open and init a serial port. port is either -6 (for */
/* the modem port) or -8 (for the printer port) */
/****************************************************************************/
#ifdef MPW
#define RamSDOpen RAMSDOPEN /* fix routine name */
#else /* !MPW (Think C) */
#define RamSDOpen(n) (noErr) /* don't really do anything */
#endif /* !MPW */
port_open (int port)
{
int err;
if ((port != -6) && (port != -8))
port = -6;
try_again:
/* Set up IO drivers */
innum = port;
outnum = port - 1;
if (innum == -6) {
if (((err = OpenDriver ("\p.AIn", &innum)) != noErr) ||
((err = OpenDriver ("\p.AOut", &outnum)) != noErr)) {
printerr ("Could not open the Modem port: ", err);
innum = outnum = 0; /* Mark the port as closed */
return;
}
} else {
if (((err = OpenDriver ("\p.BIn", &innum)) != noErr) ||
((err = OpenDriver ("\p.BOut", &outnum)) != noErr)) {
printerr("Could not open the Printer port. Try\
turning off Appletalk.", 0);
port = -6;
goto try_again;
}
}
/* try for the RAM driver */
if (innum == -6)
err = RamSDOpen (sPortA);
else
err = RamSDOpen (sPortB);
if (err == noErr) {
usingRAMdriver = TRUE;
} else {
usingRAMdriver = FALSE;
printerr("Can't open RAM serial driver; using the ROM driver\
(without flow control).",0);
}
err = SerSetBuf (innum, my_input_buf, MYBUFSIZE);
/* Make driver use larger buff */
if (err)
printerr ("Trouble making IO buffer:", err);
switch (port) {
case -6:
strcpy(ttname, "Modem");
break;
case -8:
strcpy(ttname, "Printer");
break;
}
}
/*****************************************************************************/
/* P O R T _ C L O S E -- Close down the serial port. */
/*****************************************************************************/
#ifdef MPW
#define RamSDClose RAMSDCLOSE
#else /* !MPW (Think C) */
#define RamSDClose(n) /*NOTHING*/
#endif /* !MPW */
port_close()
{
int err;
ParamBlockRec cpb;
if (!innum)
return; /* already closed */
err = KillIO(innum); /* Kill off IO drivers */
if (err != noErr)
printerr("trouble KillIO-ing serial input driver:",err);
err = KillIO(outnum); /* Kill off IO drivers */
if (err != noErr)
printerr("trouble KillIO-ing serial output driver:",err);
err = SerSetBuf (innum, NULL, 0); /* Make driver default buffer */
if (err != noErr)
printerr("trouble resetting serial IO buffer:",err);
if (usingRAMdriver) {
if (!drop_dtr) {
bzero((char *) &cpb, sizeof(cpb));
cpb.cntrlParam.ioNamePtr = NULL; /* IM-IV p226 */
/* tell the ram driver not to lower DTR on close */
cpb.cntrlParam.csCode = 16; /* misc serial control */
*((unsigned char *) cpb.cntrlParam.csParam) = 0x80;
/* don't lower DTR */
cpb.cntrlParam.ioCRefNum = innum;
err = PBControl(&cpb, FALSE);
if ((err != noErr) && (err != -1))
printerr(
"trouble telling RAM serial driver (in) not to lower DTR:",
err);
bzero((char *) &cpb, sizeof(cpb));
cpb.cntrlParam.ioNamePtr = NULL; /* IM-IV p226 */
cpb.cntrlParam.csCode = 16; /* misc serial control */
/* don't lower DTR */
*((unsigned char *) cpb.cntrlParam.csParam) = 0x80;
cpb.cntrlParam.ioCRefNum = outnum;
err = PBControl(&cpb, FALSE);
if ((err != noErr) && (err != -1))
printerr("\
trouble telling RAM serial driver (out) not to lower DTR:",err);
}
RamSDClose (sPortA); /* this "returns" void */
}
err = CloseDriver (innum);
if (err != noErr)
printerr("trouble closing serial input driver:",err);
/*
* For some reason or other, doing this close on a 64k ROM machine will cause
* the mouse to freeze. Since the input driver is the only one that really
* matters, we just close it.
*/
if (have_128roms) {
err = CloseDriver (outnum);
if (err != noErr)
printerr("trouble closing serial output driver:",err);
}
/* mark things as closed */
innum = outnum = 0;
}
/****************************************************************************/
/* T T O P E N -- Open a tty for exclusive access. */
/* */
/* ttname = C string, name of communications device */
/* lcl = (doesn't mean anything in Mac Kermit */
/* modem = 0 if no modem, positive if there is a modem */
/* timeo = (not used in Mac Kermit) */
/* */
/* Returns 0 on success, -1 on failure. */
/****************************************************************************/
int
ttopen (ttname, lcl, modem, timeo)
char *ttname;
int *lcl;
int modem;
int timeo;
#pragma unused (modem, timeo)
{
int port, *aport;
char tmp[100];
my_count = 0; /* Initialize myread() stuff */
my_item = -1;
if (*lcl < 0)
*lcl = 1; /* Always in local mode */
if (mybufp == NIL)
macfatal("No mybufp", 0);
if (tto_buf == NIL)
macfatal("No tto_buf", 0);
if (my_input_buf == NIL)
macfatal("No my_input_buf", 0);
iopb.ioParam.ioResult = 0; /* No pending output */
iopb.ioParam.ioActCount = 0; /* For error checking in ttol() */
iopb.ioParam.ioReqCount = 0;
/* Re-open the port if it has changed. */
strcpy(tmp, ttname);
(void) lower(tmp);
if (strcmp(tmp, "printer") == 0)
port = -8;
else if (strcmp(tmp, "modem") == 0)
port = -6;
else {
printfalert("Unknown port \"%s\" (\"%s\"), using Modem", ttname, tmp);
port = -6;
}
aport = &port;
if (port != innum) { /* If port changed */
int saved_drop_dtr;
saved_drop_dtr = drop_dtr;
drop_dtr = 1; /* So we don't confuse AppleTalk */
port_close();
drop_dtr = saved_drop_dtr;
port_open(port);
if (!setserial (innum, outnum, speed, KPARITY_NONE))
printfalert("Problem setting port speed");
(void) sershake(flow); /* Set flow control */
}
return (0);
} /* ttopen */
/****************************************************************************/
/* T T C L O S -- Close the TTY, releasing any lock. */
/****************************************************************************/
int
ttclos ()
{
my_count = 0; /* Initialize myread() stuff */
my_item = -1;
return 0;
} /* ttclos */
/*
* sershake
* Set handshake parameters on serial port.
*/
int sershake (int flow)
{
int err;
ParamBlockRec pb;
SerShk *controlparam; /* To change serial driver paramaters */
if (!innum)
return 0; /* if port closed, do nothing */
bzero((char *)&pb, sizeof(pb));
controlparam = (SerShk *) &pb.cntrlParam.csParam;
/*
* Old code used to issue controls to in and out sides. IM-IV
* says to use the output driver.
*/
pb.cntrlParam.ioCRefNum = outnum;
if (have_128roms)
pb.cntrlParam.csCode = 14; /* SerHShake + DTR */
else
pb.cntrlParam.csCode = 10; /* SerHShake */
if (flow)
controlparam->fXOn = TRUE; /* obey flow control */
else
controlparam->fXOn = FALSE; /* ignore flow control */
controlparam->fCTS = fCTS; /* output flow control */
controlparam->xOn = 17;
controlparam->xOff = 19;
controlparam->errs = FALSE;
controlparam->evts = FALSE;
if (flow && usingRAMdriver && fInX) /* old ROM driver can't do this */
controlparam->fInX = TRUE; /* send flow control when almost full */
else
controlparam->fInX = FALSE;
controlparam->fDTR = fDTR; /* input flow control */
err = PBControl(&pb, FALSE);
if (err != noErr) {
printfalert("sershake: problem setting handshake: %d", err);
return -1;
}
return 0;
}
/****************************************************************************/
/* T T P K T -- Condition the communication line for packets. */
/* If called with speed > -1, also set the speed. */
/* Returns 0 on success, -1 on failure. */
/****************************************************************************/
int
ttpkt (long spd, int flow, int parity) /* we only care about flow here */
{
#pragma unused (spd)
ttprty = parity; /* Let other tt functions see these. */
ttspeed = speed; /* $$$ is this correct? */
ttflow = flow; /* Now make this available too. */
fInX = TRUE;
return(sershake(flow));
}
/****************************************************************************/
/* T T V T -- Condition the communication line for a virtual terminal. */
/* If called with spd > -1, also set the spd. */
/* Returns 0 on success, -1 on failure. */
/****************************************************************************/
int
ttvt (spd, flow, parity) /* all ignoreed */
long spd;
int flow;
int parity;
#pragma unused (spd, flow, parity)
{
(void) ttres();
return (0);
} /* ttvt */
/****************************************************************************/
/* T T F L U I -- Flush tty input buffer */
/****************************************************************************/
int
ttflui ()
{
int err;
my_count = 0; /* Initialize myread() stuff */
my_item = -1;
if (!innum)
return 0; /* if port closed, do nothing */
err = KillIO (innum);
if (err)
printerr ("Bad input clear", err);
return (0);
} /* ttflui */
int
ttfluo() /* Flush output buffer */
{
int err;
if (!innum)
return 0; /* if port closed, do nothing */
err = KillIO (outnum);
if (err)
printerr ("Bad ouput clear", err);
return (0); /* (dummy for now) */
}
/****************************************************************************/
/* T T S N D B -- Send a break */
/****************************************************************************/
ttsndb() {
long finalticks;
if (!innum)
return 0; /* if port closed, do nothing */
/* delay wants 1/60th units */
SerSetBrk (outnum); /* start breaking */
Delay ((long) 15, &finalticks); /* delay about 250ms */
SerClrBrk (outnum); /* stop breaking */
return 0;
}
/****************************************************************************/
/*
* Flushio:
* Initialize some communications constants, and clear screen and
* character buffers. */
/****************************************************************************/
flushio ()
{
int err;
if (!innum)
return 0; /* if port closed, do nothing */
err = KillIO (innum);
if (err)
printerr ("Bad input clear", err);
err = KillIO (outnum);
if (err)
printerr ("Bad ouput clear", err);
return 0;
} /* flushio */
/****************************************************************************/
/* sendbreak - sends a break across the communictions line.
*
* The argument is in units of approximately 0.05 seconds (or 50
* milliseconds). To send a break of duration 250 milliseconds the
* argument would be 5; a break of duration 3.5 seconds would be (umm,
* lets see now) 70.
*
*/
/****************************************************************************/
sendbreak (msunit)
int msunit;
{
long finalticks;
if (!innum)
return 0; /* if port closed, do nothing */
/* delay wants 1/60th units. We have 3/60 (50 ms.) units, convert */
msunit = msunit * 3;
SerSetBrk (outnum); /* start breaking */
Delay ((long) msunit, &finalticks); /* delay */
SerClrBrk (outnum); /* stop breaking */
return 0;
} /* sendbreak */
/****************************************************************************/
/* toggledtr - Turn DTR off, wait a bit, turn it back on.
*
* the argument is in the same units as sendbreak (see above).
*/
/****************************************************************************/
toggle_dtr (msunit)
int msunit;
{
long finalticks;
ParamBlockRec pb;
int err;
if (!innum)
return 0; /* if port closed, do nothing */
if (usingRAMdriver) {
/* delay wants 1/60th units. We have 3/60 (50 ms.) units, convert */
msunit = msunit * 3;
pb.cntrlParam.csCode = 18; /* lower DTR */
pb.cntrlParam.ioCRefNum = outnum;
err = PBControl (&pb, FALSE);
if (err != noErr)
printerr ("toggle_dtr() trouble lowering DTR: ", err);
Delay ((long) msunit, &finalticks); /* delay */
pb.cntrlParam.csCode = 17; /* raise DTR */
pb.cntrlParam.ioCRefNum = outnum;
err = PBControl (&pb, FALSE);
if (err != noErr)
printerr ("toggle_dtr() trouble raising DTR: ", err);
}
return 0;
} /* sendbreak */
/****************************************************************************/
/* do_xon - xon the output port and send an xon (control-Q) character */
/****************************************************************************/
do_xon ()
{
ParamBlockRec pb;
int err;
if (!innum)
return 0; /* if port closed, do nothing */
if (usingRAMdriver) {
pb.cntrlParam.csCode = 22; /* clear XOFF for my output */
pb.cntrlParam.ioCRefNum = outnum;
err = PBControl (&pb, FALSE);
if (err != noErr)
printerr ("do_xon() trouble unblocking output port: ", err);
pb.cntrlParam.csCode = 24; /* unconditionally send XON */
pb.cntrlParam.ioCRefNum = outnum;
err = PBControl (&pb, FALSE);
if (err != noErr)
printerr ("do_xon() trouble sending XON: ", err);
} else {
OutputChar (ttermw, '\021'); /* XON */
}
return 0;
} /* sendbreak */
/****************************************************************************/
/* T T S S P D -- Set tty speed */
/****************************************************************************/
ttsspd (cps) int cps; {
if (!innum)
return 0; /* if port closed, do nothing */
if (setserial (innum, outnum, cps*10, KPARITY_NONE))
return(0);
else
return (-1);
}
/****************************************************************************/
/* T T G S P D -- Set tty speed */
/****************************************************************************/
long
ttgspd () {
if (speed <= 0L && innum != 0)
printerr("Speed got reset, now == ", (int) speed);
return (speed);
}
/* Interrupt Functions */
/****************************************************************************/
/* Set up terminal interrupts on console terminal */
/* Set an interrupt trap. */
/****************************************************************************/
VOID conint(SIGTYP (*f)(int), SIGTYP (*s)(int))
{
#pragma unused (f, s)
return;
} /* conint */
/****************************************************************************/
/* Reset console terminal interrupts */
/****************************************************************************/
VOID connoi ()
{
return;
} /* connoi */
/****************************************************************************/
/* writeps - write a pascal string to the serial port.
*
*/
/****************************************************************************/
void
writeps (StringPtr s)
{
long wcnt, w2;
StringPtr s2;
w2 = wcnt = *s++; /* get count */
for (s2 = s; w2 > 0; w2--, s2++) /* add parity */
*s2 = dopar (*s2);
(void) ttol((CHAR *) s, wcnt); /* ttol will printerr() if it has a problem */
return;
} /* writeps */
extern Boolean have_multifinder;/* true if running under MF */
extern Boolean in_background; /* becomes true if running MF and in bg */
extern long mf_sleep_time; /* number of 60ths between task time */
/****************************************************************************/
/* T T O L -- Similar to "ttinl", but for writing. */
/****************************************************************************/
ttol (s, n)
CHAR *s;
int n;
{
long finalticks;
int err;
ParamBlockRec cpb;
if (!innum)
return (-1); /* if port closed, do nothing */
#ifdef COMMENT
debug(F101,"ttol n","",n);
debug(F101," s","",s);
/*
* the straight-forward way to write out a buffer: synchronously
*/
finalticks = n;
err = FSWrite(outnum, &finalticks, s);
if ((err != noErr) || (finalticks != n)) {
printerr("ttol FSWrite error: ", err);
return (-1);
}
return (n);
#endif
/*
* The fancy to write out strings: do async writes, waiting for the
* previous call to finish first (and possibly unsticking the serial
* driver)
*/
/* wait for previous call to finish */
/* while the prev. request is still running */
while (iopb.ioParam.ioResult == 1) {
/* if we're running protocol */
if (have_multifinder && protocmd != 0) {
miniparser (TRUE); /* keep mac running */
finalticks = TickCount ();
} else { /* else terminal emulation */
Delay ((long) 1, &finalticks); /* wait for a bit */
}
/* (PWP) If we have waited too long, unblock the output (keep the
Mac from freezing up) */
if ((usingRAMdriver) && (finalticks > (tto_startticks + XOFFTIMEO))) {
cpb.cntrlParam.csCode = 22; /* clear XOFF for my output */
cpb.cntrlParam.ioCRefNum = outnum;
err = PBControl (&cpb, FALSE);
if (err != noErr)
printerr ("ttol() trouble unblocking output port: ", err);
tto_startticks = TickCount (); /* Get starting time */
}
}
/* check for errors in previous call */
if (iopb.ioParam.ioResult) {
printerr ("Error in previous PBWrite:", iopb.ioParam.ioResult);
return (-1);
}
if (iopb.ioParam.ioActCount != iopb.ioParam.ioReqCount) {
printerr ("PBWrite to serial didn't write enough: ",
iopb.ioParam.ioActCount);
printerr ("(asked for:)", iopb.ioParam.ioReqCount);
return (-1);
}
/* the previous call is now done, so we can load in our new information */
if (n > MAXSP) { /* MAXSP == sizeof(tto_buf) */
printerr("ttol asked to write too many chars: ", n);
return (-1);
}
bcopy((char *) s, tto_buf, n); /* in ckmfio.h if nowhere else */
iopb.ioParam.ioCompletion = NULL;
iopb.ioParam.ioNamePtr = NULL;
iopb.ioParam.ioVRefNum = 0;
iopb.ioParam.ioRefNum = outnum;
iopb.ioParam.ioVersNum = 0;
iopb.ioParam.ioPermssn = 0;
iopb.ioParam.ioMisc = NULL;
iopb.ioParam.ioBuffer = tto_buf;
iopb.ioParam.ioReqCount = (long) n;
iopb.ioParam.ioPosMode = 0;
iopb.ioParam.ioPosOffset = 0;
tto_startticks = TickCount (); /* get starting time */
PBWrite (&iopb, TRUE); /* request an async. write */
if (protocmd != 0)
miniparser (TRUE); /* allow other tasks to run */
return (n); /* fake a good run */
} /* ttol */
/* T T O C -- Output a character to the communication line */
/*
This function should only used for interactive, character-mode operations,
like terminal connection, script execution, dialer i/o, where the overhead
of the signals and alarms does not create a bottleneck.
*/
ttoc(char c) {
char foo[2];
foo[0] = c;
foo[1] = '\0';
return (ttol((CHAR *) foo, 1));
}
/* ckumyr.c by Kristoffer Eriksson, ske@pkmab.se, 15 Mar 1990. */
/* myread() -- Efficient read of one character from communications line.
*
* Uses a private buffer to minimize the number of expensive read() system
* calls. Essentially performs the equivalent of read() of 1 character, which
* is then returned. By reading all available input from the system buffers
* to the private buffer in one chunk, and then working from this buffer, the
* number of system calls is reduced in any case where more than one character
* arrives during the processing of the previous chunk, for instance high
* baud rates or network type connections where input arrives in packets.
* If the time needed for a read() system call approaches the time for more
* than one character to arrive, then this mechanism automatically compensates
* for that by performing bigger read()s less frequently. If the system load
* is high, the same mechanism compensates for that too.
*
* myread() is a macro that returns the next character from the buffer. If the
* buffer is empty, mygetbuf() is called. See mygetbuf() for possible error
* returns.
*
* This should be efficient enough for any one-character-at-a-time loops.
* For even better efficiency you might use memcpy()/bcopy() or such between
* buffers (since they are often better optimized for copying), but it may not
* be worth it if you have to take an extra pass over the buffer to strip
* parity and check for CTRL-C anyway.
*
* Note that if you have been using myread() from another program module, you
* may have some trouble accessing this macro version and the private variables
* it uses. In that case, just add a function in this module, that invokes the
* macro.
*/
#define mac_myread(timeo_ticks,intim) (--my_count < 0 \
? mac_mygetbuf(timeo_ticks,intim) \
: (int) (mybufp[++my_item]))
/* Specification: Push back up to one character onto myread()'s queue.
*
* This implementation: Push back characters into mybuf. At least one character
* must have been read through myread() before myunrd() may be used. After
* EOF or read error, again, myunrd() can not be used. Sometimes more than
* one character can be pushed back, but only one character is guaranteed.
* Since a previous myread() must have read its character out of mybuf[],
* that guarantees that there is space for at least one character. If push
* back was really needed after EOF, a small addition could provide that.
*
* myunrd() is currently not called from anywhere inside kermit...
*/
#ifdef NOTUSED
myunrd(ch) CHAR ch; {
if (my_item >= 0) {
mybufp[my_item--] = ch;
++my_count;
}
}
#endif
/* mygetbuf() -- Fill buffer for myread() and return first character.
*
* This function is what myread() uses when it can't get the next character
* directly from its buffer. First, it calls a system dependent myfillbuf()
* to read at least one new character into the buffer, and then it returns
* the first character just as myread() would have done. This function also
* is responsible for all error conditions that myread() can indicate.
*
* Returns: When OK => a positive character, 0 or greater.
* When EOF => -2.
* When error => -3, error code in errno.
*
* Older myread()s additionally returned -1 to indicate that there was nothing
* to read, upon which the caller would call myread() again until it got
* something. The new myread()/mygetbuf() always gets something. If it
* doesn't, then make it do so! Any program that actually depends on the old
* behaviour will break.
*
* The older version also used to return -2 both for EOF and other errors,
* and used to set errno to 9999 on EOF. The errno stuff is gone, EOF and
* other errors now return different results, although Kermit currently never
* checks to see which it was. It just disconnects in both cases.
*
* Kermit lets the user use the quit key to perform some special commands
* during file transfer. This causes read(), and thus also mygetbuf(), to
* finish without reading anything and return the EINTR error. This should
* be checked by the caller. Mygetbuf() could retry the read() on EINTR,
* but if there is nothing to read, this could delay Kermit's reaction to
* the command, and make Kermit appear unresponsive.
*
* The debug() call should be removed for optimum performance.
*/
/* myfillbuf():
* System-dependent read() into mybuf[], as many characters as possible.
*
* Returns: OK => number of characters read, always more than zero.
* EOF => 0
* Error => -1, error code in errno.
*
* If there is input available in the system's buffers, all of it should be
* read into mybuf[] and the function return immediately. If no input is
* available, it should wait for a character to arrive, and return with that
* one in mybuf[] as soon as possible. It may wait somewhat past the first
* character, but be aware that any such delay lengthens the packet turnaround
* time during kermit file transfers. Should never return with zero characters
* unless EOF or irrecoverable read error.
*
* Correct functioning depends on the correct tty parameters being used.
* Better control of current parameters is required than may have been the
* case in older Kermit releases. For instance, O_NDELAY (or equivalent) can
* no longer be sometimes off and sometimes on like it used to, unless a
* special myfillbuf() is written to handle that. Otherwise the ordinary
* myfillbuf()s may think they have come to EOF.
*
* If your system has a facility to directly perform the functioning of
* myfillbuf(), then use it. If the system can tell you how many characters
* are available in its buffers, then read that amount (but not less than 1).
* If the system can return a special indication when you try to read without
* anything to read, while allowing you to read all there is when there is
* something, you may loop until there is something to read, but probably that
* is not good for the system load.
*/
int
mac_mygetbuf(long timeo_tics, long intim) {
long avail; /* can't be register */
long finaltics; /* can't be register */
int err;
extern int tlevel;
if (mybufp == NIL)
macfatal("No mybufp (in mac_mygetbuf())", 0);
if (!innum)
return -1; /* if port closed, do nothing */
for (;;) {
SerGetBuf (innum, &avail); /* Get available count */
if (avail > 0)
break; /* we can get these chars */
/* no chars availiable yet */
if ((protocmd != 0) || (tlevel > -1) || intfunc) {
miniparser (TRUE); /* keep mac running */
if (sstate == 'a') { /* abort occured? */
if (intfunc) {
sstate = '\0';
(*intfunc)(0);
} else
return (-1); /* ugh, look like timeout */
}
}
/*
* Check alarm() timeouts.
*/
if (alarmtime && alarmfunc && (((ulong)TickCount() - alarmtime) > 0))
(*alarmfunc)(0);
finaltics = TickCount ();
if (timeo_tics > 0) { /* Want to do timeout? */
if (intim + timeo_tics < finaltics) {
return (-1); /* Too long, give up */
}
}
/* go back and try to get more chars */
}
if (avail > MYBUFLEN)
avail = MYBUFLEN;
/*
* CAREFUL: the Mac FSRead() function gets how much to read,
* AND PUTS HOW MUCH IT DID READ into avail
*/
err = FSRead (innum, &avail, mybufp); /* Into our buffer */
if (err != noErr) {
screen(SCR_EM,0,(long) err,"Serial input error:");
return (-3); /* return input error */
}
if (avail <= 0) {
screen(SCR_EM, 0, (long) avail,
"No serial input error, but didn't read any:");
return (-3); /* return input error */
}
/* not at end of packet yet, so let other tasks have a chance */
if (in_background)
miniparser (TRUE); /* allow other tasks to run */
my_count = (int) avail;
/* debug(F101, "myfillbuf read", "", my_count); */
--my_count;
return((int) (mybufp[my_item = 0]));
}
/****************************************************************************/
/* T T I N L -- Read a record (up to break character) from comm line. */
/*
If no break character encountered within "max", return "max" characters,
with disposition of any remaining characters undefined. Otherwise, return
the characters that were read, including the break character, in "dest" and
the number of characters read as the value of function, or 0 upon end of
file, or -1 if an error occurred. Times out & returns error if not completed
within "timo" seconds.
*/
/*
Reads up to "max" characters from the communication line, terminating on
the packet-end character (eol), or timing out and returning -1 if the eol
character not encountered within "timo" seconds. The characters that were
input are copied into "dest" with their parity bits stripped if parity was
selected. Returns the number of characters read. Characters after the
eol are available upon the next call to this function.
The idea is to minimize the number of system calls per packet, and also to
minimize timeouts. This function is the inner loop of the program and must
be as efficient as possible. The current strategy is to use myread().
WARNING: this function calls parchk(), which is defined in another module.
Normally, ckutio.c does not depend on code from any other module, but there
is an exception in this case because all the other ck?tio.c modules also
need to call parchk(), so it's better to have it defined in a common place.
*/
/****************************************************************************/
#define CTRLC '\03'
#ifdef PARSENSE
int
ttinl(CHAR *dest, int max, int timo, CHAR eol, CHAR start) {
int flag;
#else
int
ttinl(CHAR *dest, int max, int timo, CHAR eol) {
#endif
register int i, m, n; /* local variables */
int x;
long timeoticks;
long intim; /* when we started */
debug(F101,"ttinl max","",max);
debug(F101,"ttinl timo","",timo);
#ifdef PARSENSE
debug(F000,"ttinl start","",start);
flag = 0; /* Start of packet flag */
#endif
x = 0; /* Return code */
m = (ttprty) ? 0177 : 0377; /* Parity stripping mask. */
*dest = '\0'; /* Clear destination buffer */
if (timo <= 0) { /* untimed */
timo = 0; /* Safety */
intim = 0; /* tell mac_myread not to timeout */
timeoticks = 0;
} else {
timeoticks = timo * 60;
intim = TickCount (); /* now */
}
i = 0;
while (i < max-1) {
/* debug(F101,"ttinl i","",i); */
if ((n = mac_myread(timeoticks,intim)) < 0) {
debug(F101,"ttinl myread failure, n","",n);
x = -1; break;
}
/* debug(F101,"ttinl char","",n&m); */
#ifdef PARSENSE
if ((flag == 0) && ((n & 0x7f) == start)) flag = 1;
if (flag) dest[i++] = n & m;
#else
dest[i++] = n & m;
#endif /* PARSENSE */
#ifdef PARSENSE
if (flag == 0) {
debug(F101,"ttinl skipping","",n);
continue;
}
#endif /* PARSENSE */
/* Check for end of packet */
if ((n & 0x7f) == eol) {
debug(F101,"ttinl got eol","",eol);
dest[i] = '\0'; /* Yes, terminate the string, */
/* debug(F101,"ttinl i","",i); */
#ifdef PARSENSE
/* Here's where we actually check and adjust the parity. */
/* The major flaw here is if parity is NONE (ttprty = 0) and the packets */
/* really do have no parity, then parchk() is called for every packet. */
/* In practice, this doesn't really harm efficiency noticably, but it would */
/* be better if ttinl() had a way of knowing to stop doing this once a */
/* particular file transfer had been started and checked. */
if (ttprty == 0) {
if ((ttprty = parchk(dest,start,i)) > 0) {
register int j;
debug(F101,"ttinl senses parity","",ttprty);
debug(F110,"ttinl packet before",dest,0);
for (j = 0; j < i; j++)
dest[j] &= 0x7f; /* Strip parity from packet */
debug(F110,"ttinl packet after ",dest,0);
} else debug(F101,"parchk","",ttprty);
}
#endif
debug(F111,"ttinl got", dest,i);
return(i);
}
} /* end while (i < max-1) */
debug(F100,"ttinl timout","",0); /* Get here on timeout. */
debug(F111," with",dest,i);
return(x); /* and return error code. */
} /* ttinl */
/****************************************************************************/
/* ttinc(timo) - read a character with timeout. Return -1 on timeout. */
/****************************************************************************/
int
ttinc (int timo)
{
register int m, n = 0;
long timeoticks;
long intim; /* when we started */
if (timo <= 0) { /* untimed */
intim = 0; /* tell mac_myread not to timeout */
timeoticks = 0;
} else {
timeoticks = timo * 60;
intim = TickCount (); /* now */
}
m = (ttprty) ? 0177 : 0377; /* Parity stripping mask. */
/* comm line failure returns -1 thru myread, so no &= 0377 */
n = mac_myread(timeoticks,intim); /* Wait for a character... */
/* debug(F101,"ttinc n","",n); */
return(n < 0 ? n : n & m);
} /* ttinc */
/****************************************************************************/
/* PWP: input as many chars as we can read from the serial line right now */
/****************************************************************************/
int
ttinm(register char *buf, register int max)
{
long avil, num;
int err, i;
if (!innum)
return 0; /* if port closed, do nothing */
/*
* DANGER WILL ROBINSON: this KNOWS about how mac_myread works
* (in an incestuous way), so BE CAREFUL!!!
*/
if (my_count > 0) { /* do we have chars buffered up? */
for (i = 0; (my_count > 0) && (i < max); i++)
*buf++ = mac_myread(0L, 0L);
return (i); /* return contents of previous read buffer */
}
SerGetBuf (innum, &avil); /* Get available count */
if (avil > 0) { /* Have something? */
num = (avil > max) ? max : avil; /* Set max */
err = FSRead (innum, &num, buf); /* Into our buffer */
if (err != noErr)
printerr ("Serial input error: ", err);
return (num); /* return how many */
} else {
return (0);
}
}
/****************************************************************************/
/****************************************************************************/
int
ttchk ()
{
long avcnt; /* pascal long */
if (!innum)
return 0; /* if port closed, none available */
SerGetBuf (innum, &avcnt); /* get available */
return (avcnt + my_count); /* return avail plus our own */
} /* ttchk */
/****************************************************************************/
/* T T R E S -- Reset the serial line after doing a protocol things */
/****************************************************************************/
ttres ()
{
fInX = FALSE;
return(sershake(flow));
}
unsigned long starttime;
Boolean timerRunning = FALSE;
/****************************************************************************/
/* R T I M E R -- Reset elapsed time counter */
/****************************************************************************/
VOID rtimer ()
{
GetDateTime (&starttime);
timerRunning = TRUE;
} /* rtimer */
/****************************************************************************/
/* G T I M E R -- Get current value of elapsed time counter in seconds */
/****************************************************************************/
int
gtimer ()
{
unsigned long secs;
if (timerRunning) {
GetDateTime (&secs);
return (secs - starttime);
timerRunning = FALSE;
} else
return (0);
} /* gtimer */
/****************************************************************************/
/* Z T I M E -- Return date/time string */
/* Various bits stolen from Unix Kermit assume that ztime() returns */
/* a asctime() format string, vis: */
/* "Thu Feb 8 12:00:00 1990" */
/****************************************************************************/
VOID ztime (s)
char **s;
{
/*
ztime() MUST return a string in asctime() format.
*/
unsigned long secs;
DateTimeRec dtrec;
static char dtime[32]; /* really only 25 needed, but be safe */
static char *month_names[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
static char *day_names[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
GetDateTime (&secs);
Secs2Date (secs, &dtrec);
sprintf(dtime, "%3s %3s %2d %2d:%02d:%02d %4d",
day_names[dtrec.dayOfWeek - 1],
month_names[dtrec.month - 1],
dtrec.day, dtrec.hour, dtrec.minute, dtrec.second, dtrec.year);
*s = dtime;
#ifdef COMMENT
#include <Time.h>
time_t time(time_t *timer);
char *ctime(const time_t *timer);
time_t time_now;
time_now = time( (time_t) 0 );
*s = ctime( &time_now );
#endif /* COMMENT */
} /* ztime */
/* Console IO routines. The console is implemented as a text edit structure.
* These routines are supported:
*
* conoc(c) -- output one character to TE record at insertion point
* conol(s) -- output null terminated string to TE record " "
* conoll(s) -- same but with CR on the end
* conxo(n,s) -- n character to TE record " "
*
*/
#define NILTE ((TEHandle ) NILPTR)
#define LF 012
#define CR 015
/****************************************************************************/
/* C O N X O -- Output string of length len to console text edit record */
/****************************************************************************/
conxo (len, s)
int len;
register char *s;
{
register char *t;
/* change NLs to CRs for the Mac */
for (t = s; *t && (t - s < len); t++)
if (*t == LF)
*t = CR;
/* debug (F101, "conxo here: ", s, len); */
if (rcmdw->teh == NULL)
return(0);
TEDeactivate(rcmdw->teh);
(void) trimcon(rcmdw, len);
TEInsert (s, (long) len, rcmdw->teh); /* insert the string */
TESetSelect(TE_MAX, TE_MAX, rcmdw->teh);
TEActivate(rcmdw->teh);
rcdwscroll (rcmdw); /* possibly scroll it */
return (0);
} /* conxo */
/****************************************************************************/
/* C O N O C -- Output a character to the console text edit record */
/****************************************************************************/
conoc (char c)
{
(void) conxo (1, &c);
} /* conoc */
/****************************************************************************/
/****************************************************************************/
conopen ()
{
return (1);
} /* conopen */
/****************************************************************************/
/* C O N O L -- Write a line to the console text edit record */
/****************************************************************************/
conol (s)
register char *s;
{
return (conxo (strlen (s), s));
} /* conol */
/****************************************************************************/
/* C O N O L L -- Output a string followed by CRLF */
/****************************************************************************/
conoll (s)
char *s;
{
(void) conol (s); /* first the string */
(void) conoc (CR); /* now the return */
return (0);
} /* conoll */
/*****************************************
* C-Kermit Compatibility routines *
*****************************************/
/*
* dummy routines for MAC
*/
#define CINC(v,l)((v+1 == l)? 0 : v+1)
#define CBL 25 /* console buffer length */
int cbin = 0; /* circular buffer pointers */
int cbout = 0;
unsigned char cbuf[CBL]; /* console buffer */
concb (char esc)
{
#pragma unused (esc)
}
conres ()
{
}
conchk ()
{
if (sstate == 'a') /* if cmd-. in miniparser */
return 1;
else
return 0;
}
tthang ()
{
toggle_dtr(70);
return(1);
}
VOID setint ()
{
}
/*
* ttgmdm
* Get modem control signals.
* The mac only has one input handshake (CTS) and one output
* line (DTR). DTR may not be present on older MACS with
* DB-9 (instead of DIN-8) connectors.
*/
ttgmdm()
{
int err, r;
SerStaRec stat;
err = SerStatus(outnum, &stat);
if (err != noErr) {
printfalert("ttgmdm: Error getting serial status: %d", err);
return -1; /* not available */
}
r = 0;
if (!stat.ctsHold)
r |= BM_CTS;
/*
* Always say dtr is on. We may be lying if we've enabled
* dtr input flow control.
*/
r |= BM_DTR;
return r;
}
/*
* Things from ckutio.c
*/
int ckxech = 1; /* 0 if system normally echoes console characters, else 1 */
int ttcarr = CAR_AUT; /* Carrier handling mode. */
#include "ckuver.h" /* Version herald */
char *ckxsys = HERALD;
/* T T S C A R R -- Set ttcarr variable, controlling carrier handling.
*
* 0 = Off: Always ignore carrier. E.g. you can connect without carrier.
* 1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect.
* 2 = Auto: For "modem direct": The same as "Off".
* For real modem types: Heed carrier during connect, but ignore
* it anytime else. Compatible with pre-5A C-Kermit versions.
*
* As you can see, this setting does not affect dialing, which always ignores
* carrier (unless there is some special exception for some modem type). It
* does affect ttopen() if it is set before ttopen() is used. This setting
* takes effect on the next call to ttopen()/ttpkt()/ttvt(). And they are
* (or should be) always called before any communications is tried, which
* means that, practically speaking, the effect is immediate.
*
* Of course, nothing of this applies to remote mode (xlocal = 0).
*
* Someone has yet to uncover how to manipulate the carrier in the BSD
* environment (or any non-termio using environment). Until that time, this
* will simply be a no-op for BSD.
*
* Note that in previous versions, the carrier was most often left unchanged
* in ttpkt()/ttvt() unless they were called with DIALING or CONNECT. This
* has changed. Now it is controlled by ttcarr in conjunction with these
* modes.
*/
int ttscarr (carrier)
int carrier;
{
ttcarr = carrier;
debug(F101, "ttscarr","",ttcarr);
return(ttcarr);
}
VOID bgchk ()
{ /* Check background status */
#ifdef notdef
if (bgset < 0) pflag = !backgrd;
else pflag = (bgset == 0 ? 1 : 0);
#endif
}
/* T T X I N -- Get n characters from tty input buffer */
/* Returns number of characters actually gotten, or -1 on failure */
/* Intended for use only when it is known that n characters are actually */
/* Available in the input buffer. */
int ttxin (n,buf)
int n;
CHAR *buf;
{
int i, c;
for (i = 0; i < n; i++) {
c = ttinc(0);
if (c < 0)
return -1;
*buf++ = ttinc(0); /* ignore errors */
}
return n;
}
/* C O N I N C -- Get a character from the console */
int coninc (timo)
int timo;
{
int c;
newparser(1, 0, (long) timo); /* wait till timeout, no menus */
if (cbin == cbout) /* if timeout */
return -1;
c = cbuf[cbout];
cbout = CINC(cbout, CBL); /* advance pointer */
return c;
}
/*
* puts for mac console
*/
int
mac_puts (const char *string)
{
const char *cp;
if (cmdinterminal)
cursor_erase (ttermw);
for (cp = string; *cp; cp++)
ucharout(*cp);
if (cmdinterminal) {
if (ttermw->scroll_amount)
flushscroll(ctermw); /* sync the screen */
if (ctermw->out_maxcol) /* KLUDGE */
flushbuf(ctermw);
cursor_draw(ctermw);
}
return 1; /* $$$ what should this be? */
}
/*
* printf for mac console
*/
int
mac_printf (const char *format, ...) {
int i, rc;
char string[512]; /* Hope that this is big enough!! */
char *cp;
va_list ap;
va_start(ap, format);
rc = vsprintf(string, format, ap);
va_end(ap);
if ((i = strlen(string)) >= sizeof(string))
printerr("Overran allocation for string in printf(), len:", i);
if (cmdinterminal)
cursor_erase (ttermw);
for (cp = string; *cp; cp++)
ucharout(*cp);
if (cmdinterminal) {
if (ctermw->scroll_amount)
flushscroll(ctermw); /* sync the screen */
if (ctermw->out_maxcol) /* KLUDGE */
flushbuf(ctermw);
cursor_draw(ctermw);
}
return (rc);
}
int
mac_perror (const char *s) {
mac_printf("MacError: %s\n",s);
}
/*
* putchar for mac console
*/
int
mac_putchar (int c)
{
if (cmdinterminal)
cursor_erase (ttermw);
ucharout(c);
if (cmdinterminal) {
if (ctermw->scroll_amount)
flushscroll(ctermw); /* sync the screen */
if (ctermw->out_maxcol) /* KLUDGE */
flushbuf(ctermw);
cursor_draw(ctermw);
}
return 1; /* $$$ what should this be? */
}
/*
* outchar
* output a character to a termw window.
*/
void outchar (struct termw *termw, char c)
{
unsigned char buf[2];
buf[0] = c;
buf[1] = 0;
printem(termw, buf, 1);
}
/*
* Send a single character to the console, unix format.
* The MPW compiler thinks that '\r' is 0x0a and that
* '\n' is 0x0d. In this routine, we translate things
* to the appropriate action.
*
* This will be broken if we call this routine with chars
* that are already correct, e.g.: CR.
*/
static void
ucharout (char c)
{
switch (c) {
case '\n': /* unix newline -> CR, LF */
cmdout(LF);
cmdout(CR);
return;
case '\r': /* \r -> CR */
cmdout(CR);
return;
default:
cmdout(c);
return;
}
}
/*
* cmdout
* write a character to the command window
* (copied from conxo)
*/
VOID cmdout (char c)
{
unsigned char buf[2];
buf[0] = c;
buf[1] = 0;
/*
* Output to terminal window if no command window.
*/
if (cmdinterminal) {
printem(ttermw, buf, 1);
return;
}
printem(ctermw, buf, 1);
}
/*
* getchar for mac console
*/
int
mac_getchar ()
{
int c;
while (cbin == cbout) { /* while buffer is empty */
if (sstate = newparser(1, 1, 0L)) {
if (sstate == 'p') /* if wakeup flag */
sstate = '\0';
else if (sstate == 'n') { /* if null command state */
sstate = '\0';
return (-3);
} else
return(-3); /* NULL command */
}
}
c = cbuf[cbout];
cbout = CINC(cbout, CBL); /* advance pointer */
return c;
}
#ifdef THINK_C
/* Just a dummy so that we can use so ckuus*.c */
char *
getenv(const char *s)
{
return (NULL); /* the Mac doesn't have an "environment" */
}
#endif /* THINK_C */
/*
* writecbc
* Store a character into the console input buffer.
*/
writecbc (c)
char c;
{
int t;
cbuf[cbin] = c;
t = CINC(cbin, CBL); /* advance pointer */
if (t == cbout) /* if buffer was full */
return; /* drop character */
cbin = t;
}
/*
* writecb
* Store a pascal string into the console input buffer.
*/
writecb (string)
char *string;
{
int l, i;
l = *string++; /* length */
for (i = 0; i < l; i++)
writecbc(*string++);
}
/* C O N B I N -- Put console in binary mode */
/* Returns 0 if ok, -1 if not */
int
conbin (char esc)
{
#pragma unused (esc)
return 0;
}
/*
* Junk so Emacs will set local variables to be compatible with Mac/MPW.
* Should be at end of file.
* this module uses 8 char tabs
*
* Local Variables:
* tab-width: 8
* End:
*/