home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / c-kermit / ckitio.c < prev    next >
C/C++ Source or Header  |  2020-01-01  |  46KB  |  1,815 lines

  1. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  2. /* |_o_o|\\ Copyright (c) 1986 The Software Distillery.  All Rights Reserved */
  3. /* |. o.| || This program may not be distributed without the permission of   */
  4. /* | .  | || the authors.                                                    */
  5. /* | o  | ||    Dave Baker     Ed Burnette  Stan Chow    Jay Denebeim        */
  6. /* |  . |//     Gordon Keener  Jack Rouse   John Toebes  Doug Walker         */
  7. /* ======          BBS:(919)-471-6436      VOICE:(919)-469-4210              */
  8. /*                                                                           */
  9. /* Contributed to Columbia University for inclusion in C-Kermit.             */
  10. /* Permission is granted to any individual or institution to use, copy, or   */
  11. /* redistribute this software so long as it is not sold for profit, provided */
  12. /* this copyright notice is retained.                                        */
  13. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
  14.  
  15. char *ckxv = "Amiga tty I/O $Id: ckitio.c,v 1.19 1999/09/12 00:42:47 swalton Exp swalton $";
  16.  
  17. /*  C K I T I O  --  Serial and Console I/O support for the Amiga */
  18.  
  19. /*
  20.  * Author: Jack Rouse, The Software Distillery
  21.  * Based on the CKUTIO.C module for Unix
  22.  *
  23.  * Modified for Manx Aztec C and Version 1.2 and forward of Amiga's OS by
  24.  * Stephen Walton of California State University, Northridge,
  25.  * srw@csun.edu.  Further mods documented in ckiker.upd.
  26.  *
  27.  
  28.  * $Log: ckitio.c,v $
  29.  * Revision 1.19  1999/09/12 00:42:47  swalton
  30.  * At some point in version 7A(195), cmdini() was moved to be called before
  31.  * sysinit().  This meant that concb(esc) could not be called from cmdini()
  32.  * in ckuus5.c as it was before.  So concb(esc) is now called by us from
  33.  * sysinit() to open the initial Kermit window.
  34.  *   In addition, this is an administrative checkin.  Due to some lost RCS
  35.  * files, the exact changes from version 1.15 to version 1.18 were lost.
  36.  *
  37.  * Revision 1.18  1998/04/17 04:13:56  swalton
  38.  * ttgwsz() added.  Now the file transfer display looks nice!
  39.  *
  40.  * Revision 1.17  1997/01/16 22:03:07  swalton
  41.  * Simple change:  instead of dying with fatal error if cannot open
  42.  * serial.device to determine defaults, just skip the initialization.
  43.  * This allows use of Kermit with, for example, telser.device on a
  44.  * dial-up Internet link..
  45.  *
  46.  * Revision 1.16  1996/11/29 10:47:22  swalton
  47.  * Added tgetent() stub to allow code to work with 6.0 release.
  48.  *
  49.  * Revision 1.15  1996/11/25 14:59:17  swalton
  50.  * Changed the name of the variable "rawcon" to "rawconfh" to avoid conflict
  51.  * with a routine of the same name.
  52.  *
  53.  * Revision 1.14  94/10/04  22:42:58  swalton
  54.  * Minor mod to flow control:  if flow is not FLO_XONX or FLO_RTSC then
  55.  * we use NONE, without error message;  this is how ck9tio.c does it.
  56.  *
  57.  * Revision 1.13  94/09/27  05:28:23  swalton
  58.  * ttsspd() was a no-op, somehow.  I think the old code was taking advantage
  59.  * of the fact that pre-5A versions of C Kermit never called ttsspd, but
  60.  * rather always went through either ttpkt() or ttvt().  It works now.
  61.  *
  62.  * Revision 1.12  94/09/11  09:44:24  swalton
  63.  * Fixed timeout in ttinl.  There was (again) no Wait() on the timer signal
  64.  * bit, so it never really timed out.  Evidence:  try to receive a file
  65.  * with no connection and modem off.  It hangs up.
  66.  *    Deleted ttwmdm() as it is no longer needed.  Wrote ttgmdm() so it works
  67.  * instead of returning 'not implemented.'
  68.  *
  69.  * Revision 1.11  94/07/29  12:25:35  swalton
  70.  * Changed both timers to use the RKM CreateTimer() and DeleteTimer() routines
  71.  * for cleanliness sake.  In looking at the code, I also realized that
  72.  * Sleeper() should be Wait()'ing on the SigBit in the MsgPort for the
  73.  * timer, not the serial port (as it was).  Not sure how this could have
  74.  * worked at all up until now!
  75.  *
  76.  * Revision 1.10  94/07/26  16:39:36  swalton
  77.  * Added code for local alarm() function to allow use of C Kermit DIAL and
  78.  * SCRIPT commands.  Now it has a lot of duplicate code for the timer.device,
  79.  * which I plan to clean up before release.
  80.  *    Also added a few strategic debug() calls.  May not be very useful with
  81.  * above, since doing a LOG DEBUG seems to slow things down enough so that
  82.  * DIAL no longer works.
  83.  *
  84.  * Revision 1.9  93/08/03  08:36:07  swalton
  85.  * Many changes thanks to Olaf Barthel:
  86.  * 1.  Changed include files to Amiga standard.
  87.  * 2.  Changed signal-handling to use ANSI signal() call.  Still can't
  88.  *     call Aztec Chk_Abort(), though, because it ignores signal().
  89.  * 3.  Used GetScreenData() on the Workbench screen to find the window
  90.  *     size to open.
  91.  * 4.  Deleted DoIOQuick() and changed calls to it to DoIO(), which is
  92.  *     identical.
  93.  * 5.. ttol() rewritten to have a static buffer whose size is checked
  94.  *     and to handle the pendwrite flag correctly.
  95.  *
  96.  * Revision 1.8  92/10/30  16:14:46  swalton
  97.  * Put in code to attempt to open a 1024 by 1024 console, at John Ata's
  98.  * suggestion.  This will make a maximum-size window on most Amigas.
  99.  *
  100.  * Added code to set a global int "v37" to TRUE or FALSE according to the
  101.  * version of the ROM Kernel.  This is then used in other places to
  102.  * conditionally turn on V37 features.
  103.  *
  104.  * Revision 1.7  92/03/16  13:50:58  swalton
  105.  * Support added for CTR/RTS flow control, using the new FLO_ manifest
  106. #include <proto/dos.h>
  107.  * constants in version 5A.
  108.  *
  109.  * Revision 1.6  92/01/15  17:12:35  swalton
  110.  * Added Long BREAK support with new ttsndlb() routine.
  111.  *
  112.  * Added support for multiple devices;  the SET LINE command now takes a
  113.  * line of the form "device/unit".
  114.  *
  115.  *  Revision 1.5  91/07/18  16:04:57  swalton
  116.  *  ttinl() now null terminates a received packet correctly.
  117.  *
  118.  *  Revision 1.4  91/05/29  09:08:57  swalton
  119.  *  1.  Changed function definitions to prototype style.  Required adding
  120.  *      a few forward declarations.
  121.  *  2.  Removed includes of stdio.h, stdlib.h, and string.h, as they are
  122.  *      now pulled in by ckcdeb.h, provided we compile with -DCK_ANSILIBS.
  123.  *
  124.  *  Revision 1.3  90/11/19  21:46:54  swalton
  125.  *  Modifications for compiling with SAS/C Version 5.10, courtesy of
  126.  *  Larry Rosenman (ler@erami.lonestar.org, ler on BIX)
  127.  *
  128.  *  Revision 1.2  90/11/07  14:42:07  swalton
  129.  *  Version 1.2--released to world as first beta test version simultaneously
  130.  *  with release of edit 5A(160).
  131.  *
  132.  *  Revision 1.1  90/07/12  22:30:11  swalton
  133.  *  Rather extensive changes were made to ckitio.c, mainly to add new functions
  134.  * required for the proper operation of C Kermit 5A(149).  They are not listed
  135.  * in detail here;  refer to the parts of the C Kermit interface document
  136.  * (file ckasys.doc in the Kermit archive) for the portions labeled *NEW*.
  137.  * These will point you at the code revisions.
  138.  *
  139.  * Revision 1.0  90/04/30  11:54:27  swalton
  140.  * Initial revision
  141.  *
  142.  */
  143.  
  144. #include "ckcdeb.h"
  145. #include "ckcker.h"
  146. #include "ckcnet.h"
  147.  
  148. #undef ULONG
  149. #undef USHORT
  150.  
  151. #include <exec/types.h>
  152. #include <exec/exec.h>
  153. #include <devices/serial.h>
  154. #include <devices/timer.h>
  155. #include <libraries/dos.h>
  156. #include <libraries/dosextens.h>
  157. #define fh_Interact fh_Port
  158. #define fh_Process fh_Type
  159. #include <intuition/intuition.h>
  160. #include <intuition/intuitionbase.h>
  161. #define BREAKSIGS (SIGBREAKF_CTRL_C|SIGBREAKF_CTRL_D)
  162. #include <string.h>
  163. #include <time.h>
  164.  
  165. #ifdef AZTEC_C
  166. #include <fcntl.h>
  167. #include <signal.h>
  168. char *ckxsys = " Commodore Amiga (Aztec_C)";    /* system name */
  169. #else
  170. #ifdef __SASC
  171. #include <fcntl.h>
  172. #include <signal.h>
  173. #include <ios1.h>        /* defines ufbs structure */
  174. char *ckxsys = " Commodore Amiga (SAS/C)";    /* system name */
  175. #endif
  176. #endif
  177.  
  178. #include <clib/exec_protos.h>
  179. #include <clib/alib_protos.h>
  180. #include <clib/dos_protos.h>
  181. #include <clib/intuition_protos.h>
  182.  
  183. /* external definitions */
  184. UBYTE *dftty = (UBYTE *) SERIALNAME;    /* serial device name */
  185. int dfloc = 1;                /* serial line is external */
  186. int dfprty = 0;                /* default parity is none */
  187. int ttprty = 0;                /* parity in use */
  188. int dfflow = FLO_XONX;            /* default flow control is on */
  189. int backgrd = 0;            /* default to foreground */
  190. int ckxech = 0;                /* echo in case redirected stdin */
  191. int tvtflg = 0;
  192. int ttcarr = 0;                /* Carrier detection mode */
  193. extern int ttnproto;            /* Protocol for network device */
  194. extern int tlevel, escape;        /* Take level & escape character */
  195.  
  196. struct Process *CurProc;        /* current process */
  197. struct CommandLineInterface *CurCLI;    /* current CLI info */
  198. struct IntuitionBase *IntuitionBase;    /* ptr to Intuition lib */
  199. short v37;                /* Are we version 37? */
  200.  
  201. /* static definitions */
  202. static struct MsgPort *serport;        /* message port for serial comm */
  203. static struct MsgPort *conport;        /* console packet port */
  204. static struct timerequest *TimerIOB;    /* timer request */
  205. static struct IOExtSer *ReadIOB;    /* serial input request */
  206. static struct IOExtSer *WriteIOB;    /* serial output request */
  207. static struct DosPacket *conpkt;    /* console I/O packet */
  208. static WORD serialopen;            /* true iff serial device open */
  209. static WORD pendwrite;            /* true iff WriteIOB in use */
  210. static WORD pendread;            /* true iff ReadIOB in use */
  211. static WORD pendconsole;        /* true when console read pending */
  212. static int queuedser;            /* serial pushback char or -1 */
  213. static UBYTE serbufc;            /* char buffer for read ahead I/O */
  214. #define NTTOQ 64            /* connect output queue size */
  215. static char ttoq[NTTOQ];        /* connect output queue */
  216. static int nttoq;            /* number of chars in ttoq */
  217. static int pttoq;            /* next char to output in ttoq */
  218. static int queuedcon;            /* contti pushback char or -1 */
  219. static LONG intsigs;            /* signals for aborting serial I/O */
  220. static BPTR rawconfh;            /* file handle for RAW: window */
  221. static BPTR saverr;                     /* saved stderr file handle */
  222. static APTR savewindow;            /* saved process WindowPtr */
  223. static APTR pushwindow;            /* pushed process WindowPtr */
  224. static struct DateStamp prevtime;    /* saved time value */
  225.  
  226.  
  227. /* AmigaDOS support (from ckiutl.c) */
  228. struct DosPacket *CreatePacket(void);
  229. VOID DeletePacket(struct DosPacket *);
  230.  
  231. #ifdef AZTEC_C
  232. /* translate Unix file handle (0, 1, or 2) to AmigaDOS file handle */
  233. #define DOSFH(n) (_devtab[n].fd)
  234. /* translate Unix file handle (0, 1, or 2) to Aztec file handle */
  235. #define FILENO(n) (n)
  236. extern int Enable_Abort;
  237. #else
  238. /* Lattice runtime externals */
  239. #ifdef __SASC
  240. #define DOSFH(n) (chkufb(n)->ufbfh)
  241. #define FILENO(n) (n)
  242. #endif
  243. #endif
  244.  
  245. /*
  246.  * Under ANSI C, pointer-pointer assignments are illegal without an
  247.  * explicit cast.  So, we define the following to make such casts short.
  248.  */
  249. #define IOR struct IORequest
  250.  
  251. /*
  252.  * Forward declarations
  253.  */
  254. void reqres(void);
  255. static void testint(long);
  256. #ifdef AZTEC_C
  257. #define Chk_Abort() testint(0L)
  258. #else
  259. void Chk_Abort(void);        /* or #define Chk_Abort() testint(0) */
  260. #endif
  261.  
  262. /*
  263.  * make note of a serial error and quit
  264.  */
  265. static void
  266. Fail(char *msg)
  267. {
  268.     syscleanup();
  269.     fprintf(stderr, msg);
  270.     fprintf(stderr, "\n");
  271.     exit(2);
  272. }
  273.  
  274. void
  275. emergency(void) {
  276.     (void) syscleanup();
  277. }
  278.  
  279. /*
  280.  * Timer.device routines from RKM, slightly modified.
  281.  */
  282.  
  283. void
  284. DeleteTimer(struct timerequest *tr) {
  285.    struct MsgPort *tp;
  286.  
  287.    if (tr != 0) {
  288.       tp = tr->tr_node.io_Message.mn_ReplyPort;
  289.       if (tp != 0)
  290.          DeletePort(tp);
  291.       CloseDevice((struct IORequest *) tr);
  292.       DeleteExtIO((struct IORequest *) tr);
  293.    }
  294. }
  295.  
  296. struct timerequest *
  297. CreateTimer(ULONG unit) {
  298.    /* return a pointer to a timer request.  If any problem, return NULL. */
  299.  
  300.    LONG error;
  301.    struct MsgPort *timerport;
  302.    struct timerequest *timermsg;
  303.  
  304.    timerport = CreatePort(0, 0);
  305.    if (timerport == NULL)
  306.       return NULL;
  307.    timermsg = (struct timerequest *)
  308.               CreateExtIO(timerport, sizeof(struct timerequest));
  309.    if (timermsg == NULL) {
  310.       DeletePort(timerport);
  311.       return NULL;
  312.    }
  313.    error = OpenDevice((UBYTE *) TIMERNAME, unit,
  314.                       (struct IORequest *) timermsg, 0L);
  315.    if (error != 0) {
  316.       DeleteTimer(timermsg);
  317.       return NULL;
  318.    }
  319.    return timermsg;
  320. }
  321.  
  322. /*
  323.  *  sysinit -- Amiga specific initialization
  324.  */
  325. int
  326. sysinit(void) {
  327.     struct IOExtSer *iob;
  328.     extern char uidbuf[];
  329.  
  330.     /* set current process info */
  331.     CurProc = (struct Process *)FindTask((char *)NULL);
  332.     CurCLI = (struct CommandLineInterface *)BADDR(CurProc->pr_CLI);
  333.     backgrd = (CurCLI == NULL || CurCLI->cli_Background);
  334.     savewindow = CurProc->pr_WindowPtr;
  335.  
  336.     signal(SIGINT, SIG_IGN);
  337.  
  338.     /* allocate console ports and IO blocks */
  339.     if ((conport = CreatePort((char *)NULL, 0L)) == NULL)
  340.         Fail("no console MsgPort");
  341.     if ((conpkt = CreatePacket()) == NULL)
  342.         Fail("no console packet");
  343.  
  344.     /* allocate serial ports and IO blocks */
  345.     if ((serport = CreatePort((char *)NULL, 0L)) == NULL)
  346.         Fail("no serial MsgPort");
  347.     iob = (struct IOExtSer *)CreateExtIO(serport,(LONG)sizeof(*iob));
  348.     if ((WriteIOB = iob) == NULL) Fail("no WriteIOB");
  349.     iob = (struct IOExtSer *)CreateExtIO(serport,(LONG)sizeof(*iob));
  350.     if ((ReadIOB = iob) == NULL) Fail("no ReadIOB");
  351.  
  352.     /* open the timer device */
  353.     TimerIOB = CreateTimer(UNIT_VBLANK);
  354.     if (TimerIOB == NULL) Fail("no TimerIOB");
  355.  
  356.     /* open the Intuition library */
  357.     if (!IntuitionBase &&
  358.         (IntuitionBase = (struct IntuitionBase *)
  359.          OpenLibrary((UBYTE *) "intuition.library", 0L) ) == NULL )
  360.       Fail("can't open Intuition");
  361.  
  362.     if (((struct Library *)IntuitionBase)->lib_Version >= 37)
  363.         v37 = TRUE;
  364.     else
  365.         v37 = FALSE;
  366.     /* open the serial device to get configuration */
  367.     iob->io_SerFlags = SERF_SHARED;
  368.     if (OpenDevice((UBYTE *) SERIALNAME, 0L, (IOR *)iob, 0L) == 0) {
  369.                 /* set parameters from system defaults */
  370.                 if (!(iob->io_SerFlags & SERF_XDISABLED))
  371.                         dfflow = FLO_XONX;
  372.                 else if (iob->io_SerFlags & SERF_7WIRE)
  373.                         dfflow = FLO_RTSC;
  374.                 else
  375.                         dfflow = FLO_NONE;
  376.                 /*
  377.                  * Set default (startup) parity from Preferences settings.
  378.                  */
  379.                 if (iob->io_SerFlags & SERF_PARTY_ON)   /* Parity is on */
  380.           if (iob->io_ExtFlags & SEXTF_MSPON)     /* Space or mark */
  381.             if (iob->io_ExtFlags & SEXTF_MARK)
  382.               dfprty = 'm';           /* Mark parity */
  383.             else
  384.               dfprty = 's';           /* Space parity */
  385.           else                                    /* Even or odd */
  386.             if (iob->io_SerFlags & SERF_PARTY_ODD)
  387.               dfprty = 'o';           /* Odd parity */
  388.             else
  389.               dfprty = 'e';           /* Even parity */
  390.                 else
  391.           dfprty = 0;                             /* No parity. */
  392.                 ttprty = dfprty;
  393.                 CloseDevice((IOR *)iob);
  394.     }
  395.         serialopen = FALSE;
  396.     atexit(emergency);
  397.     if (tlevel < 0)
  398.       (void) concb((char) escape);
  399.     strncpy(uidbuf,"AMIGAUSER",UIDBUFLEN);
  400.     return(0);
  401. }
  402.  
  403. char *
  404. whoami() {
  405.     extern char uidbuf[];
  406.     return((char *)uidbuf;
  407. }
  408.  
  409. unsigned aalarm(unsigned);    /* forward declaration */
  410.  
  411. /*
  412.  * syscleanup -- Amiga specific cleanup
  413.  */
  414. syscleanup(void)
  415. {
  416.     /* close everything */
  417.     aalarm(0);
  418.     if (serialopen) CloseDevice((IOR *)ReadIOB);
  419.     if (TimerIOB) DeleteTimer(TimerIOB);
  420.     if (WriteIOB) DeleteExtIO((IOR *)WriteIOB);
  421.     if (ReadIOB) DeleteExtIO((IOR *)ReadIOB);
  422.     if (serport) DeletePort(serport);
  423.     if (conpkt) DeletePacket(conpkt);
  424.     if (conport) DeletePort(conport);
  425.     reqres();
  426.     if (IntuitionBase)
  427.     {
  428.         CloseLibrary((struct Library *)IntuitionBase);
  429.         IntuitionBase = NULL;
  430.     }
  431.  
  432.     /* reset standard I/O */
  433.     if (rawconfh > 0)
  434.     {
  435.         /* restore Lattice AmigaDOS file handles */
  436.         DOSFH(0) = Input();
  437.         DOSFH(1) = Output();
  438.         DOSFH(2) = saverr;
  439.         Close(rawconfh);
  440.         rawconfh = 0;
  441.     }
  442.     serialopen = 0;
  443.     TimerIOB = WriteIOB = ReadIOB = serport = conpkt = conport = NULL;
  444.     return 1;
  445. }
  446.  
  447. /*
  448.  * reqoff -- turn requestors off
  449.  *    When AmigaDOS encounters an error that user intervention can fix
  450.  *    (like inserting the correct disk), it normally puts up a requestor.
  451.  *    The following code disables requestors, causing an error to be
  452.  *    returned instead.
  453.  */
  454. void
  455. reqoff(void)
  456. {
  457.     pushwindow = CurProc->pr_WindowPtr;
  458.     CurProc->pr_WindowPtr = (APTR)-1;
  459. }
  460. /*
  461.  * reqpop -- restore requesters to action at last reqoff
  462.  */
  463. void
  464. reqpop(void)
  465. {
  466.     CurProc->pr_WindowPtr = pushwindow;
  467. }
  468.  
  469. /*
  470.  * reqres -- restore requestors to startup action
  471.  */
  472. void
  473. reqres(void)
  474. {
  475.     CurProc->pr_WindowPtr = savewindow;
  476. }
  477.  
  478. /*
  479.  * KillIO -- terminate an I/O request
  480.  */
  481. static int
  482. KillIO(struct IORequest *iob)
  483. {
  484.     AbortIO(iob);
  485.     return((int)WaitIO(iob));
  486. }
  487.  
  488. /*
  489.  * ttopen -- open the serial device
  490.  *    If already open, returns 0 immediately.
  491.  *    Otherwise, the ttname is compare to SERIALNAME and used to
  492.  *    open the serial device, and, if the value of *lcl is < 0, it is
  493.  *    reset to 1 indicating local mode.  Returns -1 on error.
  494.  *    timo is the length of time to wait before flunking open;  we don't
  495.  *    need this feature on the Amiga.
  496.  */
  497. int
  498. ttopen(char * ttname, int *lcl, int modem, int timo)
  499. {
  500.     struct IOExtSer *iob = ReadIOB;
  501.     char *p;
  502.     ULONG unit;
  503.     static  char cttname[50];    /* Current open ttname */
  504.  
  505.     debug(F111,"ttopen entry modem",ttname,modem);
  506.     debug(F101," lcl","",*lcl);
  507.     if (modem < 0) return -1;    /* We don't do networks yet. */
  508.     if (serialopen)            /* Already have serial device open */
  509.             if (strcmp(ttname, cttname) == 0)
  510.                 return(0);        /* Same device - ignore  */
  511.             else ttclos(0);        /* Different device - close */
  512.  
  513.     /* verify the serial name */
  514. #if 0
  515.     if (strcmp(ttname, SERIALNAME) != 0) return(-1);
  516. #endif
  517.  
  518.     /* set open modes.  We no longer open in shared mode. */
  519.     iob->io_SerFlags = (modem > 0 ? SERF_7WIRE : 0);
  520.  
  521.     /* parse device name as device/unit */
  522.     if ((p = strchr(ttname, '/')) == NULL)
  523.         unit = 0;
  524.     else {
  525.         if (*(p + strlen(p) - 1) == 's')    /* Open in shared mode */
  526.             {
  527.                 iob->io_SerFlags |= SERF_SHARED;
  528.                 *(p + strlen(p) - 1) = '\0';
  529.             }
  530.         unit = (ULONG) atoi(p + 1);
  531.         *p = '\0';
  532.     }
  533.     /* open the serial device */
  534.     if (OpenDevice((UBYTE *) ttname, unit, (IOR *)iob, 0L) != 0)
  535.         return(-1);
  536.     serialopen = TRUE;
  537.     tvtflg = 0;
  538.     pendread = pendwrite = pendconsole = FALSE;
  539.     queuedser = -1;
  540.  
  541.     /* fill in the fields of the other IO blocks */
  542.     *WriteIOB = *iob;
  543.  
  544.     /* set local mode */
  545.     if (*lcl == -1)    *lcl = 1; /* always local */
  546.     if (p) *p = '/';        /* restore slash */
  547.         if (iob->io_SerFlags & SERF_SHARED)
  548.             *(p + strlen(p)) = 's';     /* restore suffix if present */
  549.     strcpy(cttname, ttname);
  550.     debug(F110, "ttopen got device", ttname, 0);
  551.     return(0);
  552. }
  553.  
  554. /*
  555.  * StartTimer -- start a timeout
  556.  */
  557. static VOID
  558. StartTimer(LONG secs, LONG micro)
  559. {
  560.     TimerIOB->tr_node.io_Command = TR_ADDREQUEST;
  561.     TimerIOB->tr_time.tv_secs  = secs;
  562.     TimerIOB->tr_time.tv_micro = micro;
  563.     SendIO((IOR *)TimerIOB);
  564. }
  565.  
  566. /*
  567.  * SerialWait -- wait for serial I/O to terminate
  568.  *    return I/O error
  569.  */
  570. static int
  571. SerialWait(struct IOExtSer *iob, int timeout)
  572. {
  573.     LONG sigs;
  574.     struct timerequest *timer = TimerIOB;
  575.     LONG waitsigs;
  576.  
  577.     /* set up timeout if necessary */
  578.     if (timeout > 0) {
  579.         StartTimer((LONG)timeout, 0L);
  580.         waitsigs =
  581.            (1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit);
  582.     } else
  583.         waitsigs = 0;
  584.  
  585.     /* wait for completion, timeout, or interrupt */
  586.     sigs = 0;
  587.     waitsigs |= (1L << serport->mp_SigBit) |
  588.                    intsigs;
  589.     for (;;)
  590.     {
  591.         if (sigs & intsigs)
  592.         {    /* interrupted */
  593.             if (timeout > 0) KillIO((IOR *)timer);
  594.             KillIO((IOR *)iob);
  595.             testint(sigs);
  596.             return(-1);
  597.         }
  598.         if (CheckIO((IOR *)iob))
  599.         {
  600.             if (timeout > 0) KillIO((IOR *)timer);
  601.             return((int)WaitIO((IOR *)iob));
  602.         }
  603.         if (timeout > 0 && CheckIO((IOR *)timer))
  604.         {
  605.             KillIO((IOR *)iob);
  606.             WaitIO((IOR *)timer);
  607.             /* restart if XOFF'ed */
  608.             iob->IOSer.io_Command = CMD_START;
  609.             DoIO((IOR *)iob);
  610.             return(-1);
  611.         }
  612.         sigs = Wait(waitsigs);
  613.     }
  614. }
  615.  
  616. /*
  617.  * TerminateRead -- wait for queued read to finish
  618.  */
  619. static int
  620. TerminateRead(void)
  621. {
  622.     if (!pendread) return(0);
  623.     if (WaitIO((IOR *)ReadIOB) == 0) queuedser = serbufc;
  624.     pendread = FALSE;
  625.     return((int)ReadIOB->IOSer.io_Error);
  626. }
  627.  
  628. /*
  629.  * TerminateWrite -- ensure WriteIOB is ready for reuse
  630.  */
  631. static int
  632. TerminateWrite(int timeout)
  633. {
  634.     Chk_Abort();
  635.     if (!pendwrite) return(0);
  636.     pendwrite = FALSE;
  637.     if (timeout) {
  638.         timeout = WriteIOB->IOSer.io_Length * 80 / WriteIOB->io_Baud;
  639.     }
  640.     return(SerialWait(WriteIOB, timeout));
  641. }
  642.  
  643. /*
  644.  * SerialReset -- terminate pending serial and console I/O
  645.  */
  646. static void
  647. SerialReset(void)
  648. {
  649.     if (pendread)
  650.     {
  651.         AbortIO((IOR *)ReadIOB); /* should work even if read finished */
  652.         TerminateRead();
  653.     }
  654.  
  655.     if (pendconsole)
  656.     {    /* this does not happen normally */
  657.         WaitPort(conport);
  658.         GetMsg(conport);
  659.         pendconsole = FALSE;
  660.     }
  661.  
  662.     if (pendwrite)
  663.         TerminateWrite(1);
  664. }
  665.  
  666. /*
  667.  * ttres -- reset serial device
  668.  */
  669. ttres()
  670. {
  671.     if (!serialopen) return(-1);
  672.  
  673.     /* reset everything */
  674.     SerialReset();
  675.     ReadIOB->IOSer.io_Command = CMD_RESET;
  676.     tvtflg = 0;
  677.     return(DoIO((IOR *)ReadIOB) ? -1 : 0);
  678. }
  679.  
  680. /*
  681.  * ttclos -- close the serial device
  682.  */
  683. int
  684. ttclos(int unit)
  685. {
  686.     debug(F101, "ttopen ", "", unit);
  687.     if (!serialopen) return(0);
  688.     if (ttres() < 0) return(-1);
  689.     CloseDevice((IOR *)ReadIOB);
  690.     serialopen = FALSE;
  691.     tvtflg = 0;
  692.     return(0);
  693. }
  694.  
  695. /*
  696.  * tthang -- hang up phone line
  697.  *    Drops DTR by closing serial.device
  698.  */
  699. int
  700. tthang(void)
  701. {
  702.         return((serialopen) ? ttclos(0) : -1);
  703. }
  704.  
  705. /*
  706.  * ttpkt -- set serial device up for packet transmission
  707.  *    sets serial parameters
  708.  */
  709. int
  710. ttpkt(long speed, int flow, int parity)
  711. {
  712.     extern UBYTE eol;
  713.     struct IOExtSer *iob = ReadIOB;
  714.  
  715.     debug(F101, "ttpkt speed ", "", speed);
  716.     debug(F101, "ttpkt flow ", "", flow);
  717.     debug(F101, "ttpkt parity ", "", parity);
  718.     if (!serialopen || pendread) return(-1);
  719.  
  720.     /* terminate any pending writes */
  721.     TerminateWrite(1);
  722.  
  723.     /* fill in parameters */
  724.     iob->io_CtlChar = 0x11130000;
  725.     if (speed >= 0 && ttsspd((int) (speed / 10)) >= 0) iob->io_Baud = speed;
  726.     iob->io_RBufLen = speed;    /* 10 seconds worth of data */
  727.     /*
  728.      * Notice the dopar(eol) here to set the EOL character with the
  729.      * appropriate parity.  See also ttinl().
  730.      */
  731.     memset(&iob->io_TermArray, dopar(eol), sizeof(struct IOTArray));
  732.     iob->io_ReadLen = iob->io_WriteLen = 8;
  733.     iob->io_StopBits = 1;
  734.     if (flow == FLO_XONX)
  735.         iob->io_SerFlags &= ~(SERF_XDISABLED | SERF_7WIRE);
  736.     else if (flow == FLO_RTSC)
  737.         iob->io_SerFlags |= (SERF_XDISABLED | SERF_7WIRE);
  738.     else {
  739.         iob->io_SerFlags |= SERF_XDISABLED;
  740.         iob->io_SerFlags &= ~SERF_7WIRE;
  741.     }
  742.     /* if no XON/XOFF flow and high baud rate, RAD_BOOGIE is appropriate */
  743.     if (flow != FLO_XONX && iob->io_Baud >= 19200)
  744.         iob->io_SerFlags |= SERF_RAD_BOOGIE;
  745.     else
  746.         iob->io_SerFlags &= ~SERF_RAD_BOOGIE;
  747.  
  748.     /*
  749.      * Parity setting.  For packet send/receive, we turn off the
  750.      * Amiga's internal parity generation and checking, as this code
  751.      * does it itself (which makes it bigger and slower...).  We
  752.      * save the current parity for ttinl().
  753.      */
  754.  
  755.     ttprty = parity;
  756.     iob->io_SerFlags &= ~(SERF_EOFMODE|SERF_PARTY_ON|SERF_PARTY_ODD);
  757.     iob->io_ExtFlags = 0;        /* MUST BE ZERO unless Mark or Space. */
  758.  
  759.     /* set the parameters */
  760.     iob->IOSer.io_Command = SDCMD_SETPARAMS;
  761.     if (DoIO((IOR *)iob) != 0) return(-1);
  762.     tvtflg = 0;
  763.     return(ttflui());
  764. }
  765.  
  766. /*
  767.  * ttvt -- set up serial device for connect mode.  This is almost the same
  768.  * as ttpkt() on the Amiga, except we save the settings and a flag and return
  769.  * without doing anything if we've already been called with the same
  770.  * values.
  771.  */
  772. int
  773. ttvt(long speed, int flow) {
  774.     static long ospeed = -1;
  775.     static int oflow = -9;
  776.  
  777.     if (tvtflg != 0 && ospeed == speed && oflow == flow)
  778.         return 0;
  779.     if (ttpkt(speed, flow, 0) < 0)
  780.         return -1;
  781.     ospeed = speed;            /* Save speed */
  782.     oflow = flow;            /* and flow control set */
  783.     tvtflg = 1;            /* and flag we've been called */
  784.     return 0;
  785. }
  786.  
  787. /*  T T S S P D  --  Set the transmission of tty to ten times the argument */
  788.  
  789. ttsspd(speed) int speed; {
  790.     int s;
  791.     struct IOExtSer *iob = ReadIOB;
  792.  
  793.     debug (F101,"ttsspd: speed(cps):","",speed);
  794.     if (!serialopen) return(-1);
  795.  
  796.     switch (speed) {
  797.         case 5:         s = 50;        break;
  798.         case 7:         s = 75;        break;
  799.         case 11:        s = 110;       break;
  800.         case 13:        s = 134;       break;
  801.         case 15:        s = 150;       break;
  802.         case 30:        s = 300;       break;
  803.         case 60:        s = 600;       break;
  804.         case 120:       s = 1200;      break;
  805.         case 180:       s = 1800;      break;
  806.         case 200:       s = 2000;      break;
  807.         case 240:       s = 2400;      break;
  808.         case 360:       s = 3600;      break;
  809.         case 480:       s = 4800;      break;
  810.         case 720:       s = 7200;      break;
  811.         case 960:       s = 9600;      break;
  812.         case 1440:    s = 14400;     break;
  813.         case 1920:      s = 19200;     break;
  814.         case 3840:      s = 38400;     break;
  815.         case 5760:    s = 57600;     break;
  816.         case 888:       return(-1); /* no 75/1200 split speed */
  817.         default:        return -1;
  818.     }
  819.     /* First get a complete copy of current settings. */
  820.     iob->IOSer.io_Command = SDCMD_QUERY;
  821.     if (DoIO((IOR *)iob) != 0) return(-1);
  822.     iob->io_Baud = s;
  823.     iob->io_RBufLen = s;    /* 10 seconds worth of data */
  824.     /* set the parameters */
  825.     iob->IOSer.io_Command = SDCMD_SETPARAMS;
  826.     if (DoIO((IOR *)iob) != 0) return(-1);
  827.  
  828.     return s;
  829.  
  830. }
  831.  
  832. /* T T G S P D  -  Get speed of currently selected tty line  */
  833.  
  834. /*
  835.   Read speed from serial.device, or, if not open, return the value in
  836.   the current ReadIOB.
  837. */
  838. long
  839. ttgspd(void) {                /* Get current tty speed */
  840.     struct IOExtSer *myread = ReadIOB;
  841.  
  842.     if (!serialopen)
  843.         if (myread != NULL) return((long)myread->io_Baud);
  844.         else return -1;
  845.     Chk_Abort();
  846.     if (pendread && !CheckIO((IOR *)myread)) return(0);
  847.     if (TerminateRead() != 0) return(-1);
  848.     myread->IOSer.io_Command = SDCMD_QUERY;
  849.     return((DoIO((IOR *)myread) == 0)
  850.             ? (long)myread->io_Baud
  851.             : -1);
  852. }
  853.  
  854. /*
  855.  * ttflui -- flush serial device input buffer
  856.  */
  857. int
  858. ttflui(void)
  859. {
  860.     if (!serialopen || pendread) return(-1);
  861.     queuedser = -1;
  862.     ReadIOB->IOSer.io_Command = CMD_CLEAR;
  863.     return(DoIO((IOR *)ReadIOB) ? -1 : 0);
  864. }
  865.  
  866. /*
  867.  * ttfluo -- flush serial output buffer
  868.  */
  869. int
  870. ttfluo(void)
  871. {
  872.     if (!serialopen || pendwrite) return -1;
  873.     WriteIOB->IOSer.io_Command = CMD_CLEAR;
  874.     return(DoIO((IOR *)WriteIOB) ? -1 : 0);
  875. }
  876.  
  877.  
  878. /*
  879.  * test for and catch interrupt
  880.  */
  881. static void
  882. testint(LONG sigs)
  883. {
  884.     /* test for and reset caught interrupt signals */
  885.     if ((sigs | SetSignal(0L, (LONG)BREAKSIGS)) & intsigs) {
  886.         raise(SIGINT);
  887.     }
  888. }
  889.  
  890. /*
  891.  * conint -- set console interrupt handler and suspend handler.
  892.  */
  893. void
  894. conint(SIGTYP (*newhdlr)(int), SIGTYP (*stophdlr)(int))
  895. {
  896.     Chk_Abort();            /* handle any pending interrupts */
  897.     signal(SIGINT, newhdlr);    /* set the new handler */
  898.     intsigs = BREAKSIGS;        /* note signal caught */
  899. }
  900.  
  901. /*
  902.  * connoi -- disable interrupt trapping
  903.  */
  904. void
  905. connoi(void)
  906. {
  907.     signal(SIGINT, SIG_IGN);    /* disable interrupts */
  908.     intsigs = 0;            /* note signal ignored */
  909.     Chk_Abort();            /* ignore pending interrupts */
  910. }
  911.  
  912. /*
  913.  * ttchk -- return number of chars immediately available from serial device
  914.  */
  915. int
  916. ttchk(void)
  917. {
  918.     struct IOExtSer *myread = ReadIOB;
  919.  
  920.     if (!serialopen) return(-1);
  921.     Chk_Abort();
  922.     if (pendread && !CheckIO((IOR *)myread)) return(0);
  923.     if (TerminateRead() != 0) return(-1);
  924.     myread->IOSer.io_Command = SDCMD_QUERY;
  925.     return((DoIO((IOR *)myread) == 0)
  926.             ? ((queuedser >= 0 ? 1 : 0) + (int)myread->IOSer.io_Actual)
  927.             : -1);
  928. }
  929.  
  930. /*
  931.  * ttxin -- get n characters from serial device.  This routine should
  932.  * only be called when we know that there are at least n characters
  933.  * ready to be read.
  934.  */
  935. int
  936. ttxin(int n, CHAR *buf)
  937. {
  938.         return(ttinl(buf, n, 0, 0));
  939. }
  940.  
  941. #ifdef PARSENSE
  942.  
  943. extern CHAR partab[];
  944.  
  945. /*  P A R C H K  --  Check if Kermit packet has parity  */
  946.  
  947. /*
  948.   Call with s = pointer to packet, start = packet start character, n = length.
  949.   Returns 0 if packet has no parity, -1 on error, or if packet has parity:
  950.     'e' for even, 'o' for odd, 'm' for mark.  Space parity cannot be sensed.
  951. */
  952. parchk(s,start,n) CHAR *s, start; int n; {
  953.     CHAR s0, s1, s2, s3, sn;
  954.  
  955.     debug(F101,"parchk n","",n);
  956.     debug(F101,"parchk start","",start);
  957.     debug(F110,"parchk s",s,0);
  958.  
  959.     s0 = s[0] & 0x7f;            /* Mark field (usually Ctrl-A) */
  960.  
  961.     if (s0 != start || n < 5) return(-1); /* Not a valid packet */
  962.  
  963. /* Look at packet control fields, which never have 8th bit set */
  964. /* First check for no parity, most common case. */
  965.  
  966.     if (((s[0] | s[1] | s[2] | s[3] | s[n-2]) & 0x80) == 0)
  967.       return(0);            /* No parity */
  968.  
  969. /* Check for mark parity */
  970.  
  971.     if (((s[0] & s[1] & s[2] & s[3] & s[n-2]) & 0x80) == 0x80)
  972.       return('m');            /* Mark parity */
  973.  
  974. /* Packet has some kind of parity */
  975. /* Make 7-bit copies of control fields */
  976.  
  977.     s1 = s[1] & 0x7f;            /* LEN */
  978.     s2 = s[2] & 0x7f;            /* SEQ */
  979.     s3 = s[3] & 0x7f;            /* TYPE */
  980.     sn = s[n-2] & 0x7f;            /* CHECK */
  981.  
  982. /* Check for even parity */
  983.  
  984.     if ((s[0] == partab[s0]) &&
  985.         (s[1] == partab[s1]) &&
  986.         (s[2] == partab[s2]) &&
  987.     (s[3] == partab[s3]) &&
  988.     (s[n-2] == partab[sn]))
  989.       return('e');
  990.  
  991. /* Check for odd parity */
  992.  
  993.     if ((s[0] != partab[s0]) &&
  994.         (s[1] != partab[s1]) &&
  995.         (s[2] != partab[s2]) &&
  996.     (s[3] != partab[s3]) &&
  997.     (s[n-2] != partab[sn]))
  998.       return('o');
  999.  
  1000. /* Otherwise it's probably line noise.  Let checksum calculation catch it. */
  1001.  
  1002.     return(-1);
  1003. }
  1004. #endif /* PARSENSE */
  1005.  
  1006. /*
  1007.  * ttinc -- read character from serial line
  1008.  */
  1009. int
  1010. ttinc(int timeout)
  1011. {
  1012.     UBYTE ch;
  1013.  
  1014.     return((ttinl((CHAR *)&ch, 1, timeout, 0) > 0) ? (int)ch : -1);
  1015. }
  1016.  
  1017. /*
  1018.  * The following chunk of code is a primitive (very!) alarm() function
  1019.  * for the Amiga.  It is nowhere near general, and it will only work
  1020.  * with Kermit, most likely.  It has three parts:
  1021.  *
  1022.  * asignal() is call-compatible with signal().  If the signal is less
  1023.  * than or equal to _NUMSIG (in <signal.h>), then the vendor-supplied
  1024.  * signal() is called.  If it is equal to _NUMSIG+1, which I define as
  1025.  * SIGALRM, then it is a new alarm signal.  The pointer to the passed
  1026.  * function is saved and the old one is returned.
  1027.  *
  1028.  * aalarm() is the Unix-like call.  It is called with a time in seconds,
  1029.  * which is the time after which the routine passed in the
  1030.  * signal(SIGALRM, ...) call is to be called.  Here we just start a
  1031.  * timer and return.
  1032.  *
  1033.  * check_alarm() sees if the time specified by aalarm() is up yet, and
  1034.  * calls the saved function if it is.
  1035.  */
  1036.  
  1037. static void (*savalarm)(int) = SIG_DFL;
  1038. static struct timerequest *alarmIOB;
  1039. static unsigned savesecs;
  1040. static short alarmflag = 0;        /* flag that an alarm is pending */
  1041. #define SIGALRM (_NUMSIG+1)
  1042.  
  1043. void (*asignal(int sig, void (*func)(int)))(int) {
  1044.  
  1045.     void (*talarm)(int);
  1046.     debug(F101, "asignal sig", "", sig);
  1047.     debug(F101, "asignal func", "", func);
  1048.     if (sig <= _NUMSIG)
  1049.         return(signal(sig, func));
  1050.     else if (sig == SIGALRM) {
  1051.         talarm = savalarm;
  1052.         savalarm = func;
  1053.         return(talarm);
  1054.     }
  1055.     else {
  1056.         debug(F100, "asignal called with sig too large", "", 0);
  1057.         return(SIG_IGN);
  1058.     }
  1059. }
  1060.  
  1061. unsigned
  1062. aalarm(unsigned secs) {
  1063.     unsigned t;
  1064.  
  1065.     debug(F101, "aalarm", "", secs);
  1066.     t = savesecs;
  1067.     if (secs == 0) {
  1068.         if (alarmIOB) {
  1069.             KillIO((IOR *) alarmIOB);
  1070.             DeleteTimer(alarmIOB);
  1071.             alarmIOB = NULL;
  1072.         }
  1073.         savesecs = 0;
  1074.         alarmflag = 0;
  1075.         return(t);
  1076.     } else {
  1077.         alarmIOB = CreateTimer(UNIT_VBLANK);
  1078.         if (alarmIOB == NULL) {
  1079.             debug(F100, "CreateExtIO failed in alarm", "", 0);
  1080.             return(0);
  1081.         }
  1082.         alarmIOB->tr_time.tv_secs = savesecs = secs;
  1083.         alarmIOB->tr_time.tv_micro = 0;
  1084.         alarmIOB->tr_node.io_Command = TR_ADDREQUEST;
  1085.         SendIO((IOR *) alarmIOB);
  1086.         alarmflag = 1;
  1087.         return(t);
  1088.     }
  1089. }
  1090.  
  1091. static void
  1092. check_alarm(void) {
  1093.  
  1094.     if (alarmflag)
  1095.         if (CheckIO((IOR *) alarmIOB)) {
  1096.             WaitIO((IOR *) alarmIOB);
  1097.             alarmflag = 0;
  1098.             if (savalarm == SIG_IGN)
  1099.                 return;
  1100.             else if (savalarm == SIG_DFL)
  1101.                 Fail("uncaught alarm seen");
  1102.             else
  1103.                 (*savalarm)(SIGALRM);
  1104.         }
  1105. }
  1106.  
  1107. /*
  1108.  * ttol -- write n chars to serial device.  For small writes, we have
  1109.  * a small local buffer which allows them to run asynchronously.  For
  1110.  * large writes, we do them synchronously.  This seems to be the best
  1111.  * compromise between speed and code simplicity and size.
  1112.  *
  1113.  * Stephen Walton, 23 October 1989
  1114.  */
  1115. int
  1116. ttol(CHAR *buf, int n)
  1117. {
  1118.     struct IOExtSer *mywrite = WriteIOB;
  1119.     static char outbuf[1024];    /* safe place for output characters */
  1120.     int s;
  1121.     int oldn = n;
  1122.  
  1123.     if (!serialopen) return(-1);
  1124.     check_alarm();
  1125.     if ((s = n - sizeof(outbuf)) > 0) {
  1126.         if (TerminateWrite(1) != 0) return(-1);
  1127.         mywrite->IOSer.io_Command = CMD_WRITE;
  1128.         mywrite->IOSer.io_Data  = (APTR) buf;
  1129.         mywrite->IOSer.io_Length = s;
  1130.         SendIO((IOR *)mywrite);
  1131.         pendwrite = TRUE;
  1132.         buf += s;
  1133.         n   -= s;
  1134.         memcpy(outbuf, buf, n);
  1135.         if (TerminateWrite(1) != 0) return(-1);
  1136.     } else {
  1137.         if (TerminateWrite(1) != 0) return(-1);
  1138.         memcpy(outbuf, buf, n);
  1139.     }
  1140.     mywrite->IOSer.io_Command = CMD_WRITE;
  1141.     mywrite->IOSer.io_Data    = (APTR)outbuf;
  1142.     mywrite->IOSer.io_Length  = n;
  1143.     SendIO((IOR *)mywrite);
  1144.     pendwrite = TRUE;
  1145.  
  1146.     return oldn;
  1147. }
  1148.  
  1149. /*
  1150.  * ttoc -- output single character to serial device
  1151.  */
  1152. int
  1153. ttoc(char c)
  1154. {
  1155.         return(ttol((CHAR *) &c, 1));
  1156. }
  1157.  
  1158. /*
  1159.  * ttinl -- read from serial device, possibly with timeout and eol character
  1160.  *    reads up to n characters, returning the number of characters read
  1161.  *    if eol > 0, reading the eol character will terminate read
  1162.  *    if timeout > 0, terminates read if timeout elapses
  1163.  *    returns -1 on error, such as timeout or interrupt
  1164.  *
  1165.  *    Note that this is the single routine which does all character reading
  1166.  *    in Amiga C Kermit, and has some added "features" compared to, say,
  1167.  *    the Unix version.  If timeout is 0, this routine waits forever.
  1168.  *    If eol is zero, it is not used.
  1169.  *
  1170.  *    New for 5A(157) is the start parameter, which is the start-of-packet
  1171.  *    character.  Following the Unix example, we just read until eol,
  1172.  *    but return a bad packet if the first character we got doesn't agree
  1173.  *    with start.
  1174.  */
  1175. int
  1176. ttinl(CHAR *buf, int n, int timeout, CHAR eol)
  1177. {
  1178.         unsigned  mask;
  1179.     struct IOExtSer *myread = ReadIOB;
  1180.     int count;
  1181.     int nread, i;
  1182.  
  1183.     Chk_Abort();
  1184.     check_alarm();
  1185.      if (!serialopen || pendread || n <= 0) return(-1);
  1186.  
  1187.     mask = (ttprty ? 0177 : 0377);    /* parity stripping mask */
  1188.  
  1189.     /* handle pushback */
  1190.     if (queuedser >= 0)
  1191.     {
  1192.         *buf = queuedser & mask;    /* Strip queued character. */
  1193.         queuedser = -1;
  1194.         if (*buf == eol || n == 1) return(1);
  1195.         ++buf;
  1196.         --n;
  1197.         count = 1;
  1198.     }
  1199.     else
  1200.         count = 0;
  1201.  
  1202.     /* set up line terminator */
  1203.     if (eol > 0)
  1204.     {
  1205.         /*
  1206.          * For reasons which are obscure to me, this batch of
  1207.          * code generally fails.  Normally, this doesn't matter,
  1208.          * because io_TermArray is set in ttpkt() above, and so
  1209.          * this code is only executed if eol changes as a result
  1210.          * of the initial packet negotiation.  I found the bug
  1211.          * by inadvertently not using dopar(eol) in the setting
  1212.          * of io_TermArray in ttpkt(), which did cause this code
  1213.          * to be called if parity was MARK or EVEN (since in that
  1214.          * case dopar(eol) != eol).
  1215.          */
  1216.  
  1217.         if (dopar(eol) != *(UBYTE *)&myread->io_TermArray)
  1218.         {
  1219.             memset(&myread->io_TermArray, dopar(eol),
  1220.                    sizeof(struct IOTArray));
  1221.             myread->IOSer.io_Command = SDCMD_SETPARAMS;
  1222.             if (DoIO((IOR *)myread) != 0) {
  1223.                 debug(F111, "SETPARAMS fails in ttinl()",
  1224.                       "io_Error", (int) myread->IOSer.io_Error);
  1225.                 myread->io_TermArray.TermArray0 =
  1226.                     myread->io_TermArray.TermArray1 = 0xffffffffu;
  1227.                 return -1;
  1228.             }
  1229.         }
  1230.         myread->io_SerFlags |= SERF_EOFMODE;
  1231.     }
  1232.     else
  1233.         myread->io_SerFlags &= ~SERF_EOFMODE;
  1234.  
  1235.     /* set up the read */
  1236.     myread->IOSer.io_Command = CMD_READ;
  1237.     myread->IOSer.io_Data    = (APTR)buf;
  1238.     myread->IOSer.io_Length  = n;
  1239.  
  1240.     /* perform read quickly if possible */
  1241.     myread->IOSer.io_Flags = IOF_QUICK;
  1242.     BeginIO((IOR *)myread);
  1243.     if (myread->IOSer.io_Flags & IOF_QUICK)
  1244.         myread->IOSer.io_Flags = 0;
  1245.     else
  1246.         /* wait for read to complete if no QUICK. */
  1247.         if (SerialWait(myread, timeout) != 0)
  1248.             return -1;
  1249.  
  1250.     if (myread->IOSer.io_Error != 0)
  1251.         return -1;
  1252. #if COMMENT
  1253.     if (start != 0 && (buf[0] & mask) != start) /* Bad packet */
  1254.         return -1;
  1255. #endif
  1256.     /* Strip parity bits if need be. */
  1257.     nread = (int) myread->IOSer.io_Actual;
  1258.     if (ttprty)
  1259.         for (i = 0; i < nread; i++)
  1260.             buf[i] &= mask;
  1261.     if (nread > 1)
  1262.         buf[nread] = '\0';        /* Null terminate */
  1263.     return(count + nread);
  1264. }
  1265.  
  1266. /*
  1267.  * Sleeper -- perform an interruptible timeout
  1268.  */
  1269. static int
  1270. Sleeper(LONG secs, LONG micro)
  1271. {
  1272.     LONG sigs;
  1273.     LONG waitsigs;
  1274.     struct timerequest *timer = TimerIOB;
  1275.  
  1276.     if (!TimerIOB) return(-1);
  1277.     if (secs == 0 && micro <= 2)
  1278.         return(0);
  1279.     StartTimer(secs, micro);
  1280.     sigs = 0;
  1281.     waitsigs = (1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit) | intsigs;
  1282.     for (;;)
  1283.     {
  1284.         if (CheckIO((IOR *)timer))
  1285.         {
  1286.             WaitIO((IOR *)timer);
  1287.             return(0);
  1288.         }
  1289.         if (sigs & intsigs)
  1290.         {
  1291.             KillIO((IOR *)timer);
  1292.             testint(sigs);
  1293.             return(-1);
  1294.         }
  1295.         sigs = Wait(waitsigs);
  1296.     }
  1297. }
  1298.  
  1299. /*
  1300.  * sleep -- wait n seconds
  1301.  */
  1302. int
  1303. sleep(int n)
  1304. {    return(Sleeper((LONG)n, 0L)); }
  1305.  
  1306. /*
  1307.  * msleep -- wait n milliseconds
  1308.  */
  1309. int
  1310. msleep(int m)
  1311. {    return(Sleeper((LONG)(m / 1000), (m % 1000) * 1000L)); }
  1312.  
  1313.  
  1314. /*
  1315.  * rtimer -- reset elapsed time
  1316.  */
  1317. void
  1318. rtimer(void)
  1319. {    DateStamp(&prevtime); }
  1320.  
  1321. /*
  1322.  * gtimer -- get currently elapsed time in seconds
  1323.  */
  1324. int
  1325. gtimer(void)
  1326. {
  1327.     int x;
  1328.     struct DateStamp curtime;
  1329.  
  1330.     DateStamp(&curtime);
  1331.     x = ((curtime.ds_Days   - prevtime.ds_Days  ) * 1440 +
  1332.          (curtime.ds_Minute - prevtime.ds_Minute) ) * 60 +
  1333.          (curtime.ds_Tick   - prevtime.ds_Tick  ) / 50;
  1334.     return((x < 0) ? 0 : x );
  1335. }
  1336.  
  1337. /*
  1338.  * ztime -- format current date and time into string
  1339.  */
  1340. void
  1341. ztime(char **s)
  1342. {
  1343.     time_t xclock;
  1344.  
  1345.     (void) time(&xclock);
  1346.     *s = asctime(localtime(&xclock));
  1347. }
  1348.  
  1349. /*
  1350.  * congm -- save console modes
  1351.  */
  1352. int
  1353. congm(void)
  1354. {
  1355.     if (!saverr) saverr = DOSFH(2);
  1356.     return(0);
  1357. }
  1358.  
  1359. /*
  1360.  * CreateWindow -- create window and jam it into standard I/O
  1361.  */
  1362. int
  1363. CreateWindow(int esc)
  1364. {
  1365.     char rawname[48];
  1366.     struct Screen s;
  1367.  
  1368.     if (rawconfh > 0) return(0);
  1369.     congm();
  1370.  
  1371.     if (GetScreenData(&s, sizeof(s), WBENCHSCREEN, NULL) == 0) {
  1372.         s.Width = 640;
  1373.         s.Height = 200;
  1374.     }
  1375.     sprintf(rawname, "RAW:0/1/%d/%d/Kermit%s", s.Width, s.Height - 1,
  1376.         v37? "/ALT0/1/100/30" : "");
  1377.     rawconfh = Open((UBYTE *) rawname, (LONG)MODE_NEWFILE);
  1378.       if (rawconfh == 0)
  1379.           return(-1);
  1380.     DOSFH(0) = DOSFH(1) = DOSFH(2) = rawconfh;
  1381.  
  1382.     /* if we create a window, don't abort on errors or echo */
  1383.     backgrd = FALSE;
  1384.     ckxech = 1;
  1385.     return(0);
  1386. }
  1387.  
  1388. /*
  1389.  * concb -- put console in single character wakeup mode
  1390.  */
  1391. int
  1392. concb(char esc)
  1393. {
  1394.     if (rawconfh) return(0);
  1395.     if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
  1396.         return(0);
  1397.     return(CreateWindow(esc));
  1398. }
  1399.  
  1400. /*
  1401.  * conbin -- put console in raw mode
  1402.  */
  1403. int
  1404. conbin(char esc)
  1405. {
  1406.     if (rawconfh) return(0);
  1407.     if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
  1408.         return(isatty(0) ? 0 : -1);
  1409.     return(CreateWindow(esc));
  1410. }
  1411.  
  1412. /*
  1413.  * conres -- restore console
  1414.  *    we actually restore in syscleanup()
  1415.  */
  1416. conres()
  1417. {
  1418.         return(0);
  1419. }
  1420.  
  1421. /*
  1422.  * conoc -- output character to console
  1423.  */
  1424. int
  1425. conoc(char c)
  1426. {
  1427.     putchar(c);
  1428.     fflush(stdout);
  1429.     Chk_Abort();
  1430.     return c;
  1431. }
  1432.  
  1433. /*
  1434.  * conxo -- output x chars to console
  1435.  */
  1436. int
  1437. conxo(int n, char *buf)
  1438. {
  1439.     int retval;
  1440.  
  1441.     fflush(stdout);
  1442.     retval = write(FILENO(1), buf, n);
  1443.     Chk_Abort();
  1444.     return retval;
  1445. }
  1446.  
  1447. /*
  1448.  * conol -- output line to console
  1449.  */
  1450. int
  1451. conol(char *l)
  1452. {
  1453.     int retval;
  1454.     retval = fputs(l, stdout);
  1455.     fflush(stdout);
  1456.     Chk_Abort();
  1457.     return retval;
  1458. }
  1459.  
  1460. /*
  1461.  * conola -- output line array to console
  1462.  */
  1463. int
  1464. conola(char **l)
  1465. {
  1466.     for (; **l; ++l)
  1467.         if (conol(*l) < 0)
  1468.             return(-1);
  1469.     return 0;
  1470. }
  1471.  
  1472. /*
  1473.  * conoll -- output line with CRLF
  1474.  */
  1475. int
  1476. conoll(char *l)
  1477. {
  1478.     if (conol(l) < 0)
  1479.         return -1;
  1480.     if (conxo(2, "\r\n") < 0)
  1481.         return -1;
  1482.     return 0;
  1483. }
  1484.  
  1485. /*
  1486.  * conchk -- returns nonzero if characters available from console
  1487.  */
  1488. int
  1489. conchk(void)
  1490. {
  1491.     fflush(stdout);
  1492.     Chk_Abort();
  1493.     return(WaitForChar(DOSFH(0), 0L) != 0);
  1494. }
  1495.  
  1496. /*
  1497.  * coninc -- get input character from console
  1498.  */
  1499. int
  1500. coninc(int timeout)
  1501. {
  1502.     UBYTE ch;
  1503.  
  1504.     fflush(stdout);
  1505.     Chk_Abort();
  1506.     if (timeout > 0 && !WaitForChar(DOSFH(0), timeout * 1000000L))
  1507.         return(-1);
  1508.     if (read(FILENO(0), &ch, 1) < 1) return(-1);
  1509.     Chk_Abort();
  1510.     return((int)ch);
  1511. }
  1512.  
  1513. /*
  1514.  * T T S C A R R -- Copy desired character mode to global ttcarr for future
  1515.  * and later use.
  1516.  */
  1517. ttscarr(carrier) int carrier; {
  1518.     ttcarr = carrier;
  1519.     debug(F101, "ttscarr","",ttcarr);
  1520.     return(ttcarr);
  1521. }
  1522.  
  1523. static int
  1524. sendbreak(long time) {
  1525.     if (!serialopen) return(-1);
  1526.     /* flush queued output */
  1527.     TerminateWrite(1);
  1528.     nttoq = 0;
  1529.     pendwrite = TRUE;
  1530.     WriteIOB->IOSer.io_Command = SDCMD_SETPARAMS;
  1531.     WriteIOB->io_BrkTime = time;
  1532.     (void) DoIO((IOR *)WriteIOB);
  1533.     pendwrite = TRUE;
  1534.     WriteIOB->IOSer.io_Command = SDCMD_BREAK;
  1535.     WriteIOB->io_SerFlags &= ~SERF_QUEUEDBRK;
  1536.     SendIO((IOR *)WriteIOB);
  1537.     return(0);
  1538. }
  1539.  
  1540. /*
  1541.  * ttsndb -- send a BREAK
  1542.  *    flushes queued and active output
  1543.  */
  1544. int
  1545. ttsndb(void)
  1546. {
  1547.     return(sendbreak(275000L));
  1548. }
  1549.  
  1550. /*
  1551.  * ttsndlb -- send a long BREAK (1.5 sec)
  1552.  */
  1553. int
  1554. ttsndlb(void) {
  1555.     return(sendbreak(1500000L));
  1556. }
  1557.  
  1558. /*  T T G M D M  --  Get modem signals  */
  1559. /*
  1560.  Looks for the modem signals as defined by the BM_??? constants in
  1561.  ckcdeb.h, and returns those that are on as a bit mask.  Returns:
  1562.  -3 Not implemented
  1563.  -2 if the line does not have modem control
  1564.  -1 on error.
  1565.  >= 0 on success, with a bit mask containing the modem signals that are on.
  1566. */
  1567.  
  1568. int
  1569. ttgmdm(void) {
  1570.     struct IOExtSer *myread = ReadIOB;
  1571.     int z;
  1572.     UWORD status;
  1573.  
  1574.     if (!serialopen)
  1575.         return -1;
  1576.     Chk_Abort();
  1577.     if (pendread && !CheckIO((IOR *)myread)) return(0);
  1578.     if (TerminateRead() != 0) return(-1);
  1579.     myread->IOSer.io_Command = SDCMD_QUERY;
  1580.     if (DoIO((IOR *) myread) != 0)
  1581.         return -1;
  1582.     status = myread->io_Status;
  1583.     z = 0;
  1584.     if (status & (1<<2)) z |= BM_RNG;    /* active high */
  1585.     status = ~status;            /* rest are active low */
  1586.     if (status & (1<<3)) z |= BM_DSR;
  1587.     if (status & (1<<4)) z |= BM_CTS;
  1588.     if (status & (1<<5)) z |= BM_DCD;
  1589.     if (status & (1<<6)) z |= BM_RTS;
  1590.     if (status & (1<<7)) z |= BM_DTR;
  1591.     return(z);
  1592. }
  1593.  
  1594.  
  1595. /*
  1596.  * ttocq -- write char to serial device, queueing if necessary
  1597.  *    returns -2 on overrun, -1 on serial error
  1598.  *    use only in connect mode
  1599.  */
  1600. int
  1601. ttocq(char c)
  1602. {
  1603.     int i;
  1604.  
  1605.     if (!serialopen) return(-1);
  1606.     if (pendwrite && CheckIO((IOR *)WriteIOB))
  1607.     {
  1608.         pendwrite = FALSE;
  1609.         if (WaitIO((IOR *)WriteIOB) != 0) return(-1);
  1610.     }
  1611.     if (pendwrite)
  1612.     {
  1613.         if (nttoq >= NTTOQ) return(-2);        /* overrun */
  1614.         ttoq[(pttoq + nttoq++) % NTTOQ] = c;
  1615.     }
  1616.     else if (nttoq == 0)
  1617.         return(ttoc(c));
  1618.     else
  1619.     {
  1620.         i = ttoc(ttoq[pttoq]);
  1621.         ttoq[(pttoq + nttoq) % NTTOQ] = c;
  1622.         pttoq = (pttoq + 1) % NTTOQ;
  1623.         if (i < 0) return(-1);
  1624.     }
  1625.     return(1);
  1626. }
  1627.  
  1628. /*
  1629.  * ttonq -- returns number of characters in serial output queue
  1630.  */
  1631. int
  1632. ttonq(void)
  1633. {
  1634.         return(nttoq);
  1635. }
  1636.  
  1637. /*
  1638.  * ttgwsiz -- get window size
  1639.  */
  1640. extern int tt_rows, tt_cols;
  1641. int
  1642. ttgwsiz(void) {
  1643.     char t[20];
  1644.     int n;
  1645.  
  1646.     conol("\x9b\x30\x20\x71");
  1647.     for (n = 0; n < sizeof(t); n++) {
  1648.         if ((t[n] = coninc(1)) == -1) {
  1649.             t[n] = '\0';
  1650.             break;
  1651.         }
  1652.         if (t[n] == 'r') {
  1653.             t[++n] = '\0';
  1654.             break;
  1655.         }
  1656.     }
  1657.     if (sscanf(t, "\x9b" "1;1;%d;%d r", &tt_rows, &tt_cols) != 2) {
  1658.         tt_rows = tt_cols = 0;
  1659.         return -1;
  1660.     }
  1661.     return 0;
  1662. }
  1663.  
  1664. /*
  1665.  * conttb -- prepare for contti() usage
  1666.  */
  1667. void
  1668. conttb(void)
  1669. {
  1670.     /* flush queued input and output */
  1671.     queuedcon = -1;
  1672.     pttoq = nttoq = 0;
  1673. }
  1674.  
  1675. /*
  1676.  * contte -- end contti() usage
  1677.  *    this can be called after a tthang, it which case ttres will already
  1678.  *    have done this cleanup
  1679.  */
  1680. void
  1681. contte(void)
  1682. {
  1683.     /* clear any pending ^C, ^D interrupts */
  1684.     Chk_Abort();
  1685.  
  1686.     /* terminate any pending I/O */
  1687.     if (serialopen) SerialReset();
  1688. }
  1689.  
  1690. /*
  1691.  * contti -- wait for console or tty input
  1692.  *    returns next console input or -1 when serial input available
  1693.  */
  1694. int
  1695. contti(void)
  1696. {
  1697.     int i;
  1698.     LONG waitsigs;
  1699.     struct DosPacket *pkt = conpkt;
  1700.     struct IOExtSer *myread = ReadIOB;
  1701.     static UBYTE conchar;
  1702.     BPTR dosfh = DOSFH(0);
  1703.     struct FileHandle *fh = (struct FileHandle *)BADDR(dosfh);
  1704.  
  1705.     if (queuedcon >= 0)
  1706.     {
  1707.         conchar = queuedcon;
  1708.         queuedcon = -1;
  1709.         return((int)conchar);
  1710.     }
  1711.  
  1712.     if (!pendconsole)
  1713.     {    /* start a console read */
  1714.         pkt->dp_Port = conport;
  1715.         pkt->dp_Type = ACTION_READ;
  1716.         pkt->dp_Arg1 = (LONG)dosfh;
  1717.         pkt->dp_Arg2 = (LONG)&conchar;
  1718.         pkt->dp_Arg3 = 1;
  1719.         PutMsg(fh->fh_Process, pkt->dp_Link);
  1720.         pendconsole = TRUE;
  1721.     }
  1722.  
  1723.     if (queuedser < 0 && !pendread)
  1724.     {    /* start a serial read */
  1725.         myread->IOSer.io_Command = CMD_READ;
  1726.         myread->IOSer.io_Data    = (APTR)&serbufc;
  1727.         myread->IOSer.io_Length  = 1;
  1728.         SendIO((IOR *)myread);
  1729.         pendread = TRUE;
  1730.     }
  1731.  
  1732.     waitsigs = (1L << serport->mp_SigBit) | (1L << conport->mp_SigBit);
  1733.     for (;;)
  1734.     {
  1735.         if (pendwrite && CheckIO((IOR *)WriteIOB))
  1736.         {
  1737.             WaitIO((IOR *)WriteIOB);
  1738.             pendwrite = FALSE;
  1739.             if (nttoq > 0)
  1740.             {
  1741.                 i = ttoc(ttoq[pttoq]);
  1742.                 pttoq = (pttoq + 1) % NTTOQ;
  1743.                 --nttoq;
  1744.                 if (i < 0) return(-1);
  1745.             }
  1746.         }
  1747.  
  1748.         /* give the console first chance */
  1749.         if (GetMsg(conport))
  1750.         {
  1751.             pendconsole = FALSE;
  1752.             if (pkt->dp_Res1 != 1) return(-1);
  1753.             /* translate CSI to ESC [ */
  1754.             if (conchar == 0x9B)
  1755.             {
  1756.                     conchar = 0x1B;
  1757.                 queuedcon = '[';
  1758.             }
  1759.             return((int)conchar);
  1760.         }
  1761.  
  1762.         if (queuedser >= 0) return(-2);
  1763.  
  1764.         if (CheckIO((IOR *)myread))
  1765.             return((TerminateRead() == 0) ? -2 : -1);
  1766.  
  1767.         Wait(waitsigs);
  1768.     }
  1769. }
  1770.  
  1771. /* T G E T E N T -- Dummy routine to simulate curses/termcap. */
  1772.  
  1773. #ifdef MYCURSES
  1774. int
  1775. tgetent(char *s1, char *s2) {
  1776.     return(1);
  1777. }
  1778. #endif /* MYCURSES */
  1779.  
  1780. /* P S U S P E N D -- Put current process in background. */
  1781.  
  1782. /*
  1783.  * Even though this isn't supported on the Amiga, I return success anyway.
  1784.  * After all, the user can pop the window to the back and do something
  1785.  * else any time he wants.
  1786.  */
  1787.  
  1788. int
  1789. psuspend(int foo) {
  1790.     return 0;
  1791. }
  1792.  
  1793. /* P R I V _ functions -- all dummy on the Amiga. */
  1794.  
  1795. int
  1796. priv_ini(void) {
  1797.     return 0;
  1798. }
  1799.  
  1800.  
  1801. int
  1802. priv_on(void) {
  1803.     return 0;
  1804. }
  1805.  
  1806. int
  1807. priv_off(void) {
  1808.     return 0;
  1809. }
  1810.  
  1811. int
  1812. priv_can(void) {
  1813.     return 0;
  1814. }
  1815.