home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / ckc197.zip / ckitio.c < prev    next >
C/C++ Source or Header  |  2000-01-02  |  48KB  |  1,808 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. {
  328.     struct IOExtSer *iob;
  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.     return(0);
  400. }
  401.  
  402. unsigned aalarm(unsigned);    /* forward declaration */
  403.  
  404. /*
  405.  * syscleanup -- Amiga specific cleanup
  406.  */
  407. syscleanup(void)
  408. {
  409.     /* close everything */
  410.     aalarm(0);
  411.     if (serialopen) CloseDevice((IOR *)ReadIOB);
  412.     if (TimerIOB) DeleteTimer(TimerIOB);
  413.     if (WriteIOB) DeleteExtIO((IOR *)WriteIOB);
  414.     if (ReadIOB) DeleteExtIO((IOR *)ReadIOB);
  415.     if (serport) DeletePort(serport);
  416.     if (conpkt) DeletePacket(conpkt);
  417.     if (conport) DeletePort(conport);
  418.     reqres();
  419.     if (IntuitionBase)
  420.     {
  421.         CloseLibrary((struct Library *)IntuitionBase);
  422.         IntuitionBase = NULL;
  423.     }
  424.  
  425.     /* reset standard I/O */
  426.     if (rawconfh > 0)
  427.     {
  428.         /* restore Lattice AmigaDOS file handles */
  429.         DOSFH(0) = Input();
  430.         DOSFH(1) = Output();
  431.         DOSFH(2) = saverr;
  432.         Close(rawconfh);
  433.         rawconfh = 0;
  434.     }
  435.     serialopen = 0;
  436.     TimerIOB = WriteIOB = ReadIOB = serport = conpkt = conport = NULL;
  437.     return 1;
  438. }
  439.  
  440. /*
  441.  * reqoff -- turn requestors off
  442.  *    When AmigaDOS encounters an error that user intervention can fix
  443.  *    (like inserting the correct disk), it normally puts up a requestor.
  444.  *    The following code disables requestors, causing an error to be
  445.  *    returned instead.
  446.  */
  447. void
  448. reqoff(void)
  449. {
  450.     pushwindow = CurProc->pr_WindowPtr;
  451.     CurProc->pr_WindowPtr = (APTR)-1;
  452. }
  453. /*
  454.  * reqpop -- restore requesters to action at last reqoff
  455.  */
  456. void
  457. reqpop(void)
  458. {
  459.     CurProc->pr_WindowPtr = pushwindow;
  460. }
  461.  
  462. /*
  463.  * reqres -- restore requestors to startup action
  464.  */
  465. void
  466. reqres(void)
  467. {
  468.     CurProc->pr_WindowPtr = savewindow;
  469. }
  470.  
  471. /*
  472.  * KillIO -- terminate an I/O request
  473.  */
  474. static int
  475. KillIO(struct IORequest *iob)
  476. {
  477.     AbortIO(iob);
  478.     return((int)WaitIO(iob));
  479. }
  480.  
  481. /*
  482.  * ttopen -- open the serial device
  483.  *    If already open, returns 0 immediately.
  484.  *    Otherwise, the ttname is compare to SERIALNAME and used to
  485.  *    open the serial device, and, if the value of *lcl is < 0, it is
  486.  *    reset to 1 indicating local mode.  Returns -1 on error.
  487.  *    timo is the length of time to wait before flunking open;  we don't
  488.  *    need this feature on the Amiga.
  489.  */
  490. int
  491. ttopen(char * ttname, int *lcl, int modem, int timo)
  492. {
  493.     struct IOExtSer *iob = ReadIOB;
  494.     char *p;
  495.     ULONG unit;
  496.     static  char cttname[50];    /* Current open ttname */
  497.  
  498.     debug(F111,"ttopen entry modem",ttname,modem);
  499.     debug(F101," lcl","",*lcl);
  500.     if (modem < 0) return -1;    /* We don't do networks yet. */
  501.     if (serialopen)            /* Already have serial device open */
  502.             if (strcmp(ttname, cttname) == 0)
  503.                 return(0);        /* Same device - ignore  */
  504.             else ttclos(0);        /* Different device - close */
  505.  
  506.     /* verify the serial name */
  507. #if 0
  508.     if (strcmp(ttname, SERIALNAME) != 0) return(-1);
  509. #endif
  510.  
  511.     /* set open modes.  We no longer open in shared mode. */
  512.     iob->io_SerFlags = (modem > 0 ? SERF_7WIRE : 0);
  513.  
  514.     /* parse device name as device/unit */
  515.     if ((p = strchr(ttname, '/')) == NULL)
  516.         unit = 0;
  517.     else {
  518.         if (*(p + strlen(p) - 1) == 's')    /* Open in shared mode */
  519.             {
  520.                 iob->io_SerFlags |= SERF_SHARED;
  521.                 *(p + strlen(p) - 1) = '\0';
  522.             }
  523.         unit = (ULONG) atoi(p + 1);
  524.         *p = '\0';
  525.     }
  526.     /* open the serial device */
  527.     if (OpenDevice((UBYTE *) ttname, unit, (IOR *)iob, 0L) != 0)
  528.         return(-1);
  529.     serialopen = TRUE;
  530.     tvtflg = 0;
  531.     pendread = pendwrite = pendconsole = FALSE;
  532.     queuedser = -1;
  533.  
  534.     /* fill in the fields of the other IO blocks */
  535.     *WriteIOB = *iob;
  536.  
  537.     /* set local mode */
  538.     if (*lcl == -1)    *lcl = 1; /* always local */
  539.     if (p) *p = '/';        /* restore slash */
  540.         if (iob->io_SerFlags & SERF_SHARED)
  541.             *(p + strlen(p)) = 's';     /* restore suffix if present */
  542.     strcpy(cttname, ttname);
  543.     debug(F110, "ttopen got device", ttname, 0);
  544.     return(0);
  545. }
  546.  
  547. /*
  548.  * StartTimer -- start a timeout
  549.  */
  550. static VOID
  551. StartTimer(LONG secs, LONG micro)
  552. {
  553.     TimerIOB->tr_node.io_Command = TR_ADDREQUEST;
  554.     TimerIOB->tr_time.tv_secs  = secs;
  555.     TimerIOB->tr_time.tv_micro = micro;
  556.     SendIO((IOR *)TimerIOB);
  557. }
  558.  
  559. /*
  560.  * SerialWait -- wait for serial I/O to terminate
  561.  *    return I/O error
  562.  */
  563. static int
  564. SerialWait(struct IOExtSer *iob, int timeout)
  565. {
  566.     LONG sigs;
  567.     struct timerequest *timer = TimerIOB;
  568.     LONG waitsigs;
  569.  
  570.     /* set up timeout if necessary */
  571.     if (timeout > 0) {
  572.         StartTimer((LONG)timeout, 0L);
  573.         waitsigs =
  574.            (1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit);
  575.     } else
  576.         waitsigs = 0;
  577.  
  578.     /* wait for completion, timeout, or interrupt */
  579.     sigs = 0;
  580.     waitsigs |= (1L << serport->mp_SigBit) |
  581.                    intsigs;
  582.     for (;;)
  583.     {
  584.         if (sigs & intsigs)
  585.         {    /* interrupted */
  586.             if (timeout > 0) KillIO((IOR *)timer);
  587.             KillIO((IOR *)iob);
  588.             testint(sigs);
  589.             return(-1);
  590.         }
  591.         if (CheckIO((IOR *)iob))
  592.         {
  593.             if (timeout > 0) KillIO((IOR *)timer);
  594.             return((int)WaitIO((IOR *)iob));
  595.         }
  596.         if (timeout > 0 && CheckIO((IOR *)timer))
  597.         {
  598.             KillIO((IOR *)iob);
  599.             WaitIO((IOR *)timer);
  600.             /* restart if XOFF'ed */
  601.             iob->IOSer.io_Command = CMD_START;
  602.             DoIO((IOR *)iob);
  603.             return(-1);
  604.         }
  605.         sigs = Wait(waitsigs);
  606.     }
  607. }
  608.  
  609. /*
  610.  * TerminateRead -- wait for queued read to finish
  611.  */
  612. static int
  613. TerminateRead(void)
  614. {
  615.     if (!pendread) return(0);
  616.     if (WaitIO((IOR *)ReadIOB) == 0) queuedser = serbufc;
  617.     pendread = FALSE;
  618.     return((int)ReadIOB->IOSer.io_Error);
  619. }
  620.  
  621. /*
  622.  * TerminateWrite -- ensure WriteIOB is ready for reuse
  623.  */
  624. static int
  625. TerminateWrite(int timeout)
  626. {
  627.     Chk_Abort();
  628.     if (!pendwrite) return(0);
  629.     pendwrite = FALSE;
  630.     if (timeout) {
  631.         timeout = WriteIOB->IOSer.io_Length * 80 / WriteIOB->io_Baud;
  632.     }
  633.     return(SerialWait(WriteIOB, timeout));
  634. }
  635.  
  636. /*
  637.  * SerialReset -- terminate pending serial and console I/O
  638.  */
  639. static void
  640. SerialReset(void)
  641. {
  642.     if (pendread)
  643.     {
  644.         AbortIO((IOR *)ReadIOB); /* should work even if read finished */
  645.         TerminateRead();
  646.     }
  647.  
  648.     if (pendconsole)
  649.     {    /* this does not happen normally */
  650.         WaitPort(conport);
  651.         GetMsg(conport);
  652.         pendconsole = FALSE;
  653.     }
  654.  
  655.     if (pendwrite)
  656.         TerminateWrite(1);
  657. }
  658.  
  659. /*
  660.  * ttres -- reset serial device
  661.  */
  662. ttres()
  663. {
  664.     if (!serialopen) return(-1);
  665.  
  666.     /* reset everything */
  667.     SerialReset();
  668.     ReadIOB->IOSer.io_Command = CMD_RESET;
  669.     tvtflg = 0;
  670.     return(DoIO((IOR *)ReadIOB) ? -1 : 0);
  671. }
  672.  
  673. /*
  674.  * ttclos -- close the serial device
  675.  */
  676. int
  677. ttclos(int unit)
  678. {
  679.     debug(F101, "ttopen ", "", unit);
  680.     if (!serialopen) return(0);
  681.     if (ttres() < 0) return(-1);
  682.     CloseDevice((IOR *)ReadIOB);
  683.     serialopen = FALSE;
  684.     tvtflg = 0;
  685.     return(0);
  686. }
  687.  
  688. /*
  689.  * tthang -- hang up phone line
  690.  *    Drops DTR by closing serial.device
  691.  */
  692. int
  693. tthang(void)
  694. {
  695.         return((serialopen) ? ttclos(0) : -1);
  696. }
  697.  
  698. /*
  699.  * ttpkt -- set serial device up for packet transmission
  700.  *    sets serial parameters
  701.  */
  702. int
  703. ttpkt(long speed, int flow, int parity)
  704. {
  705.     extern UBYTE eol;
  706.     struct IOExtSer *iob = ReadIOB;
  707.  
  708.     debug(F101, "ttpkt speed ", "", speed);
  709.     debug(F101, "ttpkt flow ", "", flow);
  710.     debug(F101, "ttpkt parity ", "", parity);
  711.     if (!serialopen || pendread) return(-1);
  712.  
  713.     /* terminate any pending writes */
  714.     TerminateWrite(1);
  715.  
  716.     /* fill in parameters */
  717.     iob->io_CtlChar = 0x11130000;
  718.     if (speed >= 0 && ttsspd((int) (speed / 10)) >= 0) iob->io_Baud = speed;
  719.     iob->io_RBufLen = speed;    /* 10 seconds worth of data */
  720.     /*
  721.      * Notice the dopar(eol) here to set the EOL character with the
  722.      * appropriate parity.  See also ttinl().
  723.      */
  724.     memset(&iob->io_TermArray, dopar(eol), sizeof(struct IOTArray));
  725.     iob->io_ReadLen = iob->io_WriteLen = 8;
  726.     iob->io_StopBits = 1;
  727.     if (flow == FLO_XONX)
  728.         iob->io_SerFlags &= ~(SERF_XDISABLED | SERF_7WIRE);
  729.     else if (flow == FLO_RTSC)
  730.         iob->io_SerFlags |= (SERF_XDISABLED | SERF_7WIRE);
  731.     else {
  732.         iob->io_SerFlags |= SERF_XDISABLED;
  733.         iob->io_SerFlags &= ~SERF_7WIRE;
  734.     }
  735.     /* if no XON/XOFF flow and high baud rate, RAD_BOOGIE is appropriate */
  736.     if (flow != FLO_XONX && iob->io_Baud >= 19200)
  737.         iob->io_SerFlags |= SERF_RAD_BOOGIE;
  738.     else
  739.         iob->io_SerFlags &= ~SERF_RAD_BOOGIE;
  740.  
  741.     /*
  742.      * Parity setting.  For packet send/receive, we turn off the
  743.      * Amiga's internal parity generation and checking, as this code
  744.      * does it itself (which makes it bigger and slower...).  We
  745.      * save the current parity for ttinl().
  746.      */
  747.  
  748.     ttprty = parity;
  749.     iob->io_SerFlags &= ~(SERF_EOFMODE|SERF_PARTY_ON|SERF_PARTY_ODD);
  750.     iob->io_ExtFlags = 0;        /* MUST BE ZERO unless Mark or Space. */
  751.  
  752.     /* set the parameters */
  753.     iob->IOSer.io_Command = SDCMD_SETPARAMS;
  754.     if (DoIO((IOR *)iob) != 0) return(-1);
  755.     tvtflg = 0;
  756.     return(ttflui());
  757. }
  758.  
  759. /*
  760.  * ttvt -- set up serial device for connect mode.  This is almost the same
  761.  * as ttpkt() on the Amiga, except we save the settings and a flag and return
  762.  * without doing anything if we've already been called with the same
  763.  * values.
  764.  */
  765. int
  766. ttvt(long speed, int flow) {
  767.     static long ospeed = -1;
  768.     static int oflow = -9;
  769.  
  770.     if (tvtflg != 0 && ospeed == speed && oflow == flow)
  771.         return 0;
  772.     if (ttpkt(speed, flow, 0) < 0)
  773.         return -1;
  774.     ospeed = speed;            /* Save speed */
  775.     oflow = flow;            /* and flow control set */
  776.     tvtflg = 1;            /* and flag we've been called */
  777.     return 0;
  778. }
  779.  
  780. /*  T T S S P D  --  Set the transmission of tty to ten times the argument */
  781.  
  782. ttsspd(speed) int speed; {
  783.     int s;
  784.     struct IOExtSer *iob = ReadIOB;
  785.  
  786.     debug (F101,"ttsspd: speed(cps):","",speed);
  787.     if (!serialopen) return(-1);
  788.  
  789.     switch (speed) {
  790.         case 5:         s = 50;        break;
  791.         case 7:         s = 75;        break;
  792.         case 11:        s = 110;       break;
  793.         case 13:        s = 134;       break;
  794.         case 15:        s = 150;       break;
  795.         case 30:        s = 300;       break;
  796.         case 60:        s = 600;       break;
  797.         case 120:       s = 1200;      break;
  798.         case 180:       s = 1800;      break;
  799.         case 200:       s = 2000;      break;
  800.         case 240:       s = 2400;      break;
  801.         case 360:       s = 3600;      break;
  802.         case 480:       s = 4800;      break;
  803.         case 720:       s = 7200;      break;
  804.         case 960:       s = 9600;      break;
  805.         case 1440:    s = 14400;     break;
  806.         case 1920:      s = 19200;     break;
  807.         case 3840:      s = 38400;     break;
  808.         case 5760:    s = 57600;     break;
  809.         case 888:       return(-1); /* no 75/1200 split speed */
  810.         default:        return -1;
  811.     }
  812.     /* First get a complete copy of current settings. */
  813.     iob->IOSer.io_Command = SDCMD_QUERY;
  814.     if (DoIO((IOR *)iob) != 0) return(-1);
  815.     iob->io_Baud = s;
  816.     iob->io_RBufLen = s;    /* 10 seconds worth of data */
  817.     /* set the parameters */
  818.     iob->IOSer.io_Command = SDCMD_SETPARAMS;
  819.     if (DoIO((IOR *)iob) != 0) return(-1);
  820.  
  821.     return s;
  822.  
  823. }
  824.  
  825. /* T T G S P D  -  Get speed of currently selected tty line  */
  826.  
  827. /*
  828.   Read speed from serial.device, or, if not open, return the value in
  829.   the current ReadIOB.
  830. */
  831. long
  832. ttgspd(void) {                /* Get current tty speed */
  833.     struct IOExtSer *myread = ReadIOB;
  834.  
  835.     if (!serialopen)
  836.         if (myread != NULL) return((long)myread->io_Baud);
  837.         else return -1;
  838.     Chk_Abort();
  839.     if (pendread && !CheckIO((IOR *)myread)) return(0);
  840.     if (TerminateRead() != 0) return(-1);
  841.     myread->IOSer.io_Command = SDCMD_QUERY;
  842.     return((DoIO((IOR *)myread) == 0)
  843.             ? (long)myread->io_Baud
  844.             : -1);
  845. }
  846.  
  847. /*
  848.  * ttflui -- flush serial device input buffer
  849.  */
  850. int
  851. ttflui(void)
  852. {
  853.     if (!serialopen || pendread) return(-1);
  854.     queuedser = -1;
  855.     ReadIOB->IOSer.io_Command = CMD_CLEAR;
  856.     return(DoIO((IOR *)ReadIOB) ? -1 : 0);
  857. }
  858.  
  859. /*
  860.  * ttfluo -- flush serial output buffer
  861.  */
  862. int
  863. ttfluo(void)
  864. {
  865.     if (!serialopen || pendwrite) return -1;
  866.     WriteIOB->IOSer.io_Command = CMD_CLEAR;
  867.     return(DoIO((IOR *)WriteIOB) ? -1 : 0);
  868. }
  869.  
  870.  
  871. /*
  872.  * test for and catch interrupt
  873.  */
  874. static void
  875. testint(LONG sigs)
  876. {
  877.     /* test for and reset caught interrupt signals */
  878.     if ((sigs | SetSignal(0L, (LONG)BREAKSIGS)) & intsigs) {
  879.         raise(SIGINT);
  880.     }
  881. }
  882.  
  883. /*
  884.  * conint -- set console interrupt handler and suspend handler.
  885.  */
  886. void
  887. conint(SIGTYP (*newhdlr)(int), SIGTYP (*stophdlr)(int))
  888. {
  889.     Chk_Abort();            /* handle any pending interrupts */
  890.     signal(SIGINT, newhdlr);    /* set the new handler */
  891.     intsigs = BREAKSIGS;        /* note signal caught */
  892. }
  893.  
  894. /*
  895.  * connoi -- disable interrupt trapping
  896.  */
  897. void
  898. connoi(void)
  899. {
  900.     signal(SIGINT, SIG_IGN);    /* disable interrupts */
  901.     intsigs = 0;            /* note signal ignored */
  902.     Chk_Abort();            /* ignore pending interrupts */
  903. }
  904.  
  905. /*
  906.  * ttchk -- return number of chars immediately available from serial device
  907.  */
  908. int
  909. ttchk(void)
  910. {
  911.     struct IOExtSer *myread = ReadIOB;
  912.  
  913.     if (!serialopen) return(-1);
  914.     Chk_Abort();
  915.     if (pendread && !CheckIO((IOR *)myread)) return(0);
  916.     if (TerminateRead() != 0) return(-1);
  917.     myread->IOSer.io_Command = SDCMD_QUERY;
  918.     return((DoIO((IOR *)myread) == 0)
  919.             ? ((queuedser >= 0 ? 1 : 0) + (int)myread->IOSer.io_Actual)
  920.             : -1);
  921. }
  922.  
  923. /*
  924.  * ttxin -- get n characters from serial device.  This routine should
  925.  * only be called when we know that there are at least n characters
  926.  * ready to be read.
  927.  */
  928. int
  929. ttxin(int n, CHAR *buf)
  930. {
  931.         return(ttinl(buf, n, 0, 0));
  932. }
  933.  
  934. #ifdef PARSENSE
  935.  
  936. extern CHAR partab[];
  937.  
  938. /*  P A R C H K  --  Check if Kermit packet has parity  */
  939.  
  940. /*
  941.   Call with s = pointer to packet, start = packet start character, n = length.
  942.   Returns 0 if packet has no parity, -1 on error, or if packet has parity:
  943.     'e' for even, 'o' for odd, 'm' for mark.  Space parity cannot be sensed.
  944. */
  945. parchk(s,start,n) CHAR *s, start; int n; {
  946.     CHAR s0, s1, s2, s3, sn;
  947.  
  948.     debug(F101,"parchk n","",n);
  949.     debug(F101,"parchk start","",start);
  950.     debug(F110,"parchk s",s,0);
  951.  
  952.     s0 = s[0] & 0x7f;            /* Mark field (usually Ctrl-A) */
  953.  
  954.     if (s0 != start || n < 5) return(-1); /* Not a valid packet */
  955.  
  956. /* Look at packet control fields, which never have 8th bit set */
  957. /* First check for no parity, most common case. */
  958.  
  959.     if (((s[0] | s[1] | s[2] | s[3] | s[n-2]) & 0x80) == 0)
  960.       return(0);            /* No parity */
  961.  
  962. /* Check for mark parity */
  963.  
  964.     if (((s[0] & s[1] & s[2] & s[3] & s[n-2]) & 0x80) == 0x80)
  965.       return('m');            /* Mark parity */
  966.  
  967. /* Packet has some kind of parity */
  968. /* Make 7-bit copies of control fields */
  969.  
  970.     s1 = s[1] & 0x7f;            /* LEN */
  971.     s2 = s[2] & 0x7f;            /* SEQ */
  972.     s3 = s[3] & 0x7f;            /* TYPE */
  973.     sn = s[n-2] & 0x7f;            /* CHECK */
  974.  
  975. /* Check for even parity */
  976.  
  977.     if ((s[0] == partab[s0]) &&
  978.         (s[1] == partab[s1]) &&
  979.         (s[2] == partab[s2]) &&
  980.     (s[3] == partab[s3]) &&
  981.     (s[n-2] == partab[sn]))
  982.       return('e');
  983.  
  984. /* Check for odd parity */
  985.  
  986.     if ((s[0] != partab[s0]) &&
  987.         (s[1] != partab[s1]) &&
  988.         (s[2] != partab[s2]) &&
  989.     (s[3] != partab[s3]) &&
  990.     (s[n-2] != partab[sn]))
  991.       return('o');
  992.  
  993. /* Otherwise it's probably line noise.  Let checksum calculation catch it. */
  994.  
  995.     return(-1);
  996. }
  997. #endif /* PARSENSE */
  998.  
  999. /*
  1000.  * ttinc -- read character from serial line
  1001.  */
  1002. int
  1003. ttinc(int timeout)
  1004. {
  1005.     UBYTE ch;
  1006.  
  1007.     return((ttinl((CHAR *)&ch, 1, timeout, 0) > 0) ? (int)ch : -1);
  1008. }
  1009.  
  1010. /*
  1011.  * The following chunk of code is a primitive (very!) alarm() function
  1012.  * for the Amiga.  It is nowhere near general, and it will only work
  1013.  * with Kermit, most likely.  It has three parts:
  1014.  *
  1015.  * asignal() is call-compatible with signal().  If the signal is less
  1016.  * than or equal to _NUMSIG (in <signal.h>), then the vendor-supplied
  1017.  * signal() is called.  If it is equal to _NUMSIG+1, which I define as
  1018.  * SIGALRM, then it is a new alarm signal.  The pointer to the passed
  1019.  * function is saved and the old one is returned.
  1020.  *
  1021.  * aalarm() is the Unix-like call.  It is called with a time in seconds,
  1022.  * which is the time after which the routine passed in the
  1023.  * signal(SIGALRM, ...) call is to be called.  Here we just start a
  1024.  * timer and return.
  1025.  *
  1026.  * check_alarm() sees if the time specified by aalarm() is up yet, and
  1027.  * calls the saved function if it is.
  1028.  */
  1029.  
  1030. static void (*savalarm)(int) = SIG_DFL;
  1031. static struct timerequest *alarmIOB;
  1032. static unsigned savesecs;
  1033. static short alarmflag = 0;        /* flag that an alarm is pending */
  1034. #define SIGALRM (_NUMSIG+1)
  1035.  
  1036. void (*asignal(int sig, void (*func)(int)))(int) {
  1037.  
  1038.     void (*talarm)(int);
  1039.     debug(F101, "asignal sig", "", sig);
  1040.     debug(F101, "asignal func", "", func);
  1041.     if (sig <= _NUMSIG)
  1042.         return(signal(sig, func));
  1043.     else if (sig == SIGALRM) {
  1044.         talarm = savalarm;
  1045.         savalarm = func;
  1046.         return(talarm);
  1047.     }
  1048.     else {
  1049.         debug(F100, "asignal called with sig too large", "", 0);
  1050.         return(SIG_IGN);
  1051.     }
  1052. }
  1053.  
  1054. unsigned
  1055. aalarm(unsigned secs) {
  1056.     unsigned t;
  1057.  
  1058.     debug(F101, "aalarm", "", secs);
  1059.     t = savesecs;
  1060.     if (secs == 0) {
  1061.         if (alarmIOB) {
  1062.             KillIO((IOR *) alarmIOB);
  1063.             DeleteTimer(alarmIOB);
  1064.             alarmIOB = NULL;
  1065.         }
  1066.         savesecs = 0;
  1067.         alarmflag = 0;
  1068.         return(t);
  1069.     } else {
  1070.         alarmIOB = CreateTimer(UNIT_VBLANK);
  1071.         if (alarmIOB == NULL) {
  1072.             debug(F100, "CreateExtIO failed in alarm", "", 0);
  1073.             return(0);
  1074.         }
  1075.         alarmIOB->tr_time.tv_secs = savesecs = secs;
  1076.         alarmIOB->tr_time.tv_micro = 0;
  1077.         alarmIOB->tr_node.io_Command = TR_ADDREQUEST;
  1078.         SendIO((IOR *) alarmIOB);
  1079.         alarmflag = 1;
  1080.         return(t);
  1081.     }
  1082. }
  1083.  
  1084. static void
  1085. check_alarm(void) {
  1086.  
  1087.     if (alarmflag)
  1088.         if (CheckIO((IOR *) alarmIOB)) {
  1089.             WaitIO((IOR *) alarmIOB);
  1090.             alarmflag = 0;
  1091.             if (savalarm == SIG_IGN)
  1092.                 return;
  1093.             else if (savalarm == SIG_DFL)
  1094.                 Fail("uncaught alarm seen");
  1095.             else
  1096.                 (*savalarm)(SIGALRM);
  1097.         }
  1098. }
  1099.  
  1100. /*
  1101.  * ttol -- write n chars to serial device.  For small writes, we have
  1102.  * a small local buffer which allows them to run asynchronously.  For
  1103.  * large writes, we do them synchronously.  This seems to be the best
  1104.  * compromise between speed and code simplicity and size.
  1105.  *
  1106.  * Stephen Walton, 23 October 1989
  1107.  */
  1108. int
  1109. ttol(CHAR *buf, int n)
  1110. {
  1111.     struct IOExtSer *mywrite = WriteIOB;
  1112.     static char outbuf[1024];    /* safe place for output characters */
  1113.     int s;
  1114.     int oldn = n;
  1115.  
  1116.     if (!serialopen) return(-1);
  1117.     check_alarm();
  1118.     if ((s = n - sizeof(outbuf)) > 0) {
  1119.         if (TerminateWrite(1) != 0) return(-1);
  1120.         mywrite->IOSer.io_Command = CMD_WRITE;
  1121.         mywrite->IOSer.io_Data  = (APTR) buf;
  1122.         mywrite->IOSer.io_Length = s;
  1123.         SendIO((IOR *)mywrite);
  1124.         pendwrite = TRUE;
  1125.         buf += s;
  1126.         n   -= s;
  1127.         memcpy(outbuf, buf, n);
  1128.         if (TerminateWrite(1) != 0) return(-1);
  1129.     } else {
  1130.         if (TerminateWrite(1) != 0) return(-1);
  1131.         memcpy(outbuf, buf, n);
  1132.     }
  1133.     mywrite->IOSer.io_Command = CMD_WRITE;
  1134.     mywrite->IOSer.io_Data    = (APTR)outbuf;
  1135.     mywrite->IOSer.io_Length  = n;
  1136.     SendIO((IOR *)mywrite);
  1137.     pendwrite = TRUE;
  1138.  
  1139.     return oldn;
  1140. }
  1141.  
  1142. /*
  1143.  * ttoc -- output single character to serial device
  1144.  */
  1145. int
  1146. ttoc(char c)
  1147. {
  1148.         return(ttol((CHAR *) &c, 1));
  1149. }
  1150.  
  1151. /*
  1152.  * ttinl -- read from serial device, possibly with timeout and eol character
  1153.  *    reads up to n characters, returning the number of characters read
  1154.  *    if eol > 0, reading the eol character will terminate read
  1155.  *    if timeout > 0, terminates read if timeout elapses
  1156.  *    returns -1 on error, such as timeout or interrupt
  1157.  *
  1158.  *    Note that this is the single routine which does all character reading
  1159.  *    in Amiga C Kermit, and has some added "features" compared to, say,
  1160.  *    the Unix version.  If timeout is 0, this routine waits forever.
  1161.  *    If eol is zero, it is not used.
  1162.  *
  1163.  *    New for 5A(157) is the start parameter, which is the start-of-packet
  1164.  *    character.  Following the Unix example, we just read until eol,
  1165.  *    but return a bad packet if the first character we got doesn't agree
  1166.  *    with start.
  1167.  */
  1168. int
  1169. ttinl(CHAR *buf, int n, int timeout, CHAR eol)
  1170. {
  1171.         unsigned  mask;
  1172.     struct IOExtSer *myread = ReadIOB;
  1173.     int count;
  1174.     int nread, i;
  1175.  
  1176.     Chk_Abort();
  1177.     check_alarm();
  1178.      if (!serialopen || pendread || n <= 0) return(-1);
  1179.  
  1180.     mask = (ttprty ? 0177 : 0377);    /* parity stripping mask */
  1181.  
  1182.     /* handle pushback */
  1183.     if (queuedser >= 0)
  1184.     {
  1185.         *buf = queuedser & mask;    /* Strip queued character. */
  1186.         queuedser = -1;
  1187.         if (*buf == eol || n == 1) return(1);
  1188.         ++buf;
  1189.         --n;
  1190.         count = 1;
  1191.     }
  1192.     else
  1193.         count = 0;
  1194.  
  1195.     /* set up line terminator */
  1196.     if (eol > 0)
  1197.     {
  1198.         /*
  1199.          * For reasons which are obscure to me, this batch of
  1200.          * code generally fails.  Normally, this doesn't matter,
  1201.          * because io_TermArray is set in ttpkt() above, and so
  1202.          * this code is only executed if eol changes as a result
  1203.          * of the initial packet negotiation.  I found the bug
  1204.          * by inadvertently not using dopar(eol) in the setting
  1205.          * of io_TermArray in ttpkt(), which did cause this code
  1206.          * to be called if parity was MARK or EVEN (since in that
  1207.          * case dopar(eol) != eol).
  1208.          */
  1209.  
  1210.         if (dopar(eol) != *(UBYTE *)&myread->io_TermArray)
  1211.         {
  1212.             memset(&myread->io_TermArray, dopar(eol),
  1213.                    sizeof(struct IOTArray));
  1214.             myread->IOSer.io_Command = SDCMD_SETPARAMS;
  1215.             if (DoIO((IOR *)myread) != 0) {
  1216.                 debug(F111, "SETPARAMS fails in ttinl()",
  1217.                       "io_Error", (int) myread->IOSer.io_Error);
  1218.                 myread->io_TermArray.TermArray0 =
  1219.                     myread->io_TermArray.TermArray1 = 0xffffffffu;
  1220.                 return -1;
  1221.             }
  1222.         }
  1223.         myread->io_SerFlags |= SERF_EOFMODE;
  1224.     }
  1225.     else
  1226.         myread->io_SerFlags &= ~SERF_EOFMODE;
  1227.  
  1228.     /* set up the read */
  1229.     myread->IOSer.io_Command = CMD_READ;
  1230.     myread->IOSer.io_Data    = (APTR)buf;
  1231.     myread->IOSer.io_Length  = n;
  1232.  
  1233.     /* perform read quickly if possible */
  1234.     myread->IOSer.io_Flags = IOF_QUICK;
  1235.     BeginIO((IOR *)myread);
  1236.     if (myread->IOSer.io_Flags & IOF_QUICK)
  1237.         myread->IOSer.io_Flags = 0;
  1238.     else
  1239.         /* wait for read to complete if no QUICK. */
  1240.         if (SerialWait(myread, timeout) != 0)
  1241.             return -1;
  1242.  
  1243.     if (myread->IOSer.io_Error != 0)
  1244.         return -1;
  1245. #if COMMENT
  1246.     if (start != 0 && (buf[0] & mask) != start) /* Bad packet */
  1247.         return -1;
  1248. #endif
  1249.     /* Strip parity bits if need be. */
  1250.     nread = (int) myread->IOSer.io_Actual;
  1251.     if (ttprty)
  1252.         for (i = 0; i < nread; i++)
  1253.             buf[i] &= mask;
  1254.     if (nread > 1)
  1255.         buf[nread] = '\0';        /* Null terminate */
  1256.     return(count + nread);
  1257. }
  1258.  
  1259. /*
  1260.  * Sleeper -- perform an interruptible timeout
  1261.  */
  1262. static int
  1263. Sleeper(LONG secs, LONG micro)
  1264. {
  1265.     LONG sigs;
  1266.     LONG waitsigs;
  1267.     struct timerequest *timer = TimerIOB;
  1268.  
  1269.     if (!TimerIOB) return(-1);
  1270.     if (secs == 0 && micro <= 2)
  1271.         return(0);
  1272.     StartTimer(secs, micro);
  1273.     sigs = 0;
  1274.     waitsigs = (1L << timer->tr_node.io_Message.mn_ReplyPort->mp_SigBit) | intsigs;
  1275.     for (;;)
  1276.     {
  1277.         if (CheckIO((IOR *)timer))
  1278.         {
  1279.             WaitIO((IOR *)timer);
  1280.             return(0);
  1281.         }
  1282.         if (sigs & intsigs)
  1283.         {
  1284.             KillIO((IOR *)timer);
  1285.             testint(sigs);
  1286.             return(-1);
  1287.         }
  1288.         sigs = Wait(waitsigs);
  1289.     }
  1290. }
  1291.  
  1292. /*
  1293.  * sleep -- wait n seconds
  1294.  */
  1295. int
  1296. sleep(int n)
  1297. {    return(Sleeper((LONG)n, 0L)); }
  1298.  
  1299. /*
  1300.  * msleep -- wait n milliseconds
  1301.  */
  1302. int
  1303. msleep(int m)
  1304. {    return(Sleeper((LONG)(m / 1000), (m % 1000) * 1000L)); }
  1305.  
  1306.  
  1307. /*
  1308.  * rtimer -- reset elapsed time
  1309.  */
  1310. void
  1311. rtimer(void)
  1312. {    DateStamp(&prevtime); }
  1313.  
  1314. /*
  1315.  * gtimer -- get currently elapsed time in seconds
  1316.  */
  1317. int
  1318. gtimer(void)
  1319. {
  1320.     int x;
  1321.     struct DateStamp curtime;
  1322.  
  1323.     DateStamp(&curtime);
  1324.     x = ((curtime.ds_Days   - prevtime.ds_Days  ) * 1440 +
  1325.          (curtime.ds_Minute - prevtime.ds_Minute) ) * 60 +
  1326.          (curtime.ds_Tick   - prevtime.ds_Tick  ) / 50;
  1327.     return((x < 0) ? 0 : x );
  1328. }
  1329.  
  1330. /*
  1331.  * ztime -- format current date and time into string
  1332.  */
  1333. void
  1334. ztime(char **s)
  1335. {
  1336.     time_t xclock;
  1337.  
  1338.     (void) time(&xclock);
  1339.     *s = asctime(localtime(&xclock));
  1340. }
  1341.  
  1342. /*
  1343.  * congm -- save console modes
  1344.  */
  1345. int
  1346. congm(void)
  1347. {
  1348.     if (!saverr) saverr = DOSFH(2);
  1349.     return(0);
  1350. }
  1351.  
  1352. /*
  1353.  * CreateWindow -- create window and jam it into standard I/O
  1354.  */
  1355. int
  1356. CreateWindow(int esc)
  1357. {
  1358.     char rawname[48];
  1359.     struct Screen s;
  1360.  
  1361.     if (rawconfh > 0) return(0);
  1362.     congm();
  1363.  
  1364.     if (GetScreenData(&s, sizeof(s), WBENCHSCREEN, NULL) == 0) {
  1365.         s.Width = 640;
  1366.         s.Height = 200;
  1367.     }
  1368.     sprintf(rawname, "RAW:0/1/%d/%d/Kermit%s", s.Width, s.Height - 1,
  1369.         v37? "/ALT0/1/100/30" : "");
  1370.     rawconfh = Open((UBYTE *) rawname, (LONG)MODE_NEWFILE);
  1371.       if (rawconfh == 0)
  1372.           return(-1);
  1373.     DOSFH(0) = DOSFH(1) = DOSFH(2) = rawconfh;
  1374.  
  1375.     /* if we create a window, don't abort on errors or echo */
  1376.     backgrd = FALSE;
  1377.     ckxech = 1;
  1378.     return(0);
  1379. }
  1380.  
  1381. /*
  1382.  * concb -- put console in single character wakeup mode
  1383.  */
  1384. int
  1385. concb(char esc)
  1386. {
  1387.     if (rawconfh) return(0);
  1388.     if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
  1389.         return(0);
  1390.     return(CreateWindow(esc));
  1391. }
  1392.  
  1393. /*
  1394.  * conbin -- put console in raw mode
  1395.  */
  1396. int
  1397. conbin(char esc)
  1398. {
  1399.     if (rawconfh) return(0);
  1400.     if (CurCLI && CurProc->pr_CIS != CurCLI->cli_StandardInput)
  1401.         return(isatty(0) ? 0 : -1);
  1402.     return(CreateWindow(esc));
  1403. }
  1404.  
  1405. /*
  1406.  * conres -- restore console
  1407.  *    we actually restore in syscleanup()
  1408.  */
  1409. conres()
  1410. {
  1411.         return(0);
  1412. }
  1413.  
  1414. /*
  1415.  * conoc -- output character to console
  1416.  */
  1417. int
  1418. conoc(char c)
  1419. {
  1420.     putchar(c);
  1421.     fflush(stdout);
  1422.     Chk_Abort();
  1423.     return c;
  1424. }
  1425.  
  1426. /*
  1427.  * conxo -- output x chars to console
  1428.  */
  1429. int
  1430. conxo(int n, char *buf)
  1431. {
  1432.     int retval;
  1433.  
  1434.     fflush(stdout);
  1435.     retval = write(FILENO(1), buf, n);
  1436.     Chk_Abort();
  1437.     return retval;
  1438. }
  1439.  
  1440. /*
  1441.  * conol -- output line to console
  1442.  */
  1443. int
  1444. conol(char *l)
  1445. {
  1446.     int retval;
  1447.     retval = fputs(l, stdout);
  1448.     fflush(stdout);
  1449.     Chk_Abort();
  1450.     return retval;
  1451. }
  1452.  
  1453. /*
  1454.  * conola -- output line array to console
  1455.  */
  1456. int
  1457. conola(char **l)
  1458. {
  1459.     for (; **l; ++l)
  1460.         if (conol(*l) < 0)
  1461.             return(-1);
  1462.     return 0;
  1463. }
  1464.  
  1465. /*
  1466.  * conoll -- output line with CRLF
  1467.  */
  1468. int
  1469. conoll(char *l)
  1470. {
  1471.     if (conol(l) < 0)
  1472.         return -1;
  1473.     if (conxo(2, "\r\n") < 0)
  1474.         return -1;
  1475.     return 0;
  1476. }
  1477.  
  1478. /*
  1479.  * conchk -- returns nonzero if characters available from console
  1480.  */
  1481. int
  1482. conchk(void)
  1483. {
  1484.     fflush(stdout);
  1485.     Chk_Abort();
  1486.     return(WaitForChar(DOSFH(0), 0L) != 0);
  1487. }
  1488.  
  1489. /*
  1490.  * coninc -- get input character from console
  1491.  */
  1492. int
  1493. coninc(int timeout)
  1494. {
  1495.     UBYTE ch;
  1496.  
  1497.     fflush(stdout);
  1498.     Chk_Abort();
  1499.     if (timeout > 0 && !WaitForChar(DOSFH(0), timeout * 1000000L))
  1500.         return(-1);
  1501.     if (read(FILENO(0), &ch, 1) < 1) return(-1);
  1502.     Chk_Abort();
  1503.     return((int)ch);
  1504. }
  1505.  
  1506. /*
  1507.  * T T S C A R R -- Copy desired character mode to global ttcarr for future
  1508.  * and later use.
  1509.  */
  1510. ttscarr(carrier) int carrier; {
  1511.     ttcarr = carrier;
  1512.     debug(F101, "ttscarr","",ttcarr);
  1513.     return(ttcarr);
  1514. }
  1515.  
  1516. static int
  1517. sendbreak(long time) {
  1518.     if (!serialopen) return(-1);
  1519.     /* flush queued output */
  1520.     TerminateWrite(1);
  1521.     nttoq = 0;
  1522.     pendwrite = TRUE;
  1523.     WriteIOB->IOSer.io_Command = SDCMD_SETPARAMS;
  1524.     WriteIOB->io_BrkTime = time;
  1525.     (void) DoIO((IOR *)WriteIOB);
  1526.     pendwrite = TRUE;
  1527.     WriteIOB->IOSer.io_Command = SDCMD_BREAK;
  1528.     WriteIOB->io_SerFlags &= ~SERF_QUEUEDBRK;
  1529.     SendIO((IOR *)WriteIOB);
  1530.     return(0);
  1531. }
  1532.  
  1533. /*
  1534.  * ttsndb -- send a BREAK
  1535.  *    flushes queued and active output
  1536.  */
  1537. int
  1538. ttsndb(void)
  1539. {
  1540.     return(sendbreak(275000L));
  1541. }
  1542.  
  1543. /*
  1544.  * ttsndlb -- send a long BREAK (1.5 sec)
  1545.  */
  1546. int
  1547. ttsndlb(void) {
  1548.     return(sendbreak(1500000L));
  1549. }
  1550.  
  1551. /*  T T G M D M  --  Get modem signals  */
  1552. /*
  1553.  Looks for the modem signals as defined by the BM_??? constants in
  1554.  ckcdeb.h, and returns those that are on as a bit mask.  Returns:
  1555.  -3 Not implemented
  1556.  -2 if the line does not have modem control
  1557.  -1 on error.
  1558.  >= 0 on success, with a bit mask containing the modem signals that are on.
  1559. */
  1560.  
  1561. int
  1562. ttgmdm(void) {
  1563.     struct IOExtSer *myread = ReadIOB;
  1564.     int z;
  1565.     UWORD status;
  1566.  
  1567.     if (!serialopen)
  1568.         return -1;
  1569.     Chk_Abort();
  1570.     if (pendread && !CheckIO((IOR *)myread)) return(0);
  1571.     if (TerminateRead() != 0) return(-1);
  1572.     myread->IOSer.io_Command = SDCMD_QUERY;
  1573.     if (DoIO((IOR *) myread) != 0)
  1574.         return -1;
  1575.     status = myread->io_Status;
  1576.     z = 0;
  1577.     if (status & (1<<2)) z |= BM_RNG;    /* active high */
  1578.     status = ~status;            /* rest are active low */
  1579.     if (status & (1<<3)) z |= BM_DSR;
  1580.     if (status & (1<<4)) z |= BM_CTS;
  1581.     if (status & (1<<5)) z |= BM_DCD;
  1582.     if (status & (1<<6)) z |= BM_RTS;
  1583.     if (status & (1<<7)) z |= BM_DTR;
  1584.     return(z);
  1585. }
  1586.  
  1587.  
  1588. /*
  1589.  * ttocq -- write char to serial device, queueing if necessary
  1590.  *    returns -2 on overrun, -1 on serial error
  1591.  *    use only in connect mode
  1592.  */
  1593. int
  1594. ttocq(char c)
  1595. {
  1596.     int i;
  1597.  
  1598.     if (!serialopen) return(-1);
  1599.     if (pendwrite && CheckIO((IOR *)WriteIOB))
  1600.     {
  1601.         pendwrite = FALSE;
  1602.         if (WaitIO((IOR *)WriteIOB) != 0) return(-1);
  1603.     }
  1604.     if (pendwrite)
  1605.     {
  1606.         if (nttoq >= NTTOQ) return(-2);        /* overrun */
  1607.         ttoq[(pttoq + nttoq++) % NTTOQ] = c;
  1608.     }
  1609.     else if (nttoq == 0)
  1610.         return(ttoc(c));
  1611.     else
  1612.     {
  1613.         i = ttoc(ttoq[pttoq]);
  1614.         ttoq[(pttoq + nttoq) % NTTOQ] = c;
  1615.         pttoq = (pttoq + 1) % NTTOQ;
  1616.         if (i < 0) return(-1);
  1617.     }
  1618.     return(1);
  1619. }
  1620.  
  1621. /*
  1622.  * ttonq -- returns number of characters in serial output queue
  1623.  */
  1624. int
  1625. ttonq(void)
  1626. {
  1627.         return(nttoq);
  1628. }
  1629.  
  1630. /*
  1631.  * ttgwsiz -- get window size
  1632.  */
  1633. extern int tt_rows, tt_cols;
  1634. int
  1635. ttgwsiz(void) {
  1636.     char t[20];
  1637.     int n;
  1638.  
  1639.     conol("\x9b\x30\x20\x71");
  1640.     for (n = 0; n < sizeof(t); n++) {
  1641.         if ((t[n] = coninc(1)) == -1) {
  1642.             t[n] = '\0';
  1643.             break;
  1644.         }
  1645.         if (t[n] == 'r') {
  1646.             t[++n] = '\0';
  1647.             break;
  1648.         }
  1649.     }
  1650.     if (sscanf(t, "\x9b" "1;1;%d;%d r", &tt_rows, &tt_cols) != 2) {
  1651.         tt_rows = tt_cols = 0;
  1652.         return -1;
  1653.     }
  1654.     return 0;
  1655. }
  1656.  
  1657. /*
  1658.  * conttb -- prepare for contti() usage
  1659.  */
  1660. void
  1661. conttb(void)
  1662. {
  1663.     /* flush queued input and output */
  1664.     queuedcon = -1;
  1665.     pttoq = nttoq = 0;
  1666. }
  1667.  
  1668. /*
  1669.  * contte -- end contti() usage
  1670.  *    this can be called after a tthang, it which case ttres will already
  1671.  *    have done this cleanup
  1672.  */
  1673. void
  1674. contte(void)
  1675. {
  1676.     /* clear any pending ^C, ^D interrupts */
  1677.     Chk_Abort();
  1678.  
  1679.     /* terminate any pending I/O */
  1680.     if (serialopen) SerialReset();
  1681. }
  1682.  
  1683. /*
  1684.  * contti -- wait for console or tty input
  1685.  *    returns next console input or -1 when serial input available
  1686.  */
  1687. int
  1688. contti(void)
  1689. {
  1690.     int i;
  1691.     LONG waitsigs;
  1692.     struct DosPacket *pkt = conpkt;
  1693.     struct IOExtSer *myread = ReadIOB;
  1694.     static UBYTE conchar;
  1695.     BPTR dosfh = DOSFH(0);
  1696.     struct FileHandle *fh = (struct FileHandle *)BADDR(dosfh);
  1697.  
  1698.     if (queuedcon >= 0)
  1699.     {
  1700.         conchar = queuedcon;
  1701.         queuedcon = -1;
  1702.         return((int)conchar);
  1703.     }
  1704.  
  1705.     if (!pendconsole)
  1706.     {    /* start a console read */
  1707.         pkt->dp_Port = conport;
  1708.         pkt->dp_Type = ACTION_READ;
  1709.         pkt->dp_Arg1 = (LONG)dosfh;
  1710.         pkt->dp_Arg2 = (LONG)&conchar;
  1711.         pkt->dp_Arg3 = 1;
  1712.         PutMsg(fh->fh_Process, pkt->dp_Link);
  1713.         pendconsole = TRUE;
  1714.     }
  1715.  
  1716.     if (queuedser < 0 && !pendread)
  1717.     {    /* start a serial read */
  1718.         myread->IOSer.io_Command = CMD_READ;
  1719.         myread->IOSer.io_Data    = (APTR)&serbufc;
  1720.         myread->IOSer.io_Length  = 1;
  1721.         SendIO((IOR *)myread);
  1722.         pendread = TRUE;
  1723.     }
  1724.  
  1725.     waitsigs = (1L << serport->mp_SigBit) | (1L << conport->mp_SigBit);
  1726.     for (;;)
  1727.     {
  1728.         if (pendwrite && CheckIO((IOR *)WriteIOB))
  1729.         {
  1730.             WaitIO((IOR *)WriteIOB);
  1731.             pendwrite = FALSE;
  1732.             if (nttoq > 0)
  1733.             {
  1734.                 i = ttoc(ttoq[pttoq]);
  1735.                 pttoq = (pttoq + 1) % NTTOQ;
  1736.                 --nttoq;
  1737.                 if (i < 0) return(-1);
  1738.             }
  1739.         }
  1740.  
  1741.         /* give the console first chance */
  1742.         if (GetMsg(conport))
  1743.         {
  1744.             pendconsole = FALSE;
  1745.             if (pkt->dp_Res1 != 1) return(-1);
  1746.             /* translate CSI to ESC [ */
  1747.             if (conchar == 0x9B)
  1748.             {
  1749.                     conchar = 0x1B;
  1750.                 queuedcon = '[';
  1751.             }
  1752.             return((int)conchar);
  1753.         }
  1754.  
  1755.         if (queuedser >= 0) return(-2);
  1756.  
  1757.         if (CheckIO((IOR *)myread))
  1758.             return((TerminateRead() == 0) ? -2 : -1);
  1759.  
  1760.         Wait(waitsigs);
  1761.     }
  1762. }
  1763.  
  1764. /* T G E T E N T -- Dummy routine to simulate curses/termcap. */
  1765.  
  1766. #ifdef MYCURSES
  1767. int
  1768. tgetent(char *s1, char *s2) {
  1769.     return(1);
  1770. }
  1771. #endif /* MYCURSES */
  1772.  
  1773. /* P S U S P E N D -- Put current process in background. */
  1774.  
  1775. /*
  1776.  * Even though this isn't supported on the Amiga, I return success anyway.
  1777.  * After all, the user can pop the window to the back and do something
  1778.  * else any time he wants.
  1779.  */
  1780.  
  1781. int
  1782. psuspend(int foo) {
  1783.     return 0;
  1784. }
  1785.  
  1786. /* P R I V _ functions -- all dummy on the Amiga. */
  1787.  
  1788. int
  1789. priv_ini(void) {
  1790.     return 0;
  1791. }
  1792.  
  1793.  
  1794. int
  1795. priv_on(void) {
  1796.     return 0;
  1797. }
  1798.  
  1799. int
  1800. priv_off(void) {
  1801.     return 0;
  1802. }
  1803.  
  1804. int
  1805. priv_can(void) {
  1806.     return 0;
  1807. }
  1808.