home *** CD-ROM | disk | FTP | other *** search
/ Columbia Kermit / kermit.zip / archives / ckc190.zip / ckotio.c < prev    next >
C/C++ Source or Header  |  1994-10-01  |  74KB  |  2,901 lines

  1. char *ckxv = "OS/2 Communications I/O, 5A(141), 1 Oct 94";
  2.  
  3. /* Define this symbol to allow setting the title bar... */
  4. /* Not recommended because the call to do this is undocumented. */
  5. /* #define CK_SETTITLE  */
  6.  
  7. /* C K O T I O  --  Kermit communications I/O support for OS/2 systems */
  8.  
  9. /*
  10.   Also contains code to emulate the UNIX alarm() function under OS/2
  11.   and a set of opendir/readdir/closedir, etc, functions.
  12. */
  13.  
  14. /*
  15.   Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
  16.   Columbia University Academic Information Systems, New York City.
  17.  
  18.   Copyright (C) 1985, 1993, Trustees of Columbia University in the City of New
  19.   York.  The C-Kermit software may not be, in whole or in part, licensed or
  20.   sold for profit as a software product itself, nor may it be included in or
  21.   distributed with commercial products or otherwise distributed by commercial
  22.   concerns to their clients or customers without written permission of the
  23.   Office of Kermit Development and Distribution, Columbia University.  This
  24.   copyright notice must not be removed, altered, or obscured.
  25.  
  26.   Originally adapted to OS/2 by Chris Adie <C.Adie@uk.ac.edinburgh>,
  27.   Edinburgh University Computing Service, 1988.
  28.  
  29.   Adapted to C-Kermit 5A by Kai Uwe Rommel <rommel@informatik.tu-muenchen.de>,
  30.   1992-93.  Many contributions by Jeffrey Altman for 5A(190), 1993-94.
  31. */
  32.  
  33. /* Includes */
  34.  
  35. #include "ckcdeb.h"            /* Typedefs, debug formats, etc */
  36. #include "ckcasc.h"            /* ASCII character names */
  37. #include "ckcker.h"            /* Kermit definitions */
  38. #include "ckcnet.h"            /* Kermit definitions */
  39. #include "ckuxla.h"            /* Translation tables */
  40.  
  41. #include <ctype.h>            /* Character types */
  42. #include <stdio.h>            /* Standard i/o */
  43. #include <io.h>                /* File io function declarations */
  44. #include <fcntl.h>
  45. #include <process.h>            /* Process-control functions */
  46. #include <string.h>            /* String manipulation declarations */
  47. #include <stdlib.h>            /* Standard library declarations */
  48. #include <sys/types.h>
  49. #include <sys/stat.h>
  50. #include <time.h>            /* Time functions */
  51. #include <signal.h>
  52.  
  53. #include <assert.h>
  54. #include <setjmp.h>
  55.  
  56. #include "ckodir.h"
  57.  
  58. /* Version herald(s) */
  59.  
  60. #include "ckuver.h"
  61. char ckxsystem[64] = HERALD;
  62. char *ckxsys = ckxsystem;
  63.  
  64. #ifdef __32BIT__
  65. static char *ckxrev = "32-bit";
  66. #else
  67. static char *ckxrev = "16-bit";
  68. #endif
  69.  
  70. /* OS/2 system header files & related stuff */
  71.  
  72. #ifndef __32BIT__
  73. #ifdef OS2PM
  74. #undef OS2PM
  75. #endif /* OS2PM */
  76. #define far _far
  77. #define near _near
  78. #define pascal _pascal
  79. #endif
  80.  
  81. #define    INCL_WINSWITCHLIST
  82. #define    INCL_ERRORS
  83. #define    INCL_KBD
  84. #ifdef OS2MOUSE
  85. #define INCL_MOU
  86. #endif /* OS2MOUSE */
  87. #define    INCL_VIO
  88. #define    INCL_DOSMISC
  89. #define    INCL_DOSPROCESS
  90. #define  INCL_DOSSEMAPHORES
  91. #define    INCL_DOSQUEUES
  92. #define    INCL_DOSSIGNALS
  93. #define    INCL_DOSDEVICES
  94. #define    INCL_DOSDEVIOCTL
  95. #define    INCL_DOSNLS
  96. #ifdef __32BIT__
  97. #define INCL_DOSASYNCTIMER
  98. #define INCL_DOSDATETIME
  99. #endif /* __32BIT__ */
  100. #ifdef OS2PM
  101. #define  INCL_DOSNMPIPES
  102. #endif /* OS2PM */
  103. #include <os2.h>    /* This pulls in a whole load of stuff */
  104. #ifdef CK_REXX
  105. #define  INCL_REXXSAA
  106. #include <rexxsaa.h>
  107. #endif /* CK_REXX */
  108. #undef COMMENT
  109.  
  110. #ifdef CHAR
  111. #undef CHAR
  112. #endif /* CHAR */
  113.  
  114. /*
  115.  Variables available to outside world:
  116.  
  117.    dftty  -- Pointer to default tty name string, like "/dev/tty".
  118.    dfloc  -- 0 if dftty is console, 1 if external line.
  119.    dfprty -- Default parity
  120.    dfflow -- Default flow control
  121.    ckxech -- Flag for who echoes console typein:
  122.      1 - The program (system echo is turned off)
  123.      0 - The system (or front end, or terminal).
  124.    functions that want to do their own echoing should check this flag
  125.    before doing so.
  126.  
  127.  Functions for assigned communication line (either external or console tty):
  128.  
  129.    sysinit()               -- System dependent program initialization
  130.    syscleanup()            -- System dependent program shutdown
  131.    ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access.
  132.    ttclos()                -- Close & reset the tty, releasing any access lock.
  133.    ttpkt(speed,flow,parity)-- Put the tty in packet mode
  134.                 or in DIALING or CONNECT modem control state.
  135.    ttvt(speed,flow)        -- Put the tty in virtual terminal mode.
  136.    ttinl(dest,max,timo,...) -- Timed read packet from the tty.
  137.    ttinc(timo)             -- Timed read character from tty.
  138.    ttchk()                 -- See how many characters in tty input buffer.
  139.    ttxin(n,buf)            -- Read n characters from tty (untimed).
  140.    ttol(string,length)     -- Write a string to the tty.
  141.    ttoc(c)                 -- Write a character to the tty.
  142.    ttflui()                -- Flush tty input buffer.
  143.    ttgspd()                -- Speed of tty line.
  144.  
  145. Functions for console terminal:
  146.  
  147.    conraw()  -- Set console into Raw mode
  148.    concooked() -- Set console into Cooked mode
  149.    conoc(c)  -- Unbuffered output, one character to console.
  150.    conol(s)  -- Unbuffered output, null-terminated string to the console.
  151.    conola(s) -- Unbuffered output, array of strings to the console.
  152.    conxo(n,s) -- Unbuffered output, n characters to the console.
  153.    conchk()  -- Check if characters available at console (bsd 4.2).
  154.         Check if escape char (^\) typed at console (System III/V).
  155.    coninc(timo)  -- Timed get a character from the console.
  156.  Following routines are dummies:
  157.    congm()   -- Get console terminal mode.
  158.    concb()   -- Put console into single char mode with no echo.
  159.    conres()  -- Restore console to congm mode.
  160.    conint()  -- Enable console terminal interrupts.
  161.    connoi()  -- No console interrupts.
  162.  
  163. Time functions
  164.  
  165.    sleep(t)  -- Like UNIX sleep
  166.    msleep(m) -- Millisecond sleep
  167.    ztime(&s) -- Return pointer to date/time string
  168.    rtimer() --  Reset timer
  169.    gtimer()  -- Get elapsed time since last call to rtimer()
  170. */
  171.  
  172.  
  173. /* Defines */
  174.  
  175. #define HUPTIME 1000            /* Milliseconds for hangup */
  176.  
  177. #ifdef NETCONN        /* Allow for long network hostnames */
  178. #define DEVNAMLEN 128
  179. #else            /* No networks, applies to OS/2 device names only. */
  180. #define DEVNAMLEN 14
  181. #endif /* NETCONN */
  182.  
  183. /* definitions hiding 32-bit / 16-bit differences */
  184.  
  185. #ifdef __32BIT__
  186.  
  187. USHORT DosDevIOCtl32(PVOID pData, USHORT cbData, PVOID pParms, USHORT cbParms,
  188.              USHORT usFunction, USHORT usCategory, HFILE hDevice)
  189. {
  190.   ULONG ulParmLengthInOut = cbParms, ulDataLengthInOut = cbData;
  191.   return (USHORT) DosDevIOCtl(hDevice, usCategory, usFunction,
  192.                   pParms, cbParms, &ulParmLengthInOut,
  193.                   pData, cbData, &ulDataLengthInOut);
  194. }
  195.  
  196. typedef ULONG U_INT;
  197. #define FILEFINDBUF FILEFINDBUF3 
  198. #define FSQBUFFER FSQBUFFER2
  199.  
  200. #define DosFindFirst(p1, p2, p3, p4, p5, p6) \
  201.         DosFindFirst(p1, p2, p3, p4, p5, p6, 1)
  202.  
  203. #define DosDevIOCtl DosDevIOCtl32
  204.  
  205. #else /* Not 32-bit ... */
  206.  
  207. typedef USHORT U_INT;
  208.  
  209. #define DosFindFirst(p1, p2, p3, p4, p5, p6) \
  210.         DosFindFirst(p1, p2, p3, p4, p5, p6, 0)
  211. #define DosQueryFSAttach(p1, p2, p3, p4, p5) \
  212.         DosQFSAttach(p1, p2, p3, p4, p5, 0)
  213.  
  214. #define DosQueryCurrentDisk     DosQCurDisk
  215. #define DosQueryFSInfo          DosQFSInfo
  216. #define DosSetFHState           DosSetFHandState
  217. #define DosWaitChild            DosCwait
  218. #define DosDevIOCtl             DosDevIOCtl2
  219.  
  220. #define DosQueryCp              DosGetCp
  221. #define DosSetProcessCp(x)      DosSetProcCp(x, 0)
  222.  
  223. #endif
  224.  
  225. /* Declarations */
  226.  
  227. /* dftty is the device name of the default device for file transfer */
  228. /* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */
  229.  
  230. extern long speed;
  231. extern int parity, fcharset, flow, ttcarr;
  232. extern KEY     *keymap;
  233. extern MACRO     *macrotab;
  234. #ifdef OS2PM
  235. extern int os2pm ;
  236. #endif /* OS2PM */
  237. #ifdef COMMENT
  238. /* This is to allow remote operation */
  239. char *dftty = "0"; /* stdin */
  240. int dfloc = 0;
  241. #else
  242. char *dftty = "com1"; /* COM1 */
  243. int dfloc = 1;
  244. #endif /* COMMENT */
  245.  
  246. int ttyfd = -1;        /* TTY file descriptor (not open yet) */
  247. int dfprty = 0;            /* Default parity (0 = none) */
  248. int ttprty = 0;            /* Parity in use. */
  249. int ttmdm = 0;            /* Modem in use. */
  250. int dfflow = FLO_NONE;        /* Default flow is KEEP = no change */
  251. int backgrd = 0;        /* Assume in foreground */
  252. int ttcarr = CAR_AUT;        /* Carrier handling mode. */
  253. int ckxech = 1; /* 0 if system normally echoes console characters, else 1 */
  254.  
  255. char startupdir[CCHMAXPATH] = ".";
  256.  
  257. /* Declarations of variables global within this module */
  258.  
  259. static struct rdchbuf_rec {        /* Buffer for serial characters */
  260.     unsigned char buffer[256];
  261.     U_INT length, index;
  262. } rdchbuf;
  263.  
  264. static long tcount;            /* Elapsed time counter */
  265. static int conmode, consaved;
  266. static int ttpmsk = 0377;        /* Parity stripping mask. */
  267. int ttpflg = 0;                /* Parity not sensed yet */
  268. static char ttnmsv[DEVNAMLEN+1];
  269. static int islocal, ishandle;
  270. int pid = 0;
  271. static DCBINFO ttydcb;
  272.  
  273. static int nOldCP;
  274. static char szOldTitle[80];
  275.  
  276. #ifdef __32BIT__
  277. HMTX hmtxAlarmSem = (HMTX) 0 ;
  278. HMTX hmtxScreenSem = (HMTX) 0 ;
  279. HEV  hevKeyAvail = (HEV) 0 ;
  280. HEV  hevAlarmTimer = (HEV) 0 ;
  281. HTIMER hAlarmTimer = 0 ;
  282.  
  283. #define THRDSTKSIZ      32768
  284. TID KbdHandlerThreadID = (TID) 0 ;
  285. #endif /* __32BIT__ */
  286.  
  287. /* Forward declarations */
  288.  
  289. _PROTOTYP( static int os2setdtr,  (int) );
  290. _PROTOTYP( static int os2setflow, (int) );
  291. _PROTOTYP( static int os2setcarr, (int) );
  292. _PROTOTYP( static int ttsettings, (int, int) );
  293. _PROTOTYP( int ttsetspd, (long) );
  294. _PROTOTYP( int concooked, (void) );
  295. _PROTOTYP( int os2rexxinit, (void) ); 
  296. _PROTOTYP( int os2setcp, (int) );
  297. _PROTOTYP( int os2getcplist, (int *, int) );
  298. _PROTOTYP( int os2getcp, (void) );
  299. _PROTOTYP( int os2settitle, (char *) );
  300. _PROTOTYP( int os2gettitle, (char *, int) );
  301. _PROTOTYP( void keybufinit, (void) ) ;
  302. _PROTOTYP( void keybufcleanup, (void) ) ;
  303. _PROTOTYP( int KbdHandlerInit, (void) ) ;
  304. _PROTOTYP( int KbdHandlerCleanup, ( void ) ) ;
  305. _PROTOTYP( void KbdHandlerThread, ( void * ) ) ;
  306.  
  307. #ifdef OS2PM
  308. _PROTOTYP( APIRET ConnectToPM, (void) );
  309. _PROTOTYP( APIRET ReadFromPM, (void) );
  310. #endif /* OS2PM */
  311.  
  312. /* Control-C interrupt handler */
  313.  
  314. void cc_trap(int sig) {
  315.   signal(sig, cc_trap);
  316. #ifdef __EMX__
  317.   signal(sig, SIG_ACK);
  318. #endif
  319. }
  320.  
  321. /* Saving/restoring of hot handles */
  322.  
  323. static int savedtty = 0;
  324. static long savedspeed;
  325. static LINECONTROL savedlc;
  326. static DCBINFO saveddcb;
  327. static BYTE savedstat;
  328.  
  329. savetty() {
  330.  
  331.   if (ttyfd != -1) {
  332.     savedspeed = ttgspd();
  333.     DosDevIOCtl(&savedlc,sizeof(savedlc),NULL,0,
  334.         ASYNC_GETLINECTRL,IOCTL_ASYNC,ttyfd);
  335.     DosDevIOCtl(&saveddcb,sizeof(saveddcb),NULL,0,
  336.         ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd);
  337.     DosDevIOCtl(&savedstat,sizeof(savedstat),NULL,0,
  338.         ASYNC_GETMODEMOUTPUT,IOCTL_ASYNC,ttyfd);
  339.     savedtty = 1;
  340.   }
  341.  
  342.   return 0;
  343. }
  344.  
  345. restoretty() {
  346.   MODEMSTATUS ms;
  347.   UINT cmd = 0, data = 0 ;
  348.  
  349.   if (savedtty) {
  350.     ttsetspd(savedspeed);
  351.     DosDevIOCtl(&data,sizeof(data),&cmd,sizeof(cmd),
  352.                 DEV_FLUSHOUTPUT,IOCTL_GENERAL,ttyfd);
  353.     DosDevIOCtl(NULL,0,&savedlc,sizeof(savedlc),
  354.         ASYNC_SETLINECTRL,IOCTL_ASYNC,ttyfd);
  355.     ms.fbModemOn = 0;
  356.     ms.fbModemOff = 255;
  357.     if (savedstat & DTR_ON) ms.fbModemOn |= DTR_ON;
  358.     else ms.fbModemOff &= DTR_OFF;
  359.     if (savedstat & RTS_ON) ms.fbModemOn |= RTS_ON;
  360.     else ms.fbModemOff &= RTS_OFF;
  361.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  362.         ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  363.     DosDevIOCtl(NULL,0,&saveddcb,sizeof(saveddcb),
  364.         ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd);
  365.     savedtty = 0;
  366.   }
  367.  
  368.   return 0;
  369. }
  370.  
  371. /* Code Page functions */
  372.  
  373. int
  374. os2getcp() {
  375.     U_INT cbData, nCodePage[8];
  376.     return (DosQueryCp(sizeof(nCodePage), nCodePage, &cbData) == 0)
  377.            ? nCodePage[0] : 0;
  378. }
  379.  
  380.  
  381. #ifdef COMMENT
  382. int
  383. os2checkcp(cp) int cp; {
  384.     U_INT cbData, nCodePage[8], nCnt;
  385.     if (DosQueryCp(sizeof(nCodePage), nCodePage, &cbData))
  386.       return FALSE;
  387.     for (nCnt = 1; nCnt < cbData / sizeof(nCodePage[0]); nCnt++)
  388.       if (nCodePage[nCnt] == cp)
  389.     return TRUE;
  390.     return FALSE;
  391. }
  392. #endif /* COMMENT */
  393.  
  394. int
  395. os2getcplist(cplist, size) int *cplist; int size; {
  396.     U_INT cbData;
  397.     if (DosQueryCp(size, (U_INT *) cplist, &cbData))
  398.       return 0;
  399.     return cbData / sizeof(int);
  400. }
  401.  
  402. int                    /* Change code page */
  403. os2setcp(cp) int cp; {
  404.     return((VioSetCp(0, cp, 0) == 0) && 
  405.        (KbdSetCp(0, cp, 0) == 0) &&
  406.        (DosSetProcessCp(cp) == 0));
  407. }
  408.  
  409. /*  S Y S I N I T  --  System-dependent program initialization.  */
  410.  
  411. sysinit() {
  412.     char *ptr;
  413.     int n;
  414. #ifdef __32BIT__
  415.     PTIB pptib;
  416.     PPIB pppib;
  417.  
  418.     DosGetInfoBlocks(&pptib, &pppib);
  419.     pid = pppib -> pib_ulpid;
  420.     DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
  421.  
  422.     DosCreateMutexSem( 0, &hmtxAlarmSem, 0, 0 ) ;
  423.     DosCreateMutexSem( 0, &hmtxScreenSem, 0, 0 ) ;
  424.  
  425.     DosCreateEventSem( 0, &hevAlarmTimer, DC_SEM_SHARED, 0 ) ;
  426.     keybufinit() ;   /* Must come before the KbdHandler */
  427.     KbdHandlerInit() ;
  428. #else
  429.     PIDINFO pi;
  430.  
  431.     DosGetPID(&pi);
  432.     pid = pi.pid;
  433.     DosError(HARDERROR_DISABLE | EXCEPTION_DISABLE);
  434. #endif
  435.  
  436.     signal(SIGINT, cc_trap);
  437.     signal(SIGBREAK, cc_trap);
  438.  
  439.     os2gettitle(szOldTitle, sizeof(szOldTitle));
  440.  
  441.     nOldCP = os2getcp();
  442.  
  443.     switch (nOldCP) {
  444.       case 437:
  445.     fcharset = FC_CP437;
  446.     break;
  447.       case 850:
  448.     fcharset = FC_CP850;
  449.     break;
  450.       case 852:
  451.     fcharset = FC_CP852;
  452.     break;
  453.       }
  454.  
  455.     sprintf(ckxsystem, " OS/2 %1d.%02d %s", _osmajor / 10, _osminor, ckxrev);
  456. #ifdef __IBMC__
  457.     setvbuf(stdout, NULL, _IONBF, 0);
  458.     setmode(1, O_TEXT);
  459. #endif /* __IBMC__ */
  460.  
  461.     strcpy(startupdir, GetLoadPath());
  462.     if ( (ptr = strrchr(startupdir, '\\')) != NULL )
  463.       *ptr = 0;
  464.     for (ptr = startupdir; *ptr; ptr++)    /* Convert backslashes to slashes */
  465.       if (*ptr == '\\')
  466.     *ptr = '/';
  467.     n = (int)strlen(startupdir);    /* Add slash to end if necessary */
  468.     if (n > -1 && n < CCHMAXPATH)
  469.       if (startupdir[n-1] != '/') {
  470.       startupdir[n] = '/';
  471.       startupdir[n+1] = '\0';
  472.       }
  473.  
  474.     strcpy(ttnmsv, dftty);
  475.     islocal = isatty(0) && !ttiscom(0);
  476.     if (!islocal) {
  477.     os2setdtr(1);
  478.     ttsettings(ttprty,0);
  479.     os2setflow(flow);
  480.     os2setcarr(ttcarr == CAR_ON);
  481.     } else
  482.       concooked();            /* Initialize keyboard */
  483.  
  484. #ifdef NETCONN
  485.     netinit();
  486. #endif /* NETCONN */
  487.  
  488. #ifdef CK_REXX
  489.       os2rexxinit() ;
  490. #endif /* CK_REXX */
  491.  
  492.     return(0);
  493. }
  494.  
  495.  
  496. /*  S Y S C L E A N U P  --  System-dependent program cleanup.  */
  497.  
  498. syscleanup() {
  499. #ifdef __32BIT__
  500.    DosCloseMutexSem( hmtxAlarmSem ) ;
  501.    DosCloseMutexSem( hmtxScreenSem ) ;
  502.    DosCloseEventSem( hevAlarmTimer ) ;
  503.    KbdHandlerCleanup() ;
  504.    keybufcleanup() ;
  505. #endif /* __32BIT__ */
  506. #ifdef NETCONN
  507.    netcleanup() ;
  508. #endif /* NETCONN */
  509.    os2settitle(szOldTitle);
  510.    os2setcp(nOldCP);
  511.    signal(SIGINT, SIG_DFL);
  512.    signal(SIGBREAK, SIG_DFL);
  513.  
  514.    return(0);
  515. }
  516.  
  517. /* Timeout handler for communication line input functions */
  518.  
  519. static jmp_buf kbbuf;            /* Timeout longjmp targets */
  520. static jmp_buf sjbuf;
  521.  
  522. #ifndef __EMX__
  523. unsigned alarm(unsigned);        /* Prototype */
  524. #endif /* __EMX__ */
  525. SIGTYP (*saval)(int) = NULL;        /* For saving alarm() handler */
  526.  
  527. SIGTYP
  528. timerh(foo) int foo; {            /* For ttinl() timeout */
  529.     ttimoff();
  530.     longjmp(sjbuf,1);
  531. }
  532.  
  533. VOID
  534. ttimoff() {                /* Turn off any timer interrupts */
  535.     int xx;
  536.     xx = alarm(0);
  537.     if (saval) {            /* Restore any previous */
  538.     signal(SIGALRM,saval);        /* alarm handler. */
  539.     saval = NULL;
  540.     } else {
  541.     signal(SIGALRM,SIG_IGN);
  542.     }
  543. }
  544.  
  545.  
  546. /*  O S 2 S E T F L O W -- set flow state of tty */
  547.  
  548. static int
  549. os2setflow(int nflow) {
  550.     /* Get the current settings */
  551.     if (DosDevIOCtl(&ttydcb,sizeof(ttydcb),NULL,0,
  552.             ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd))
  553.         return(-1);
  554.  
  555.     ttydcb.fbCtlHndShake = MODE_DTR_CONTROL; 
  556.     ttydcb.fbFlowReplace &= ~(MODE_AUTO_RECEIVE | MODE_AUTO_TRANSMIT |
  557.     /* clear only a few */    MODE_RTS_CONTROL  | MODE_RTS_HANDSHAKE);
  558.  
  559.     if (nflow == FLO_XONX) {
  560.         ttydcb.fbFlowReplace |= 
  561.       (MODE_AUTO_RECEIVE | MODE_AUTO_TRANSMIT | MODE_RTS_CONTROL);
  562.     }
  563.     else if (nflow == FLO_RTSC) {
  564.         ttydcb.fbCtlHndShake |= MODE_CTS_HANDSHAKE;
  565.     ttydcb.fbFlowReplace |= MODE_RTS_HANDSHAKE;
  566.     }
  567.     else if ( nflow != FLO_KEEP ) {
  568.       ttydcb.fbFlowReplace |= MODE_RTS_CONTROL;
  569.       }
  570.  
  571.     /* set write timeout */
  572.     ttydcb.fbTimeout &= ~MODE_NO_WRITE_TIMEOUT;
  573.     ttydcb.usWriteTimeout = 15 * 100;    /* 15-second timeout */
  574.  
  575.     /* Read "some" data from line mode */
  576.     ttydcb.fbTimeout &= ~MODE_NOWAIT_READ_TIMEOUT;
  577.     ttydcb.fbTimeout |= MODE_WAIT_READ_TIMEOUT;
  578.  
  579.     /* Set DCB */
  580.     if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  581.             ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
  582.         return(-1);
  583.  
  584.     if (nflow != FLO_RTSC && nflow != FLO_KEEP) {/* keep RTS permanently on */
  585.         MODEMSTATUS ms;
  586.     UINT data;
  587.     ms.fbModemOn = RTS_ON;
  588.     ms.fbModemOff = 255;
  589.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  590.             ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  591.     }
  592.  
  593.     return(0);
  594. }
  595.  
  596.  
  597. static int
  598. os2setcarr(int ncarr) {
  599.     /* Get the current settings */
  600.     if (DosDevIOCtl(&ttydcb,sizeof(ttydcb),NULL,0,
  601.             ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd))
  602.         return(-1);
  603.  
  604.     if (ncarr)
  605.       ttydcb.fbCtlHndShake |=  MODE_DCD_HANDSHAKE;
  606.     else
  607.       ttydcb.fbCtlHndShake &= ~MODE_DCD_HANDSHAKE;
  608.  
  609.     /* Set DCB */
  610.     if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  611.             ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
  612.         return(-1);
  613.  
  614.     return(0);
  615. }
  616.  
  617.  
  618. /*  O S 2 S E T D T R -- set state of DTR signal */
  619.  
  620. static int
  621. os2setdtr(int on) {
  622.     MODEMSTATUS ms;
  623.     UINT data;
  624.  
  625.     if (ttyfd == -1) return(0);
  626.     ms.fbModemOn = on ? DTR_ON : 0;
  627.     ms.fbModemOff = on ? 255 : DTR_OFF;
  628.     return(DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  629.                ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd));
  630. }
  631.  
  632.  
  633. /*  T T S E T T I N G S  --  Set the device driver parity and stop bits */
  634.  
  635. static int
  636. ttsettings(int par, int stop) {
  637.     LINECONTROL lc;
  638.  
  639.     if (DosDevIOCtl(&lc,sizeof(lc),NULL,0,
  640.             ASYNC_GETLINECTRL,IOCTL_ASYNC,ttyfd))
  641.       return(-1); /* Get line */
  642.  
  643. #ifdef COMMENT
  644.     switch (par) {
  645.     case 'o':
  646.         lc.bDataBits = 7;    /* Data bits */
  647.         lc.bParity   = 1;
  648.         break;
  649.         case 'e':
  650.         lc.bDataBits = 7;    /* Data bits */
  651.         lc.bParity   = 2;
  652.         break;
  653.     case 'm':
  654.         lc.bDataBits = 7;    /* Data bits */
  655.         lc.bParity   = 3;
  656.         break;
  657.     case 's':
  658.         lc.bDataBits = 7;    /* Data bits */
  659.         lc.bParity   = 4;
  660.         break;
  661.     default :
  662.         lc.bDataBits = 8;    /* Data bits */
  663.         lc.bParity   = 0;    /* No parity */
  664.     }
  665. #else
  666. /* Always let Kermit handle parity itself */
  667.     lc.bDataBits = 8;    /* Data bits */
  668.     lc.bParity   = 0;    /* No parity */
  669. #endif /* COMMENT */
  670.     switch (stop) {
  671.         case 2:
  672.             lc.bStopBits = 2;    /* Two stop bits */
  673.             break;
  674.         case 1:
  675.             lc.bStopBits = 0;    /* One stop bit */
  676.             break;
  677.         default:         /* No change */
  678.             break;
  679.     }
  680.     if (DosDevIOCtl(NULL,0,&lc,sizeof(lc),
  681.             ASYNC_SETLINECTRL,IOCTL_ASYNC,ttyfd))
  682.       return(-1); /* Set line */
  683.     return(0);
  684. }
  685.  
  686. /*  T T O P E N  --  Open a tty for exclusive access.  */
  687.  
  688. /*  Returns 0 on success, -1 on failure.  */
  689. /*
  690.   If called with lcl < 0, sets value of lcl as follows:
  691.   0: the terminal named by ttname is the job's controlling terminal.
  692.   1: the terminal named by ttname is not the job's controlling terminal.
  693.   But watch out: if a line is already open, or if requested line can't
  694.   be opened, then lcl remains (and is returned as) -1.
  695. */
  696. ttopen(char *ttname, int *lcl, int modem, int spare) {
  697.     char *x; extern char* ttyname();
  698.     U_INT action, res;
  699.     debug(F111,"ttopen DEVNAMLEN","",DEVNAMLEN);
  700.     debug(F111,"ttopen entry modem",ttname,modem);
  701.     debug(F101," ttyfd","",ttyfd);
  702.  
  703.     rdchbuf.length = rdchbuf.index = 0;
  704.  
  705.     if (ttyfd > -1) {            /* if device already opened */
  706.         if (strncmp(ttname,ttnmsv,DEVNAMLEN)) { /* new & old names equal? */
  707.         debug(F111,"ttopen closing",ttname,ttyfd);
  708.         ttclos(ttyfd);        /* no, close old ttname, open new */
  709.         } else {            /* else same, ignore this call, */
  710.         debug(F111,"ttopen already open",ttname,ttyfd);
  711.         return(0);            /* and return. */
  712.     }
  713.     }
  714.  
  715.     if (*lcl == 0) return(-1);        /* Won't open in local mode */
  716.  
  717.     ishandle = 0;
  718.     ttmdm = modem;            /* Make this available to other fns */
  719.     debug(F111,"ttname",ttname,(int) strlen(ttname));
  720.     strncpy(ttnmsv, ttname, DEVNAMLEN);    /* Keep copy of name locally. */
  721.  
  722. #ifdef NETCONN
  723.     if (modem < 0) return os2_netopen(ttname, lcl, -modem);
  724. #endif /* NETCONN */
  725.  
  726. /*
  727.   This code lets you give Kermit an open file descriptor for a serial
  728.   communication device, rather than a device name.  Kermit assumes that the
  729.   line is already open, conditioned with the right parameters, etc.
  730. */
  731.     for (x = ttname; isdigit(*x); x++) ; /* Check for all digits */
  732.  
  733.     if (*x == '\0') {
  734.     ttyfd = atoi(ttname);
  735.     ishandle = 1;
  736.     *lcl = 1;            /* Assume it's local. */
  737.     if (ttiscom(ttyfd))
  738.       return savetty();
  739.     ttyfd = -1;
  740.     return(-4);
  741.     }
  742.     if (res = DosOpen(ttname,(PHFILE)&ttyfd,&action,0L,0,FILE_OPEN,
  743.                       OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE |
  744.                       OPEN_FLAGS_FAIL_ON_ERROR ,0L)) {
  745.     ttyfd = -1;
  746.     return((res == ERROR_SHARING_VIOLATION) ? -5 : -1);
  747.     }
  748.     debug(F111,"ttopen ok",ttname,*lcl);
  749.  
  750. /* Caller wants us to figure out if line is controlling tty */
  751.     if (*lcl == -1) {
  752.     *lcl = 1;            /* Can never transfer with console */
  753.     }
  754.     if (!ttiscom(ttyfd)) {        /* Not a serial port */
  755.         ttclos(0);
  756.         return(-4);
  757.     }
  758.     savetty();
  759.     ttprty = dfprty;            /* Make parity the default parity */
  760.     if (ttsettings(ttprty,0)) return(-1);
  761.     return(ttflui());
  762. }
  763.  
  764. /*  T T I S C O M  --  Is the given handle an open COM port? */
  765.  
  766. ttiscom(int f) {
  767.     DCBINFO testdcb;
  768.     /* Read DCB */
  769.     if (DosDevIOCtl(&testdcb,sizeof(testdcb),NULL,0,
  770.             ASYNC_GETDCBINFO,IOCTL_ASYNC,f)) {
  771.         return( 0 );            /* Bad, not a serial port */
  772.     }
  773.     return( 1 );            /* Good */
  774. }
  775.  
  776. /*  T T C L O S  --  Close the TTY.  */
  777.  
  778. ttclos(int spare) {
  779. #ifdef NETCONN
  780.     if (ttmdm < 0) return os2_netclos();
  781. #endif /* NETCONN */
  782.     if (ttyfd == -1) return(0);        /* Wasn't open. */
  783.     if (savedtty)
  784.       restoretty();
  785.     if (!ishandle)
  786.       DosClose(ttyfd);
  787.     ishandle = 0;
  788.     ttyfd = -1;
  789.     return(0);
  790. }
  791.  
  792. /*  T T G S P D  --  return speed of COM port, or of default line */
  793.  
  794. long
  795. ttgspd() {
  796.     long sp = 0;
  797.     struct {
  798.       long current_rate;
  799.       char current_fract;
  800.       long minimum_rate;
  801.       char minimum_fract;
  802.       long maximum_rate;
  803.       char maximum_fract;
  804.     } speed;
  805.  
  806.     if (ttyfd == -1) return(-1);
  807.  
  808.     if (DosDevIOCtl(&speed,sizeof(speed),NULL,0,0x0063,IOCTL_ASYNC,ttyfd) == 0)
  809.       return speed.current_rate;
  810.     else
  811.       if (DosDevIOCtl(&sp,sizeof(sp),NULL,0,
  812.               ASYNC_GETBAUDRATE,IOCTL_ASYNC,ttyfd) == 0) 
  813.       return sp;
  814.     else
  815.       return -1;
  816. }
  817.  
  818. ttsetspd(long sp) {
  819.     struct {
  820.       long rate;
  821.       char fract;
  822.     } speed;
  823.  
  824.     if (ttyfd == -1) return(-1);    /* Not open */
  825.  
  826.     if (sp > 65535L) {
  827.       speed.rate = sp;
  828.       speed.fract = 0;
  829.       return DosDevIOCtl(NULL,0,&speed,sizeof(speed),0x0043,IOCTL_ASYNC,ttyfd);
  830.     } else
  831.       return DosDevIOCtl(NULL,0,&sp,sizeof(sp),
  832.              ASYNC_SETBAUDRATE,IOCTL_ASYNC,ttyfd);
  833. }
  834.  
  835.  
  836.  
  837. /*  T T H A N G -- Hangup phone line */
  838.  
  839. tthang() {
  840. #ifdef NETCONN
  841.     if (ttmdm < 0) return os2_netclos();
  842. #endif /* NETCONN */
  843. /*
  844.   Perhaps better to either let user specify the hangup interval, or else
  845.   do something with carrier -- e.g. if CD was on when we entered this routine,
  846.   then cancel the sleep as soon as it goes down, so we don't sleep longer
  847.   than we need to.
  848. */
  849.     if (os2setdtr(0)) return -1;
  850.     msleep(HUPTIME);
  851.     os2setdtr(1);
  852.     return 1;
  853. }
  854.  
  855.  
  856. /*  T T R E S  --  Restore terminal to "normal" mode.  */
  857.  
  858. ttres() {                /* Restore the tty to normal. */
  859.     if (ttyfd == -1) return(-1);    /* Not open */
  860.     return(0);
  861. }
  862.  
  863.  
  864. /*  T T P K T  --  Condition the communication line for packets. */
  865. /*        or for modem dialing */
  866.  
  867. /*  If called with speed > -1, also set the speed.  */
  868. /*  Returns 0 on success, -1 on failure.  */
  869.  
  870. ttpkt(long speed, int flow, int parity) {
  871.     int s;
  872.  
  873.     if (ttmdm < 0) return(0);
  874.     if (ttyfd < 0)  return(-1);        /* Not open. */
  875.     if (speed < 0) return(-1);
  876.  
  877.     ttprty = parity;
  878.     ttpflg = 0;
  879.     ttpmsk = ttprty ? 0177 : 0377;    /* Parity stripping mask */
  880.     os2setdtr(1);
  881.     if (ttsetspd(speed)) return(-1);
  882.     if (ttsettings(ttprty,0)) return(-1);
  883.     if (os2setflow(flow)) return(-1);
  884.     if (os2setcarr(ttcarr == CAR_ON && flow != FLO_DIAL)) return(-1);
  885.  
  886.     DosSetPrty(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0);
  887.     return(0);
  888. }
  889.  
  890.  
  891. /*  T T V T -- Condition communication line for use as virtual terminal  */
  892.  
  893. ttvt(long speed, int flow) {
  894.  
  895.     if (ttmdm < 0) return(0);
  896.     if (ttyfd < 0) return(-1);        /* Not open. */
  897.     if (speed < 0) return(-1);
  898.  
  899.     ttprty = parity;
  900.     os2setdtr(1);
  901.     if (ttsetspd(speed)) return(-1);
  902.     if (ttsettings(ttprty,0)) return(-1);
  903.     if (os2setflow(flow)) return (-1);
  904.     if (os2setcarr(ttcarr == CAR_ON || ttcarr == CAR_AUT)) return(-1);
  905.  
  906.     return(0);
  907. }
  908.  
  909.  
  910. /*  T T S S P D  --  Return the speed if OK, otherwise -1 */
  911.  
  912. int
  913. ttsspd(int speed) {
  914.     long s;
  915.  
  916.     if (speed < 0) return(-1);
  917.  
  918.     s = (long) speed * 10L;
  919.     ttsetspd(s);
  920.     return(0);    
  921. }
  922.  
  923.  
  924. /*  T T F L U I  --  Flush tty input buffer */
  925.  
  926. ttflui() {
  927.     char parm=0;
  928.     long int data;
  929.     int i;
  930.  
  931. #ifdef NETCONN
  932.    if (ttmdm < 0) return os2_netflui() ;
  933. #endif /* NETCONN */
  934.     rdchbuf.index = rdchbuf.length = 0;        /* Flush internal buffer */
  935.     DosDevIOCtl(&data,sizeof(data),&parm,sizeof(parm),
  936.         DEV_FLUSHINPUT,IOCTL_GENERAL,ttyfd); /* Flush driver */
  937.     return(0);
  938. }
  939.  
  940.  
  941. /*  T T C H K  --  Tell how many characters are waiting in tty input buffer  */
  942.  
  943. ttchk() {
  944.     USHORT data[2];
  945. #ifdef NETCONN
  946.     if (ttmdm < 0) return os2_nettchk();
  947. #endif /* NETCONN */
  948.     if(DosDevIOCtl(data,sizeof(data),NULL,0,
  949.            ASYNC_GETINQUECOUNT,IOCTL_ASYNC,ttyfd)) return(0);
  950.     else return((rdchbuf.length-rdchbuf.index)+data[0]);
  951. }
  952.  
  953.  
  954. /*  T T X I N  --  Get n characters from tty input buffer  */
  955.  
  956. /*  Returns number of characters actually gotten, or -1 on failure  */
  957.  
  958. /*  Intended for use only when it is known that n characters are actually */
  959. /*  available in the input buffer.  */
  960.  
  961. ttxin(int n, CHAR *buf) {
  962.     int i, j;
  963.  
  964.     if (ttyfd < 0) return(-1);        /* Not open. */
  965.     i = 0;
  966.     while (i < n) {
  967.         if ((j = ttinc(0)) < 0) break;
  968.         buf[i++] = j;
  969.     }
  970.     return(i);
  971. }
  972.  
  973. /*  T T O L  --  Similar to "ttinl", but for writing.  */
  974. /*
  975.   Outputs all n characters of s, or else fails with -2 if the
  976.   connection is broken, or with -1 upon some other kind of error.
  977. */
  978. int
  979. ttol(CHAR *s, int n) {
  980.     UINT i;
  981.     int  rc = 0 ;
  982.     int  charsleft = n ;
  983.     CHAR *chars = s ;
  984.  
  985.     while ( rc >= 0 && charsleft ) {
  986. #ifdef NETCONN
  987.     if (ttmdm < 0) {
  988.         rc = os2_nettol(chars,charsleft);
  989.     } else {
  990. #endif /* NETCONN */
  991.         if (ttyfd < 0)
  992.           rc = -1 ;        /* Not open. */
  993.         if(DosWrite(ttyfd,s,n,(PVOID)&i))
  994.           rc = -1 ;
  995.         else
  996.           rc = i ;
  997. #ifdef NETCONN
  998.     }
  999. #endif /* NETCONN */
  1000.  
  1001.     if ( rc >= 0 ) {
  1002.         charsleft -= rc ;
  1003.         chars += rc ;
  1004.     }
  1005.     }
  1006.     return rc ;
  1007. }
  1008.  
  1009.  
  1010. /*  T T O C  --  Output a character to the communication line  */
  1011.  
  1012. ttoc(char c) {
  1013.     UINT i;
  1014. #ifdef NETCONN
  1015.     if (ttmdm < 0) return os2_nettoc(c);
  1016. #endif /* NETCONN */
  1017.     if (ttyfd < 0) return(-1);        /* Not open. */
  1018.     if(DosWrite(ttyfd,&c,1,(PVOID)&i)) return(-1);
  1019.     else return(i);
  1020. }
  1021.  
  1022. #ifndef NOTTOCI
  1023. #define NEWTTOCI
  1024.  
  1025. /*  T T O C I  --  Output a character to the communication line immediately */
  1026. #ifdef NEWTTOCI
  1027. int
  1028. ttoci(char c) {
  1029.     int x;
  1030.     BYTE i;
  1031.     ULONG Data = 0L ;
  1032.  
  1033. #ifdef NETCONN
  1034.     if (ttmdm < 0) return os2_nettoc(c);
  1035. #endif /* NETCONN */
  1036.     if (ttyfd < 0) return(-1);          /* Not open. */
  1037.  
  1038.     x =
  1039.      DosDevIOCtl(&Data,sizeof(Data),0,0,ASYNC_GETCOMMSTATUS,IOCTL_ASYNC,ttyfd);
  1040.     if ( Data ) {
  1041.     debug( F101,"ttoci: Query COM Status","",Data) ;
  1042.     return(Data) ;
  1043.     }
  1044.     x = DosWrite(ttyfd,&c,1,(PVOID)&i) ;
  1045.     if (x) {
  1046.     debug(F101,"ttoci failure status","",x);
  1047.     return(x);
  1048.     } else return(0);
  1049. }
  1050.  
  1051. #else /* NEWTTOCI */
  1052.  
  1053. int
  1054. ttoci(char c) {
  1055.     int x;
  1056. #ifdef NETCONN
  1057.     if (ttmdm < 0) return os2_nettoc(c);
  1058. #endif /* NETCONN */
  1059.     if (ttyfd < 0) return(-1);        /* Not open. */
  1060.     x =  DosDevIOCtl(NULL,0,&c,sizeof(c),ASYNC_TRANSMITIMM,IOCTL_ASYNC,ttyfd);
  1061.     if (x) {
  1062.     debug(F101,"ttoci failure status","",x);
  1063.     return(x);
  1064.     } else return(0);
  1065. }
  1066. #endif /* NEWTTOCI */
  1067. #endif /* NOTTOCI */
  1068.  
  1069. /*  T T I N L  --  Read a packet from the communication device.  */
  1070. /*
  1071.   blah blah
  1072. */
  1073. ttinl(CHAR *dest, int max, int timo, CHAR eol, CHAR start, int turn) {
  1074.     int x = 0, c, i, m;
  1075.  
  1076. #ifdef COMMENT
  1077. /* old code -- worked, but no parity detection, no DDK */
  1078.  
  1079.     if (ttyfd < 0) return(-1);        /* Not open. */
  1080.     *dest = '\0';            /* Clear destination buffer */
  1081.     i = 0;                /* Next char to process */
  1082.     while (1) {
  1083.     if ((c = ttinc(timo)) == -1) {
  1084.         x = -1;
  1085.         break;
  1086.     }
  1087.         dest[i] = c;            /* Got one. */
  1088.     if (dest[i] == eol) {
  1089.         dest[++i] = '\0';
  1090.         return(i);
  1091.     }
  1092.     if (i++ > max) {
  1093.         debug(F101,"ttinl buffer overflow","",i);
  1094.         x = -1;
  1095.         break;
  1096.     }
  1097.     }
  1098.     debug(F100,"ttinl timout","",0);    /* Get here on timeout. */
  1099.     debug(F111," with",dest,i);
  1100.     return(x);                /* and return error code. */
  1101. #else /* !COMMENT */
  1102.  
  1103. /* New code, lifted from the UNIX version */
  1104.  
  1105.     unsigned char ch;
  1106.     int pktlen = -1;
  1107.     int lplen = 0;
  1108.     int havelen = 0;
  1109.  
  1110.     if (ttyfd < 0) return(-1);          /* Not open. */
  1111.  
  1112.     debug(F101,"ttinl max","",max);
  1113.     debug(F101,"ttinl timo","",timo);
  1114.  
  1115.     *dest = '\0';                       /* Clear destination buffer */
  1116.     if (timo < 0) timo = 0;        /* Safety */
  1117.     if (timo) {                /* Don't time out if timo == 0 */
  1118.     int xx;
  1119.     saval = signal(SIGALRM,timerh);    /* Enable timer interrupt */
  1120.     xx = alarm(timo);        /* Set it. */
  1121.     debug(F101,"ttinl alarm","",xx);
  1122.     }
  1123.     if (setjmp(sjbuf)) {                /* Timer went off? */
  1124.     debug(F100,"ttinl timout","",0); /* Get here on timeout. */
  1125.     /* debug(F110," with",(char *) dest,0); */
  1126.     ttimoff();            /* Turn off timer */
  1127.     return(-1);            /* and return error code. */
  1128.     } else {                /* Timer didn't go off yet, start. */
  1129.     register int i, m, n;        /* Local variables */
  1130.     int flag = 0;
  1131.  
  1132.     debug(F000,"ttinl start","",start);
  1133.     flag = 0;            /* Start of packet flag */
  1134.  
  1135.     ttpmsk = m = (ttprty) ? 0177 : 0377; /* Set parity stripping mask. */
  1136.  
  1137. /* Now read into destination, stripping parity and looking for the */
  1138. /* the packet terminator, and also for two Ctrl-C's typed in a row. */
  1139.  
  1140.     i = 0;                /* Destination index */
  1141.     debug(F101,"ttinl eol","",eol);
  1142.  
  1143.     while ((i < max-1)  &&  (n = ttinc(0)) >= 0) {
  1144.         if (n == 0) {
  1145.         /* For some reason, ttinc() sometimes delivers */
  1146.         /* a spurious NUL.  Discarding it effectively */
  1147.         /* works around the problem. */
  1148.         debug(F100,"ttinc got NUL","",0);
  1149.         continue;
  1150.         }        
  1151.         ch = n & 0xff;
  1152. /*
  1153.   Here, if OS/2 C-Kermit is ever taught to run in remote mode, we would need
  1154.   to check for transfer cancellation (e.g. user types 3 Ctrl-C's in a row
  1155.   while Kermit is trying to read a packet).  See ckutio.c for the code.
  1156. */
  1157.         if ((flag == 0) && ((n & 0x7f) == start)) flag = 1;
  1158.         if (flag) {
  1159.                  dest[i++] = n & ttpmsk;
  1160.             if (i == 2) {
  1161.             pktlen = xunchar(dest[1] & 0x7f);
  1162.             havelen = (pktlen > 1);
  1163.             debug(F101,"ttinl length","",pktlen);
  1164.             } else if (i == 5 && pktlen == 0) {
  1165.             lplen = xunchar(dest[4] & 0x7f);
  1166.             } else if (i == 6 && pktlen == 0) {
  1167.             pktlen = lplen * 95 + xunchar(dest[5] & 0x7f) + 5;
  1168.             havelen = 1;
  1169.             debug(F101,"ttinl length","",pktlen);
  1170.         }
  1171.         } else {
  1172.         debug(F101,"ttinl skipping","",n);
  1173.         continue;
  1174.         }
  1175.  
  1176.     /* Check for end of packet */
  1177.  
  1178.         if ((havelen && (i > pktlen+1) &&
  1179.          (!turn || (turn && (n & 0x7f) == eol))) ) {
  1180.         if (deblog) {
  1181.              if ((n & 0x7f) != eol) {
  1182.                 debug(F101,"ttinl EOP length","",pktlen);
  1183.                 debug(F101,"ttinl i","",i);
  1184.             } else debug(F101,"ttinl got eol","",eol);
  1185.         }
  1186.         dest[i] = '\0';        /* Terminate the string, */
  1187.         /* Parity checked yet? */
  1188.         debug(F101,"ttinl ttpflg","",ttpflg);
  1189.         debug(F101,"ttinl ttprty","",ttprty);
  1190.         debug(F101,"ttinl ttpmsk","",ttpmsk);
  1191.             if (ttpflg++ == 0 && ttprty == 0) {
  1192.             if ((ttprty = parchk(dest,start,i)) > 0) { /* No, check. */
  1193.             int j;
  1194.             debug(F101,"ttinl senses parity","",ttprty);
  1195.             debug(F110,"ttinl packet before",dest,0);
  1196.             ttpmsk = 0x7f;
  1197.             for (j = 0; j < i; j++)
  1198.               dest[j] &= 0x7f;    /* Strip parity from packet */
  1199.             debug(F110,"ttinl packet after ",dest,0);
  1200.             } else ttprty = 0;    /* restore if parchk error */
  1201.         }
  1202.         if (timo) {            /* Turn off timer. */
  1203.             ttimoff();
  1204.         }
  1205.         debug(F111,"ttinl got", dest,i);
  1206.         return(i);
  1207.         }
  1208.     }                /* end of while() */
  1209.     ttimoff();
  1210.     return(-1);
  1211.     }
  1212. #endif /* COMMENT */
  1213. }
  1214.  
  1215.  
  1216. /*  T T I N C --  Read a character from the communication line  */
  1217.  
  1218. /* The time should be in secs for consistency with the other modules in    */
  1219. /* kermit.  To retain the option of using times of less than 1s a negative */
  1220. /* parameter is interpreted as meaning multiples of 0.01s                  */
  1221.  
  1222. static rdch(void);
  1223.  
  1224. ttinc(timo) int timo; {
  1225.     int m, i;
  1226.     char ch = 0;
  1227. #ifdef NETCONN
  1228.     if (ttmdm < 0) return os2_netinc(timo);
  1229. #endif /* NETCONN */
  1230.     m = (ttprty) ? 0177 : 0377;        /* Parity stripping mask. */
  1231.     if (ttyfd < 0) return(-1);        /* Not open. */
  1232.  
  1233.     if (timo == 0) {            /* Untimed. */
  1234.  
  1235.         if (ttydcb.usReadTimeout != 9) {
  1236.         ttydcb.usReadTimeout = 9;    /* Test every  0.1s per call */
  1237.         if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  1238.                 ASYNC_SETDCBINFO,1,ttyfd))
  1239.           return(-1);
  1240.     }
  1241.  
  1242.         do
  1243.           i = rdch();
  1244.         while (i < 0);   /* Wait for a character. */
  1245.  
  1246.         return(i & m);
  1247.     }
  1248.  
  1249.     if (timo < 0)
  1250.         timo= -timo - 1;
  1251.     else
  1252.         timo = timo * 100 - 1;
  1253.  
  1254.     if (ttydcb.usReadTimeout != timo) { /* Set timeout value */
  1255.        ttydcb.usReadTimeout = timo;
  1256.        if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  1257.                ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
  1258.           return(-1);
  1259.     }
  1260.  
  1261.     i = rdch();
  1262.  
  1263.     if (i < 0) return(-1);
  1264.     else return(i & m);
  1265. }
  1266.  
  1267. /*  RDCH -- Read characters from the serial port, maintaining an internal
  1268.             buffer of characters for the sake of efficiency. */
  1269. static
  1270. rdch() {
  1271.  
  1272.     if (rdchbuf.index == rdchbuf.length) {
  1273.     rdchbuf.index = 0;
  1274.         if (DosRead(ttyfd,rdchbuf.buffer,sizeof(rdchbuf.buffer),
  1275.                     &rdchbuf.length)) {
  1276.         rdchbuf.length = 0;
  1277.         return(-1);
  1278.         }
  1279.     }
  1280.  
  1281.     return( (rdchbuf.index < rdchbuf.length)
  1282.             ? rdchbuf.buffer[rdchbuf.index++] : -1 );
  1283. }
  1284.  
  1285. /*  T T S C A R R  --  Set ttcarr variable, controlling carrier handling.
  1286.  *
  1287.  *  0 = Off: Always ignore carrier. E.g. you can connect without carrier.
  1288.  *  1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect.
  1289.  *  2 = Auto: For "modem direct": The same as "Off".
  1290.  *            For real modem types: Heed carrier during connect, but ignore
  1291.  *                it anytime else.  Compatible with pre-5A C-Kermit versions.
  1292.  */
  1293.  
  1294. int
  1295. ttscarr(carrier) int carrier; {
  1296.     ttcarr = carrier;
  1297.     debug(F101, "ttscarr","",ttcarr);
  1298.     return(ttcarr);
  1299. }
  1300.  
  1301. /*  T T G M D M  --  Get modem signals  */
  1302. /*
  1303.  Looks for the modem signals CTS, DSR, and CTS, and returns those that are
  1304.  on in as its return value, in a bit mask as described for ttwmdm.  Returns:
  1305.  -3 Not implemented
  1306.  -2 if the line does not have modem control
  1307.  -1 on error.
  1308.  >= 0 on success, with a bit mask containing the modem signals that are on.
  1309. */
  1310.  
  1311. int
  1312. ttgmdm() {
  1313.     BYTE instat, outstat;
  1314.     int modem = 0;
  1315.  
  1316.     if(DosDevIOCtl(&instat,sizeof(instat),NULL,0,
  1317.            ASYNC_GETMODEMINPUT,IOCTL_ASYNC,ttyfd))
  1318.        return(-1);
  1319.     if(DosDevIOCtl(&outstat,sizeof(outstat),NULL,0,
  1320.            ASYNC_GETMODEMOUTPUT,IOCTL_ASYNC,ttyfd))
  1321.        return(-1);
  1322.  
  1323.     /* Clear To Send */
  1324.     if (instat & CTS_ON) modem |= BM_CTS;
  1325.     /* Data Set Ready */
  1326.     if (instat & DSR_ON) modem |= BM_DSR;
  1327.     /* Carrier */
  1328.     if (instat & DCD_ON) modem |= BM_DCD;
  1329.     /* Ring Indicate */
  1330.     if (instat & RI_ON)  modem |= BM_RNG;
  1331.  
  1332.     /* Data Terminal Ready */
  1333.     if (outstat & DTR_ON) modem |= BM_DTR;
  1334.     /* Request To Send */
  1335.     if (outstat & RTS_ON) modem |= BM_RTS;
  1336.  
  1337.     return(modem);
  1338. }
  1339.  
  1340. /*  T T S N D B  --  Send a BREAK signal  */
  1341.  
  1342. ttsndb() {
  1343.     MODEMSTATUS ms;
  1344.     UINT data, i;
  1345. #ifdef NETCONN
  1346.     if (ttmdm < 0) return os2_netbreak();
  1347. #endif /* NETCONN */
  1348.     ms.fbModemOn = RTS_ON;
  1349.     ms.fbModemOff = 255;
  1350.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  1351.         ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  1352.  
  1353.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  1354.         ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);    /* Break on */
  1355.     DosSleep(275L);                    /* ZZZzzz */
  1356.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  1357.         ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);    /* Break off */
  1358. }
  1359.  
  1360. /*  T T S N D L B  --  Send a LONG BREAK signal  */
  1361.  
  1362. ttsndlb() {
  1363.     MODEMSTATUS ms;
  1364.     UINT data, i;
  1365. #ifdef NETCONN
  1366.     if (ttmdm < 0) return os2_netbreak();
  1367. #endif /* NETCONN */
  1368.     ms.fbModemOn = RTS_ON;
  1369.     ms.fbModemOff = 255;
  1370.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  1371.         ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  1372.  
  1373.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  1374.         ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);    /* Break on */
  1375.     DosSleep(1800L);                    /* ZZZzzz */
  1376.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  1377.         ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);    /* Break off */
  1378. }
  1379.  
  1380. #ifndef __EMX__
  1381. /*  S L E E P  --  Emulate the Unix sleep function  */
  1382.  
  1383. unsigned sleep(t) unsigned t; {
  1384.     DosSleep((long)t*1000);
  1385. }
  1386. #endif
  1387.  
  1388.  
  1389. /*  M S L E E P  --  Millisecond version of sleep().  */
  1390.  
  1391. /* Intended only for small intervals.  For big ones, just use sleep(). */
  1392.  
  1393. msleep(m) int m; {
  1394.     DosSleep((long)m);
  1395. }
  1396.  
  1397.  
  1398. /*  R T I M E R --  Reset elapsed time counter  */
  1399.  
  1400. void rtimer() {
  1401.     tcount = time((long *)NULL);
  1402. }
  1403.  
  1404.  
  1405. /*  G T I M E R --  Get current value of elapsed time counter in seconds  */
  1406.  
  1407. int gtimer(void) {
  1408.     int x;
  1409.     x = (int) (time( (long *) 0 ) - tcount);
  1410.     return( (x < 0) ? 0 : x );
  1411. }
  1412.  
  1413.  
  1414. /*  Z T I M E  --  Return date/time string  */
  1415.  
  1416. void ztime(char **s) {
  1417.     long clock_storage;
  1418.  
  1419.     clock_storage = time( (long *) 0 );
  1420.     *s = ctime( &clock_storage );
  1421. }
  1422.  
  1423. /*  C O N O C  --  Output a character to the console terminal  */
  1424.  
  1425. int conoc(char c) {
  1426.     write(1,&c,1);
  1427. }
  1428.  
  1429.  
  1430. /*  C O N X O  --  Write x characters to the console terminal  */
  1431.  
  1432. int conxo(int x, char *s) {
  1433.     write(1,s,x);
  1434. }
  1435.  
  1436.  
  1437. /*  C O N O L  --  Write a line to the console terminal  */
  1438.  
  1439. int conol(char *s) {
  1440.     int len;
  1441.     len = strlen(s);
  1442.     write(1,s,len);
  1443. }
  1444.  
  1445.  
  1446. /*  C O N O L A  --  Write an array of lines to the console terminal */
  1447.  
  1448. conola(s) char *s[]; {
  1449.     int i;
  1450.     for (i=0 ; *s[i] ; i++) conol(s[i]);
  1451. }
  1452.  
  1453.  
  1454. /*  C O N O L L  --  Output a string followed by CRLF  */
  1455.  
  1456. conoll(s) char *s; {
  1457.     conol(s);
  1458.     write(1,"\r\n",2);
  1459. }
  1460.  
  1461.  
  1462. /*  C O N C H K  --  Return how many characters available at console  */
  1463.  
  1464. int
  1465. conchk() {
  1466.     KBDKEYINFO k;
  1467. #ifdef __32BIT__
  1468.     return keyinbuf() ;
  1469. #else
  1470.     KbdPeek(&k,0);
  1471.     return( (k.fbStatus & 0x40) ? 1 : 0 );
  1472. #endif /* __32BIT__ */
  1473. }
  1474.  
  1475.  
  1476. /*  C O N I N C  --  Get a character from the console  */
  1477.  
  1478. int
  1479. coninc(timo) int timo; {
  1480.     int c;
  1481.     extern int what;
  1482.  
  1483.     while ( (c = congks(timo)) >= 0x100 ) {
  1484.     if ( c > 0x200 && isdigit(c & 0xFF) || c == 0x109 /* TAB */ ) {
  1485.         break;
  1486.     } else {
  1487.         /* This allows up- and down-arrow for command recall. */
  1488.         if (c == 584 || c == 328) { /* Up arrow = Ctrl-B */
  1489.         if (what == W_COMMAND)
  1490.           return(2);
  1491.         } else if (c == 592 || c == 336) { /* Down arrow = Ctrl-N */
  1492.         if (what == W_COMMAND)
  1493.           return(14);
  1494.         }
  1495.     }
  1496.     }
  1497.     return (c & 0xFF);
  1498. }
  1499.  
  1500. SIGTYP
  1501. kbdtimo(int sig) {
  1502. #ifdef __EMX__
  1503.     signal(SIGALRM, SIG_ACK);
  1504. #endif
  1505.     signal(SIGALRM, saval);
  1506.     longjmp(kbbuf, 1);
  1507. }
  1508.  
  1509. #ifndef SHIFT_KEY_IN
  1510. #define SHIFT_KEY_IN    KBDTRF_SHIFT_KEY_IN
  1511. #endif /* SHIFT_KEY_IN */
  1512.  
  1513. #ifndef CONTROL
  1514. #define CONTROL         KBDSTF_CONTROL
  1515. #define SCROLLLOCK      KBDSTF_SCROLLLOCK
  1516. #define SCROLLLOCK_ON   KBDSTF_SCROLLLOCK_ON
  1517. #define NUMLOCK         KBDSTF_NUMLOCK
  1518. #define NUMLOCK_ON      KBDSTF_NUMLOCK_ON
  1519. #define LEFTSHIFT       KBDSTF_LEFTSHIFT
  1520. #define RIGHTSHIFT      KBDSTF_RIGHTSHIFT
  1521. #endif /* CONTROL */
  1522.  
  1523. int
  1524. congks(timo) int timo; {
  1525.     KBDKEYINFO k;
  1526.     int c ;
  1527. #ifdef __32BIT__
  1528.     APIRET rc ;
  1529.     int timeout ;
  1530. #endif /* __32BIT__ */
  1531. #ifdef OS2PM
  1532.     static int pipeopen = 0 ;
  1533. #endif /* OS2PM */
  1534.  
  1535.     if (!islocal) {
  1536.     c = 0;
  1537.     if ( read(ttyfd, &c, 1) < 1 )
  1538.       return -1;
  1539.     return c;
  1540.     }
  1541.     for (;;) {
  1542. #ifdef __32BIT__
  1543. #ifdef OS2PM
  1544.     if ( !os2pm ) {
  1545. #endif /* OS2PM */
  1546.         if ( timo < -1 )
  1547.           timeout = -timo * 10 ;
  1548.         if ( timo == -1 || timo == 0 )
  1549.           timeout = SEM_INDEFINITE_WAIT ;
  1550.         if ( timo > 0 )
  1551.           timeout = timo * 1000 ;
  1552.  
  1553.         rc = DosWaitEventSem( hevKeyAvail, timeout ) ;
  1554.         switch ( rc ) {
  1555.           case NO_ERROR:
  1556.         getkey( &c ) ;
  1557.         return c ;
  1558.           case ERROR_TIMEOUT:
  1559.         return -1 ;
  1560.           default:
  1561.         return -1 ;
  1562.         }
  1563. #ifdef OS2PM
  1564.     } else {
  1565.         if ( !pipeopen )
  1566.           if ( !ConnectToPM() )
  1567.         pipeopen = 1 ;
  1568.           else {
  1569.           os2pm = 0 ;
  1570.           return -1 ;
  1571.           }
  1572.         return ReadFromPM() ;
  1573.     }
  1574. #endif /* OS2PM */
  1575. #else /* not __32BIT__ */
  1576.         if (timo <= 0)
  1577.       KbdCharIn(&k, IO_WAIT, 0);
  1578.     else {
  1579.         saval = signal(SIGALRM, kbdtimo);
  1580.         alarm(timo);
  1581.         
  1582.         if (setjmp(kbbuf))
  1583.           return -1;        /* What about signal(SIGALRM, saval)? */
  1584.         else
  1585.           KbdCharIn(&k, IO_WAIT, 0);
  1586.  
  1587.         alarm(0);
  1588.         signal(SIGALRM, saval);
  1589.     }
  1590.  
  1591.     if ( k.chChar || k.chScan ) {
  1592.             c = k.chChar;
  1593.  
  1594.             if (c == 0x00) c = 0x100 | k.chScan;
  1595.             if (c == 0xE0) c = 0x200 | k.chScan;
  1596.  
  1597.         switch (c) {    /* Handle ambiguous keypad and space keys */
  1598.             case '\t':
  1599.         return k.chScan == 0x0F ? 0x100 | c : c;
  1600.           case '\b':
  1601.         return k.chScan == 0x0E ? DEL : c;
  1602.           case ESC:
  1603.         return ((k.fsState & LEFTSHIFT) || (k.fsState & RIGHTSHIFT)
  1604.             ? 0x100 | c : c);
  1605.           case DEL:
  1606.         return 0x200 | c;
  1607.           case ' ':
  1608.         return (k.fsState & CONTROL) ? 0x200 | c : c;
  1609.           case '+':
  1610.         return k.chScan == 0x4E ? 0x200 | c : c;
  1611.           case '-':
  1612.         return k.chScan == 0x4A ? 0x200 | c : c;
  1613.           case '*':
  1614.         return k.chScan == 0x37 ? 0x200 | c : c;
  1615.           case '/':
  1616.         return k.chScan == 0xE0 ? 0x200 | c : c;
  1617.           case '\r':
  1618.           case '\n':
  1619.         return k.chScan == 0xE0 ? 0x200 | c : c;
  1620.           case '.':
  1621.           case ',':
  1622.         return k.chScan == 0x53 ? 0x200 | c : c;
  1623.           case '0':
  1624.           case '1':
  1625.           case '2':
  1626.           case '3':
  1627.           case '4':
  1628.           case '5':
  1629.           case '6':
  1630.           case '7':
  1631.           case '8':
  1632.           case '9':
  1633.         return k.chScan >= 0x47 ? 0x200 | c : c;
  1634.           default:
  1635.         return c;
  1636.         }
  1637.         }
  1638.         if ( (k.fbStatus & SHIFT_KEY_IN) && (k.fsState & NUMLOCK) )
  1639.       return (k.fsState & NUMLOCK_ON) ? 0x2FE : 0x1FE;
  1640.  
  1641.         if ( (k.fbStatus & SHIFT_KEY_IN) && (k.fsState & SCROLLLOCK) )
  1642.       return (k.fsState & SCROLLLOCK_ON) ? 0x2FF : 0x1FF;
  1643. #endif  /* __32BIT__ */
  1644.     }
  1645. }
  1646.  
  1647. int
  1648. conraw() {
  1649.     KBDINFO k;
  1650.  
  1651.     if (!islocal) return(0);
  1652.     conmode = 1;
  1653.     k.cb = sizeof(k);
  1654.     KbdGetStatus(&k,0);
  1655.     k.fsMask &= ~(KEYBOARD_ECHO_ON | 
  1656.           KEYBOARD_ASCII_MODE);
  1657.     k.fsMask |=  (KEYBOARD_ECHO_OFF  
  1658.         | KEYBOARD_BINARY_MODE  
  1659.       | KEYBOARD_SHIFT_REPORT);  /* Generates excessive key reports */
  1660.     return(KbdSetStatus(&k,0));  /* But lets us see ScrollLock, Ctrl, Alt */
  1661. }
  1662.  
  1663. int
  1664. concooked() {
  1665.     KBDINFO k;
  1666.  
  1667.     if (!islocal) return(0);
  1668.     conmode = 0;
  1669.     k.cb = sizeof(k);
  1670.     KbdGetStatus(&k,0);
  1671.     k.fsMask &= ~(KEYBOARD_ECHO_OFF | 
  1672.           KEYBOARD_BINARY_MODE |
  1673.           KEYBOARD_SHIFT_REPORT);
  1674.     k.fsMask |=  (KEYBOARD_ECHO_ON | 
  1675.           KEYBOARD_ASCII_MODE);
  1676.     return(KbdSetStatus(&k,0));
  1677. }
  1678.  
  1679. /*  C O N B I N  --  Put console in binary mode  */
  1680.  
  1681. /*  Returns 0 if ok, -1 if not  */
  1682.  
  1683. conbin(char esc) {
  1684.     if (!islocal) return(0);          /* only for real ttys */
  1685.     conraw();
  1686. }
  1687.  
  1688. /*  C O N C B  -- Put console into single char mode with no echo. */
  1689.  
  1690. concb(char esc) {
  1691.     if (!islocal) return(0);          /* only for real ttys */
  1692.     concooked();
  1693. }
  1694.  
  1695.  
  1696. /*  C O N G M  -- Get console terminal mode. */
  1697.  
  1698. congm() {}
  1699.  
  1700. /*  C O N R E S -- Restore console to congm mode. */
  1701.  
  1702. conres() {}
  1703.  
  1704.  
  1705. /*  C O N I N T -- Enable console terminal interrupts. */
  1706.  
  1707. void conint(f, s) SIGTYP (*f)(int), (*s)(int); {
  1708.     signal(SIGINT, f);
  1709.     signal(SIGBREAK, f);
  1710. }
  1711.  
  1712.  
  1713. /*  C O N N O I -- No console interrupts. */
  1714.  
  1715. void connoi() {
  1716.     signal(SIGINT, cc_trap);
  1717.     signal(SIGBREAK, cc_trap);
  1718. }
  1719.  
  1720.  
  1721. /* privilege dummy */
  1722.  
  1723. int priv_chk() {return 0;}
  1724.  
  1725.  
  1726. #ifndef __EMX__
  1727.  
  1728. /* alarm() implementation for all others, emx/gcc already has it built-in */
  1729.  
  1730. #ifdef __32BIT__
  1731.  
  1732. #define STACK 
  1733. static BOOL alrm, running, isalarm;
  1734. static UINT delay;
  1735.  
  1736. static VOID
  1737. alarm_thread(VOID *args) {
  1738.     ULONG post_count ;
  1739.  
  1740.     for (;;) {
  1741.  
  1742.     DosWaitEventSem( hevAlarmTimer, SEM_INDEFINITE_WAIT ) ;
  1743.     DosRequestMutexSem( hmtxAlarmSem, SEM_INDEFINITE_WAIT ) ; 
  1744.     DosResetEventSem( hevAlarmTimer, &post_count ) ;
  1745.     if ( alrm ) {
  1746.         debug( F100, "alarm() triggered", "", 0 ) ;
  1747.         alrm = FALSE;
  1748.         isalarm = TRUE;
  1749.         DosKillProcess(DKP_PROCESS, pid);
  1750.         }     
  1751.     DosReleaseMutexSem( hmtxAlarmSem ) ; 
  1752.     }
  1753.  
  1754.     running = FALSE;
  1755.     _endthread();
  1756. }
  1757.  
  1758. static void
  1759. alarm_signal(int sig) {
  1760.   signal(SIGTERM, SIG_DFL);
  1761.   if (isalarm) {
  1762.     isalarm = FALSE;
  1763. /*  signal(SIGTERM, SIG_DFL); */
  1764.     raise(SIGALRM);
  1765.   } else {
  1766. /*  signal(SIGTERM, SIG_DFL); */
  1767.     DosKillProcess(DKP_PROCESS, pid);
  1768.   }
  1769. }
  1770.  
  1771. unsigned
  1772. alarm(unsigned sec) {
  1773.   TID tid;
  1774.   unsigned old;
  1775.   APIRET rc = 0 ;
  1776.   ULONG TimeInterval = sec * 1000 ;
  1777.  
  1778.   DosRequestMutexSem( hmtxAlarmSem, SEM_INDEFINITE_WAIT ) ; 
  1779.   if ( delay ) {
  1780.    rc = DosStopTimer( hAlarmTimer ) ;
  1781.    debug(F101,"alarm(0) DosStopTimer","",rc) ;
  1782.    hAlarmTimer = 0 ;
  1783.    }
  1784.  
  1785.   old = delay;
  1786.   delay = sec;
  1787.  
  1788.   if ( alrm = (delay > 0) ) {
  1789.     signal(SIGTERM, alarm_signal);
  1790.     rc = DosAsyncTimer( TimeInterval, (HSEM) hevAlarmTimer, &hAlarmTimer ) ;
  1791.     debug(F101,"alarm(t) DosAsyncTimer: t","",TimeInterval) ;
  1792.       }
  1793.    else
  1794.       debug(F100,"alarm() reset","",0);
  1795.  
  1796.    DosReleaseMutexSem( hmtxAlarmSem ) ; 
  1797.  
  1798.   if ( !running )
  1799.   {
  1800.     running = TRUE;
  1801.     tid = _beginthread( &alarm_thread, 0, THRDSTKSIZ, 0 ) ;
  1802.     DosSetPrty(PRTYS_THREAD, PRTYC_REGULAR, PRTYD_MAXIMUM, tid);
  1803.   }
  1804.  
  1805.   return old;
  1806. }
  1807.  
  1808. #else
  1809.  
  1810.  
  1811. #define STACK 2048
  1812. static PBYTE pstack;
  1813. static BOOL alrm, running;
  1814. static USHORT delay;
  1815.  
  1816. #pragma check_stack(off)
  1817.  
  1818. static VOID FAR alarm_thread(VOID)
  1819. {
  1820.   for (;;)
  1821.   {
  1822.     DosSleep(1000L);
  1823.     DosEnterCritSec();
  1824.  
  1825.     if ( alrm )
  1826.       if ( --delay == 0 ) {
  1827.     alrm = FALSE;
  1828.     DosFlagProcess(pid, FLGP_PID, PFLG_A, 1);
  1829.       }
  1830.  
  1831.     DosExitCritSec();
  1832.   }
  1833.  
  1834.   running = FALSE;
  1835.   DosExit(EXIT_THREAD, 0);
  1836. }
  1837.  
  1838. #pragma check_stack()
  1839.  
  1840. static VOID PASCAL FAR alarm_signal(USHORT sigarg, USHORT signum)
  1841. {
  1842.   PFNSIGHANDLER prev;
  1843.   USHORT action;
  1844.   DosSetSigHandler(alarm_signal, &prev, &action, SIGA_ACKNOWLEDGE, SIG_PFLG_A);
  1845.   raise(SIGALRM);
  1846. }
  1847.  
  1848. unsigned alarm(unsigned sec)
  1849. {
  1850.   PFNSIGHANDLER prev;
  1851.   USHORT action;
  1852.   TID tid;
  1853.   unsigned old;
  1854.  
  1855.   if ( pstack == NULL )
  1856.   {
  1857.     pstack = malloc(STACK);
  1858.     assert(pstack != NULL);
  1859.     DosSetSigHandler(alarm_signal, &prev, &action, SIGA_ACCEPT, SIG_PFLG_A);
  1860.   }
  1861.  
  1862.   DosEnterCritSec();
  1863.  
  1864.   old = delay;
  1865.   delay = sec;
  1866.   alrm = (delay > 0);
  1867.  
  1868.   DosExitCritSec();
  1869.  
  1870.   if ( !running )
  1871.   {
  1872.     running = TRUE;
  1873.     DosCreateThread(alarm_thread, &tid, pstack + STACK);
  1874.     DosSetPrty(PRTYS_THREAD, PRTYC_REGULAR, 0, tid);
  1875.   }
  1876.  
  1877.   return old;
  1878. }
  1879.  
  1880. #endif
  1881.  
  1882.  
  1883. /*
  1884.  *  A public domain implementation of BSD directory routines for
  1885.  *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
  1886.  *  August 1897
  1887.  *  Ported to OS/2 by Kai Uwe Rommel
  1888.  *  December 1989, February 1990
  1889.  *  Change for HPFS support, October 1990
  1890.  */
  1891.  
  1892. int attributes = A_DIR | A_HIDDEN | A_RONLY | A_SYSTEM | A_ARCHIVE ;
  1893.  
  1894. static char *getdirent(char *);
  1895. static void free_dircontents(struct _dircontents *);
  1896.  
  1897. static HDIR hdir;
  1898. static U_INT count;
  1899. static FILEFINDBUF find;
  1900.  
  1901. int IsFileSystemFAT(char *dir)
  1902. {
  1903.   static USHORT nLastDrive = -1, nResult;
  1904.   ULONG lMap;
  1905.   BYTE bData[64], bName[3];
  1906.   U_INT nDrive, cbData;
  1907.   FSQBUFFER *pData = (FSQBUFFER *) bData;
  1908.  
  1909.   /* We separate FAT and HPFS file systems here. */
  1910.  
  1911.   if ( isalpha(dir[0]) && (dir[1] == ':') )
  1912.     nDrive = toupper(dir[0]) - '@';
  1913.   else
  1914.     DosQueryCurrentDisk(&nDrive, &lMap);
  1915.  
  1916.   if ( nDrive == nLastDrive )
  1917.     return nResult;
  1918.  
  1919.   bName[0] = (char) (nDrive + '@');
  1920.   bName[1] = ':';
  1921.   bName[2] = 0;
  1922.  
  1923.   nLastDrive = nDrive;
  1924.   cbData = sizeof(bData);
  1925.  
  1926.   if ( !DosQueryFSAttach(bName, 0, FSAIL_QUERYNAME, (PVOID) pData, &cbData) )
  1927.     nResult = !strcmp(pData -> szFSDName + pData -> cbName, "FAT");
  1928.   else
  1929.     nResult = FALSE;
  1930.  
  1931.   return nResult;
  1932. }
  1933.  
  1934. DIR *opendir(char *name)
  1935. {
  1936.   struct stat statb;
  1937.   DIR *dirp;
  1938.   char c;
  1939.   char *s;
  1940.   struct _dircontents *dp;
  1941.   char nbuf[MAXPATHLEN + 1];
  1942.   int len;
  1943.  
  1944.   strcpy(nbuf, name);
  1945.   if ((len = strlen(nbuf)) == 0)
  1946.     return NULL;
  1947.  
  1948.   if ( ((c = nbuf[len - 1]) == '\\' || c == '/') && (len > 1) )
  1949.   {
  1950.     nbuf[len - 1] = 0;
  1951.     --len;
  1952.  
  1953.     if ( nbuf[len - 1] == ':' )
  1954.     {
  1955.       strcpy(nbuf + len, "\\.");
  1956.       len += 2;
  1957.     }
  1958.   }
  1959.   else
  1960.     if ( nbuf[len - 1] == ':' )
  1961.     {
  1962.       strcpy(nbuf+len, ".");
  1963.       ++len;
  1964.     }
  1965.  
  1966.   if (stat(nbuf, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
  1967.     return NULL;
  1968.  
  1969.   if ( (dirp = malloc(sizeof(DIR))) == NULL )
  1970.     return NULL;
  1971.  
  1972.   if ( nbuf[len - 1] == '.' && (len == 1 || nbuf[len - 2] != '.') )
  1973.     strcpy(nbuf + len - 1, "*");
  1974.   else
  1975.     if ( ((c = nbuf[len - 1]) == '\\' || c == '/') && (len == 1) )
  1976.       strcpy(nbuf + len, "*");
  1977.     else
  1978.       strcpy(nbuf + len, "\\*");
  1979.  
  1980.   dirp -> dd_loc = 0;
  1981.   dirp -> dd_contents = dirp -> dd_cp = NULL;
  1982.  
  1983.   if ((s = getdirent(nbuf)) == NULL)
  1984.     return dirp;
  1985.  
  1986.   do
  1987.   {
  1988.     if (((dp = malloc(sizeof(struct _dircontents))) == NULL) ||
  1989.         ((dp -> _d_entry = malloc(strlen(s) + 1)) == NULL)      )
  1990.     {
  1991.       if (dp)
  1992.         free(dp);
  1993.       free_dircontents(dirp -> dd_contents);
  1994.  
  1995.       return NULL;
  1996.     }
  1997.  
  1998.     if (dirp -> dd_contents)
  1999.     {
  2000.       dirp -> dd_cp -> _d_next = dp;
  2001.       dirp -> dd_cp = dirp -> dd_cp -> _d_next;
  2002.     }
  2003.     else
  2004.       dirp -> dd_contents = dirp -> dd_cp = dp;
  2005.  
  2006.     strcpy(dp -> _d_entry, s);
  2007.     dp -> _d_next = NULL;
  2008.  
  2009.     dp -> _d_size = find.cbFile;
  2010.     dp -> _d_mode = find.attrFile;
  2011.     dp -> _d_time = *(unsigned *) &(find.ftimeLastWrite);
  2012.     dp -> _d_date = *(unsigned *) &(find.fdateLastWrite);
  2013.   }
  2014.   while ((s = getdirent(NULL)) != NULL);
  2015.  
  2016.   dirp -> dd_cp = dirp -> dd_contents;
  2017.  
  2018.   return dirp;
  2019. }
  2020.  
  2021.  
  2022. void closedir(DIR * dirp)
  2023. {
  2024.   free_dircontents(dirp -> dd_contents);
  2025.   free(dirp);
  2026. }
  2027.  
  2028.  
  2029. struct dirent *readdir(DIR * dirp)
  2030. {
  2031.   static struct dirent dp;
  2032.  
  2033.   if (dirp -> dd_cp == NULL)
  2034.     return NULL;
  2035.  
  2036.   dp.d_namlen = dp.d_reclen =
  2037.     strlen(strcpy(dp.d_name, dirp -> dd_cp -> _d_entry));
  2038.  
  2039.   dp.d_ino = 1;
  2040.  
  2041.   dp.d_size = dirp -> dd_cp -> _d_size;
  2042.   dp.d_mode = dirp -> dd_cp -> _d_mode;
  2043.   dp.d_time = dirp -> dd_cp -> _d_time;
  2044.   dp.d_date = dirp -> dd_cp -> _d_date;
  2045.  
  2046.   dirp -> dd_cp = dirp -> dd_cp -> _d_next;
  2047.   dirp -> dd_loc++;
  2048.  
  2049.   return &dp;
  2050. }
  2051.  
  2052.  
  2053. void seekdir(DIR * dirp, long off)
  2054. {
  2055.   long i = off;
  2056.   struct _dircontents *dp;
  2057.  
  2058.   if (off >= 0)
  2059.   {
  2060.     for (dp = dirp -> dd_contents; --i >= 0 && dp; dp = dp -> _d_next);
  2061.  
  2062.     dirp -> dd_loc = off - (i + 1);
  2063.     dirp -> dd_cp = dp;
  2064.   }
  2065. }
  2066.  
  2067.  
  2068. long telldir(DIR * dirp)
  2069. {
  2070.   return dirp -> dd_loc;
  2071. }
  2072.  
  2073.  
  2074. static void free_dircontents(struct _dircontents * dp)
  2075. {
  2076.   struct _dircontents *odp;
  2077.  
  2078.   while (dp)
  2079.   {
  2080.     if (dp -> _d_entry)
  2081.       free(dp -> _d_entry);
  2082.  
  2083.     dp = (odp = dp) -> _d_next;
  2084.     free(odp);
  2085.   }
  2086. }
  2087.  
  2088.  
  2089. char *getdirent(char *dir)
  2090. {
  2091.   int done;
  2092.   static int lower = TRUE;
  2093.  
  2094.   if (dir != NULL)
  2095.   {                       /* get first entry */
  2096.     lower = IsFileSystemFAT(dir);
  2097.  
  2098.     hdir = HDIR_CREATE;
  2099.     count = 1;
  2100.     done = DosFindFirst(dir, &hdir, attributes, &find, sizeof(find), &count);
  2101.   }
  2102.   else                       /* get next entry */
  2103.     done = DosFindNext(hdir, &find, sizeof(find), &count);
  2104.  
  2105.   if (done == 0)
  2106.   {
  2107.     if ( lower )
  2108.       strlwr(find.achName);
  2109.     return find.achName;
  2110.   }
  2111.   else
  2112.   {
  2113.     DosFindClose(hdir);
  2114.     return NULL;
  2115.   }
  2116. }
  2117.  
  2118. #endif /* __EMX__ */
  2119.  
  2120. #ifdef __IBMC__
  2121.  
  2122. /* quick hack because IBM C lacks popen() and pclose() */
  2123.  
  2124. int pids[64];
  2125.  
  2126. FILE *
  2127. popen(char *cmd, char *mode) {
  2128.   HFILE end1, end2, std, old1, old2, temp;
  2129.   FILE *file;
  2130.   char fail[256], cmd_line[256], *cmd_exe, *args;
  2131.   RESULTCODES res;
  2132.   int rc;
  2133.  
  2134.   if (DosCreatePipe(&end1, &end2, 4096))
  2135.     return NULL;
  2136.  
  2137.   std = (*mode == 'w') ? 0 /* stdin */ : 1 /* stdout */;
  2138.   if (std == 0) {
  2139.     temp = end1; end1 = end2; end2 = temp;
  2140.   }
  2141.  
  2142.   old1 = -1; /* save stdin or stdout */
  2143.   DosDupHandle(std, &old1);
  2144.   DosSetFHState(old1, OPEN_FLAGS_NOINHERIT);
  2145.   temp = std; /* redirect stdin or stdout */
  2146.   DosDupHandle(end2, &temp);
  2147.  
  2148.   if ( std == 1 ) {
  2149.     old2 = -1; /* save stderr */
  2150.     DosDupHandle(2, &old2);
  2151.     DosSetFHState(old2, OPEN_FLAGS_NOINHERIT);
  2152.     temp = 2;   /* redirect stderr */
  2153.     DosDupHandle(end2, &temp);
  2154.   }
  2155.  
  2156.   DosClose(end2);
  2157.   DosSetFHState(end1, OPEN_FLAGS_NOINHERIT);
  2158.  
  2159.   if ( (cmd_exe = getenv("COMSPEC")) == NULL )
  2160.     cmd_exe = "cmd.exe";
  2161.  
  2162.   strcpy(cmd_line, cmd_exe);
  2163.   args = cmd_line + strlen(cmd_line) + 1; /* skip zero */
  2164.   strcpy(args, "/c ");
  2165.   strcat(args, cmd);
  2166.   args[strlen(args) + 1] = '\0'; /* two zeroes */
  2167.   rc = DosExecPgm(fail, sizeof(fail), EXEC_ASYNCRESULT, 
  2168.           cmd_line, 0, &res, cmd_exe);
  2169.  
  2170.   temp = std; /* restore stdin or stdout */
  2171.   DosDupHandle(old1, &temp);
  2172.   DosClose(old1);
  2173.  
  2174.   if ( std == 1 ) {
  2175.     temp = 2;   /* restore stderr */
  2176.     DosDupHandle(old2, &temp);
  2177.     DosClose(old2);
  2178.   }
  2179.  
  2180.   if (rc) {
  2181.     DosClose(end1);
  2182.     return NULL;
  2183.   }
  2184.   
  2185.   file = fdopen(end1, mode);
  2186.   pids[end1] = res.codeTerminate;
  2187.   return file;
  2188. }
  2189.  
  2190. int
  2191. pclose(FILE *pipe) {
  2192.   RESULTCODES rc;
  2193.   PID pid;
  2194.   int handle = fileno(pipe);
  2195.   fclose(pipe);
  2196.   if (pids[handle])
  2197.     DosWaitChild(DCWA_PROCESSTREE, DCWW_WAIT, &rc, &pid, pids[handle]);
  2198.   pids[handle] = 0;
  2199.   return rc.codeTerminate == 0 ? rc.codeResult : -1;
  2200. }
  2201. #endif
  2202.  
  2203. #ifdef CK_REDIR
  2204. int
  2205. ttruncmd(cmd) char *cmd; { /* Return: 0 = failure, 1 = success */
  2206.   HFILE old[3], temp;
  2207.   char fail[256], cmd_line[256], *cmd_exe, *args;
  2208.   RESULTCODES res;
  2209.   PID pid;
  2210.   int cnt, rc;
  2211.   
  2212.   if (ttyfd == -1) {
  2213.     printf("?Sorry, device is not open\n");
  2214.     return 0;
  2215.   }
  2216.   
  2217.   for (cnt = 0; cnt <= 2; cnt++) /* save stdin, stdout, stderr */
  2218.   {
  2219.     old[cnt] = -1; /* save old std* */
  2220.     DosDupHandle(cnt, &old[cnt]);
  2221.     DosSetFHState(old[cnt], OPEN_FLAGS_NOINHERIT);
  2222.     temp = cnt; /* redirect to line */
  2223.     DosDupHandle(ttyfd, &temp);
  2224.   }
  2225.  
  2226.   if ( (cmd_exe = getenv("COMSPEC")) == NULL )
  2227.     cmd_exe = "cmd.exe";
  2228.  
  2229.   strcpy(cmd_line, cmd_exe);
  2230.   args = cmd_line + strlen(cmd_line) + 1; /* skip zero */
  2231.   strcpy(args, "/c ");
  2232.   strcat(args, cmd);
  2233.   args[strlen(args) + 1] = '\0'; /* two zeroes */
  2234.  
  2235.   rc = DosExecPgm(fail, sizeof(fail), EXEC_ASYNCRESULT, 
  2236.           cmd_line, 0, &res, cmd_exe);
  2237.   pid = res.codeTerminate;
  2238.  
  2239.   for (cnt = 0; cnt <= 2; cnt++) /* restore stdin, stdout, stderr */
  2240.   {
  2241.     temp = cnt; /* restore std* */
  2242.     DosDupHandle(old[cnt], &temp);
  2243.     DosClose(old[cnt]);
  2244.   }
  2245.  
  2246.   if (rc) 
  2247.     return 0;
  2248.   
  2249.   rc = DosWaitChild(DCWA_PROCESSTREE, DCWW_WAIT, &res, &pid, pid);
  2250.  
  2251.   if (rc) {
  2252.     printf("?Command did not terminate: %d\r\n", rc);
  2253.     DosKillProcess(DKP_PROCESSTREE, pid);
  2254.     return 0;
  2255.   }
  2256.  
  2257.   if (res.codeTerminate != 0) {
  2258.     printf("?Command terminated abnormally: %d\r\n", res.codeTerminate);
  2259.     return 0;
  2260.   }
  2261.  
  2262.   if (res.codeResult != 0) {
  2263.     printf("?Command exit status: %d\r\n", res.codeResult);
  2264.     return 0;
  2265.   }
  2266.  
  2267.   return 1;
  2268. }
  2269. #endif /* CK_REDIR */
  2270.  
  2271. void
  2272. ChangeNameForFAT(char *name) {
  2273.   char *src, *dst, *next, *ptr, *dot, *start;
  2274.   static char invalid[] = ":;,=+\"[]<>| \t";
  2275.  
  2276.   if ( isalpha(name[0]) && (name[1] == ':') )
  2277.     start = name + 2;
  2278.   else
  2279.     start = name;
  2280.  
  2281.   src = dst = start;
  2282.   if ( (*src == '/') || (*src == '\\') )
  2283.     src++, dst++;
  2284.  
  2285.   while ( *src )
  2286.   {
  2287.     for ( next = src; *next && (*next != '/') && (*next != '\\'); next++ );
  2288.  
  2289.     for ( ptr = src, dot = NULL; ptr < next; ptr++ )
  2290.       if ( *ptr == '.' )
  2291.       {
  2292.         dot = ptr; /* remember last dot */
  2293.         *ptr = '_';
  2294.       }
  2295.  
  2296.     if ( dot == NULL )
  2297.       for ( ptr = src; ptr < next; ptr++ )
  2298.         if ( *ptr == '_' )
  2299.           dot = ptr; /* remember last _ as if it were a dot */
  2300.  
  2301.     if ( dot && (dot > src) &&
  2302.          ((next - dot <= 4) ||
  2303.           ((next - src > 8) && (dot - src > 3))) )
  2304.     {
  2305.       if ( dot )
  2306.         *dot = '.';
  2307.  
  2308.       for ( ptr = src; (ptr < dot) && ((ptr - src) < 8); ptr++ )
  2309.         *dst++ = *ptr;
  2310.  
  2311.       for ( ptr = dot; (ptr < next) && ((ptr - dot) < 4); ptr++ )
  2312.         *dst++ = *ptr;
  2313.     }
  2314.     else
  2315.     {
  2316.       if ( dot && (next - src == 1) )
  2317.         *dot = '.';           /* special case: "." as a path component */
  2318.  
  2319.       for ( ptr = src; (ptr < next) && ((ptr - src) < 8); ptr++ )
  2320.         *dst++ = *ptr;
  2321.     }
  2322.  
  2323.     *dst++ = *next; /* either '/' or 0 */
  2324.  
  2325.     if ( *next )
  2326.     {
  2327.       src = next + 1;
  2328.  
  2329.       if ( *src == 0 ) /* handle trailing '/' on dirs ! */
  2330.         *dst = 0;
  2331.     }
  2332.     else
  2333.       break;
  2334.   }
  2335.  
  2336.   for ( src = start; *src != 0; ++src )
  2337.     if ( strchr(invalid, *src) != NULL )
  2338.         *src = '_';
  2339. }
  2340.  
  2341.  
  2342. int IsFileNameValid(char *name)
  2343. {
  2344.   HFILE hf;
  2345.   U_INT uAction;
  2346.   int rc;
  2347.   switch( DosOpen(name, &hf, &uAction, 0, 0, FILE_OPEN,
  2348.                   OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE, 0) )
  2349.   {
  2350.   case ERROR_INVALID_NAME:
  2351.   case ERROR_FILENAME_EXCED_RANGE:
  2352.   case ERROR_INVALID_PARAMETER:
  2353.     return FALSE;
  2354.   case NO_ERROR:
  2355.     DosClose(hf);
  2356.   default:
  2357.     return TRUE;
  2358.   }
  2359. }
  2360.  
  2361. long
  2362. zdskspace(int drive) {
  2363.   FSALLOCATE fsa;
  2364.   if ( DosQueryFSInfo(drive, 1, (PBYTE) &fsa, sizeof(fsa)) )
  2365.     return 0;
  2366.   return fsa.cUnitAvail * fsa.cSectorUnit * fsa.cbSector;
  2367. }
  2368.  
  2369. char *
  2370. GetLoadPath(void) {
  2371. #ifdef __32BIT__
  2372.   PTIB pptib;
  2373.   PPIB pppib;
  2374.   char *szPath;
  2375.  
  2376.   DosGetInfoBlocks(&pptib, &pppib);
  2377.  
  2378.   szPath = pppib -> pib_pchenv;
  2379.  
  2380.   while (*szPath)
  2381.     szPath = strchr(szPath, 0) + 1;
  2382.  
  2383.   return szPath + 1;
  2384. #else
  2385.   extern char *_pgmptr;
  2386.   return _pgmptr;
  2387. #endif
  2388. }
  2389.  
  2390. /* 
  2391.   Keyboard                        Hardware ID 
  2392.   PC AT* Standard Keyboard        0001H 
  2393.   101 Key Enhanced Keyboard and   AB41H 
  2394.   102 Key Enhanced Keyboard
  2395.    88 Key Enhanced Keyboard and   AB54H 
  2396.    89 Key Enhanced Keyboard
  2397.   122 Key Mainframe Interactive   AB86H 
  2398.       (MFI) Keyboard 
  2399. */
  2400. static char os2kbt[20];
  2401. char *
  2402. conkbg(void) {
  2403.     HKBD hKbd;
  2404.     KBDHWID kbID;
  2405.     int rc;
  2406.     int x;
  2407.     char * p;
  2408.  
  2409.     p = os2kbt;
  2410.     *p = '\0';
  2411.  
  2412.     if ( KbdOpen(&hKbd) == 0) {
  2413.     KbdGetFocus(IO_WAIT, hKbd);
  2414.     kbID.cb = sizeof(kbID);
  2415.     KbdGetHWID(&kbID, hKbd);
  2416.     KbdClose(hKbd);
  2417.     debug(F101,"conkbg","",kbID.idKbd);
  2418.     switch (kbID.idKbd) {
  2419.       case 0x0001:            /* PC/AT keyboard */
  2420.       case 0xab54:            /* PC or PC/XT 88 or 89 key */
  2421.         x = 88;
  2422.         break;
  2423.       case 0xab41:            /* 101 or 102 enhanced keyboard */
  2424.       case 0xab83:
  2425.         x = 101;
  2426.         break;
  2427.       case 0xab86:            /* 122-key "mainframe interactive" */
  2428.         x = 122;
  2429.         break;
  2430.       default:
  2431.         x = 0; break;    /* Something else... */
  2432.     }
  2433.     if (x)                /* If it's known model */
  2434.       sprintf(p,"%d",x);        /* use its "name" */
  2435.     else                /* otherwise */
  2436.       sprintf(p,"%04X",(int) kbID.idKbd); /* use the hex code */
  2437.     }
  2438.     return(p);                /* Return string pointer */
  2439. };
  2440.  
  2441.  
  2442. #ifdef CK_LABELED
  2443. static CHAR os2version[50] ;
  2444.  
  2445. char *
  2446. get_os2_vers() {
  2447.     ULONG StartIndex = 11 ; /* Major Version Number */
  2448.     ULONG EndIndex   = 13 ; /* Revision Letter      */
  2449.     ULONG DataBuf[3] ;
  2450.     ULONG DataBufLen = 3 * sizeof(ULONG) ;
  2451.     APIRET rc ;
  2452.  
  2453.     rc = DosQuerySysInfo( StartIndex, 
  2454.                           EndIndex,
  2455.                           DataBuf,
  2456.                           DataBufLen );
  2457.  
  2458.     if (rc)
  2459.       os2version[0] = '\0' ;
  2460.     else
  2461.       sprintf(os2version,"%02d.%02d%c",DataBuf[0],DataBuf[1],DataBuf[2]) ;
  2462.     return os2version ;
  2463. }
  2464. #endif /* CK_LABELED */
  2465.  
  2466. #ifdef CK_REXX
  2467. RexxFunctionHandler os2rexxckcmd ;
  2468. RexxSubcomHandler   os2rexxsubcom ;
  2469. extern char * mrval[] ;
  2470. extern int maclvl ;
  2471.  
  2472. /* This is the CkCommand/CKermit function handler.  It is an undocumented  */
  2473. /* C-Kermit feature.  Do not remove this code.                             */
  2474.  
  2475. ULONG 
  2476. os2rexxckcmd( 
  2477.     PUCHAR Name,
  2478.     ULONG Argc,
  2479.     PRXSTRING Argv,
  2480.     PSZ Queuename,
  2481.     PRXSTRING Retstr) {
  2482.     APIRET rc = 0 ;
  2483.     int i ;
  2484.   
  2485.     for ( i = 0 ; i < Argc ; i++ ) {
  2486.     rc = domac("_rexx_commands",RXSTRPTR(Argv[i]));
  2487.     debug(F111,"os2rexxckcmd",RXSTRPTR(Argv[i]),rc); 
  2488.     delmac("_rexx_commands");
  2489.     if (rc < 0) break ;
  2490.     }
  2491.     if ( mrval[maclvl+1] ) {
  2492.        MAKERXSTRING(*Retstr,
  2493.             strdup(mrval[maclvl+1]),
  2494.             strlen(mrval[maclvl+1])
  2495.             ) ;
  2496.     } else
  2497.        MAKERXSTRING( *Retstr, strdup(""), 0 ) ;
  2498.  
  2499.     return (rc < 0 ? rc : 0 ) ;
  2500. }
  2501.  
  2502. ULONG
  2503. os2rexxsubcom(
  2504.     PRXSTRING Command,
  2505.     PUSHORT pFlags,
  2506.     PRXSTRING Retstr)
  2507. {
  2508.    APIRET rc = 0 ;
  2509.  
  2510.    rc = domac("_rexx_commands",RXSTRPTR(Command[0]));
  2511.    debug(F111,"os2rexxsubcom",RXSTRPTR(Command[0]),rc);
  2512.    delmac("_rexx_commands");
  2513.  
  2514.    *pFlags = rc < 0 ? RXSUBCOM_ERROR : RXSUBCOM_OK;
  2515.  
  2516.    if ( mrval[maclvl+1] ) {
  2517.       MAKERXSTRING(*Retstr,
  2518.       strdup(mrval[maclvl+1]),
  2519.       strlen(mrval[maclvl+1])) ;
  2520.    } else
  2521.       MAKERXSTRING( *Retstr, strdup(""), 0 ) ;
  2522.    return 0;
  2523. }
  2524.          
  2525. int
  2526. os2rexx( char * rexxcmd, char * rexxbuf, int rexxbuflen ) {
  2527.     long return_code  ;  /* rexx interpreter return code */
  2528.     short     rc      ;  /* converted return code   */
  2529.     char      return_buffer[256] ; /*returned buffer*/
  2530.     RXSTRING  Instore[2] ; /* Instorage rexx procedure */
  2531.     RXSTRING  retstr  ;  /* program return value    */
  2532.     int       retval  ;  /* os2rexx return value    */
  2533.  
  2534.     MAKERXSTRING( Instore[0], rexxcmd, strlen(rexxcmd) ) ;
  2535.     MAKERXSTRING( Instore[1], 0, 0 ) ;
  2536.     MAKERXSTRING( retstr, return_buffer, sizeof(return_buffer) ) ;
  2537.  
  2538.     debug(F110,"os2rexx: procedure",rexxcmd,0);
  2539.     return_code = RexxStart( 0,   /* no program arguments */
  2540.                              0,   /* null argument list   */
  2541.                             "C-Kermit for OS/2 REXX Command",
  2542.                                                /* default program name */
  2543.                             Instore, /* rexx procedure to interpret */
  2544.                             "CKermit",         /* default address name */
  2545.                             RXFUNCTION,         /* calling as a function */
  2546.                             0,                  /* no exits used */
  2547.                             &rc,                /* converted return code */
  2548.                             &retstr);           /* returned result */
  2549.  
  2550.     debug(F111,"os2rexx: returns",RXSTRPTR(retstr),return_code);
  2551.     if ( !return_code && RXSTRLEN( retstr ) < rexxbuflen ) {
  2552.     strncpy( rexxbuf, RXSTRPTR(retstr), RXSTRLEN( retstr ) );
  2553.     rexxbuf[ RXSTRLEN( retstr ) ] = '\0' ;
  2554.     retval = 0 ;            /* Success */
  2555.     } else {
  2556.     rexxbuf[0] = '\0' ;
  2557.     retval = 1 ;            /* Failure */
  2558.     }
  2559.     if (RXSTRPTR(retstr) != return_buffer)
  2560.       DosFreeMem(RXSTRPTR(retstr));
  2561.  
  2562.     return retval ;
  2563. }
  2564. int
  2565. os2rexxinit()
  2566. {
  2567.    /* this next line installs the CkCommand statement into the Rexx    */
  2568.    /* interpretter environment.  We have replaced it with a subcommand */
  2569.    /* handler instead.  Both mechanisms can co-exist, so we leave in   */
  2570.    /* the CkCommand/CKermit as an undocumented function.               */
  2571.  
  2572.    RexxRegisterFunctionExe("CKermit",os2rexxckcmd) ; 
  2573.    RexxRegisterFunctionExe("CKCommand",os2rexxckcmd) ; 
  2574.    RexxRegisterSubcomExe("CKermit", (PFN)os2rexxsubcom, NULL);
  2575. }
  2576.  
  2577. #endif /* CK_REXX */
  2578.  
  2579. int
  2580. os2settitle(char *title) {
  2581.     HSWITCH hSwitch;
  2582.     SWCNTRL swctl;
  2583.   
  2584.     /* This changes the text in the task list. */
  2585.     /* That the window handle (first parameter) in the WinQuerySwitchHandle */
  2586.     /* call can be NULL is fully documented in the API description. */
  2587.  
  2588.     hSwitch = WinQuerySwitchHandle((HWND) NULL, pid);
  2589.     WinQuerySwitchEntry(hSwitch, &swctl);
  2590.     strcpy(swctl.szSwtitle, title);
  2591.     WinChangeSwitchEntry(hSwitch, &swctl);
  2592.     zsyscmd( "" ) ;
  2593. #ifdef CK_SETTITLE
  2594.     {
  2595.     /* and this undocumented call changes the session title */
  2596. #ifdef __32BIT__
  2597.     extern _Far16 _Pascal DosSmSetTitle(char * _Seg16 szTitle);
  2598. #else
  2599.     extern _far _pascal DosSmSetTitle(char _far *szTitle);
  2600. #endif /* __32BIT__ */
  2601.     DosSmSetTitle(title);
  2602.     }
  2603. #endif /* CK_SETTITLE */
  2604.     return 0;
  2605. }
  2606.  
  2607. int
  2608. os2gettitle(char *buffer, int size) {
  2609.     HSWITCH hSwitch;
  2610.     SWCNTRL swctl;
  2611.  
  2612.     /* Query the text in the task list */
  2613.  
  2614.     hSwitch = WinQuerySwitchHandle( (HWND) NULL, pid);
  2615.     WinQuerySwitchEntry(hSwitch, &swctl);
  2616.     strncpy(buffer, swctl.szSwtitle, size);
  2617.     buffer[size - 1] = NUL;        /* In case we truncate the title */
  2618.  
  2619. #ifdef COMMENT
  2620.     /* Only documented for PM applications */
  2621.     WinQuerySessionTitle(NULL, 0, buffer, size);
  2622. #endif /* COMMENT */
  2623.     return 0;
  2624. }
  2625.  
  2626. #ifdef __32BIT__ 
  2627.  
  2628. /* Begin Keyboard Buffer Code
  2629.    This is a simple implementation of a circular queue with access
  2630.    protected by a Mutual Exclusion Semaphore
  2631. */
  2632.  
  2633. #define KEY_BUF_SIZE 16384
  2634. int Keystroke[KEY_BUF_SIZE] ;
  2635. int start=0, end=0 ;
  2636. HMUX hmuxKeyStroke = (HMUX) 0 ;
  2637. HMUX hmuxKeyboard  = (HMUX) 0 ;
  2638.  
  2639. void
  2640. keybufinit( void ) {
  2641.    int i ;
  2642.  
  2643.    DosCreateEventSem( NULL, &hevKeyAvail, 0, 0 ) ;
  2644.    DosCreateMutexSem( NULL, &hmuxKeyStroke, 0, 1 ) ;
  2645.    DosCreateMutexSem( NULL, &hmuxKeyboard, 0, 0 ) ;
  2646.    for ( i = 0 ; i < KEY_BUF_SIZE ; i++ )
  2647.      Keystroke[i] = 0 ;
  2648.    start = 0 ;
  2649.    end = 0 ;
  2650.    DosReleaseMutexSem( hmuxKeyStroke ) ;
  2651. }
  2652.  
  2653. void
  2654. keybufcleanup( void ) {
  2655.     DosCloseMutexSem( hmuxKeyStroke ) ;
  2656.     DosCloseMutexSem( hmuxKeyboard ) ;
  2657.     DosCloseEventSem( hevKeyAvail ) ;
  2658. }
  2659.  
  2660. int
  2661. keyinbuf( void ) {
  2662.     int rc = 0 ;
  2663.  
  2664.     DosRequestMutexSem( hmuxKeyStroke, SEM_INDEFINITE_WAIT ) ;
  2665.     rc = start != end ;
  2666.     DosReleaseMutexSem( hmuxKeyStroke ) ;
  2667.  
  2668.     return rc ;
  2669. }
  2670.  
  2671. int
  2672. putkey( int k ) {
  2673.     int rc = 0 ;
  2674.  
  2675.     DosRequestMutexSem( hmuxKeyStroke, SEM_INDEFINITE_WAIT ) ;
  2676.     if ( (start - end == 1) || ( start == 0 && end == KEY_BUF_SIZE - 1 ) )
  2677.       rc = -1 ;   /* Buffer is full */
  2678.     else {
  2679.     Keystroke[end++] = k ;
  2680.     if ( end == KEY_BUF_SIZE )
  2681.       end = 0 ;
  2682.     DosPostEventSem( hevKeyAvail ) ;
  2683.     }
  2684.     DosReleaseMutexSem( hmuxKeyStroke ) ;
  2685.  
  2686.     return rc ;
  2687. }
  2688.  
  2689. int
  2690. getkey( int * k ) {
  2691.     int rc = 0 ;
  2692.     ULONG PostCount ;
  2693.  
  2694.     DosRequestMutexSem( hmuxKeyStroke, SEM_INDEFINITE_WAIT ) ;
  2695.     if ( start != end ) {
  2696.     *k = Keystroke[start] ;
  2697.     Keystroke[start] = 0 ;
  2698.     start++ ;
  2699.  
  2700.     if ( start == KEY_BUF_SIZE )
  2701.       start = 0 ;
  2702.  
  2703.     if ( start == end )
  2704.       DosResetEventSem( hevKeyAvail, &PostCount ) ;
  2705.     rc++ ;
  2706.     }
  2707.     DosReleaseMutexSem( hmuxKeyStroke ) ;
  2708.  
  2709.     return rc ;
  2710.    }
  2711.  
  2712. /* Begin Keyboard Handler Thread Code */
  2713.  
  2714. int
  2715. KbdHandlerInit( void ) {
  2716.     KbdHandlerThreadID = _beginthread( &KbdHandlerThread, 0, THRDSTKSIZ, 0 ) ;
  2717.     if ( KbdHandlerThreadID == -1 ) {
  2718.     printf( "Sorry, can't create KbdHandlerThread\n" ) ;
  2719.     return -1 ;
  2720.     }
  2721.     return 0 ;
  2722. }
  2723.  
  2724. int
  2725. KbdHandlerCleanup( void ) {
  2726.     return 0 ;
  2727. }
  2728.  
  2729. void
  2730. KbdHandlerThread( void * ArgList ) {
  2731.     KBDKEYINFO k ;
  2732.     int rc, c ;
  2733.  
  2734.     for (;;) {
  2735.     rc = -1 ;
  2736.     while (1){
  2737.         memset( &k, 0, sizeof(k) ) ;
  2738.        DosRequestMutexSem( hmuxKeyboard, SEM_INDEFINITE_WAIT ) ;
  2739.         KbdCharIn(&k, IO_NOWAIT, 0 ) ;
  2740.        DosReleaseMutexSem( hmuxKeyboard ) ;
  2741.         if (k.fbStatus & (3 << 6))
  2742.          /* && !(k.fbStatus & 1) --- blocks ScrollLock */
  2743.           break ;
  2744.         DosSleep( 80 ) ; /* a human can't type faster than this */
  2745.     }
  2746.  
  2747.     if ( k.chChar || k.chScan ) {
  2748.         c = k.chChar;
  2749.  
  2750.         if (c == 0x00)
  2751.           c = 0x100 | k.chScan;
  2752.         if (c == 0xE0)
  2753.           c = 0x200 | k.chScan;
  2754.  
  2755.         switch (c) {    /* Handle ambiguous keypad and space keys */
  2756.           case '\t':
  2757.         rc = k.chScan == 0x0F ? 0x100 | c : c;
  2758.         break;
  2759.           case '\b':
  2760.         rc = k.chScan == 0x0E ? DEL : c;
  2761.         break;
  2762.           case ESC:
  2763.         rc = ((k.fsState & LEFTSHIFT) || (k.fsState & RIGHTSHIFT)
  2764.               ? 0x100 | c : c);
  2765.         break;
  2766.           case DEL:
  2767.         rc = 0x200 | c;
  2768.                break;
  2769.           case ' ':
  2770.         rc = (k.fsState & CONTROL) ? 0x200 | c : c;
  2771.         break;
  2772.           case '+':
  2773.         rc = k.chScan == 0x4E ? 0x200 | c : c;
  2774.         break;
  2775.           case '-':
  2776.         rc = k.chScan == 0x4A ? 0x200 | c : c;
  2777.         break;
  2778.           case '*':
  2779.         rc = k.chScan == 0x37 ? 0x200 | c : c;
  2780.                break;
  2781.           case '/':
  2782.         rc = k.chScan == 0xE0 ? 0x200 | c : c;
  2783.         break;
  2784.           case '\r':
  2785.           case '\n':
  2786.         rc = k.chScan == 0xE0 ? 0x200 | c : c;
  2787.         break;
  2788.           case '.':
  2789.           case ',':
  2790.         rc = k.chScan == 0x53 ? 0x200 | c : c;
  2791.         break;
  2792.           case '0':
  2793.           case '1':
  2794.           case '2':
  2795.           case '3':
  2796.           case '4':
  2797.           case '5':
  2798.           case '6':
  2799.           case '7':
  2800.           case '8':
  2801.           case '9':
  2802.         rc = k.chScan >= 0x47 ? 0x200 | c : c;
  2803.         break;
  2804.           default:
  2805.         rc = c;
  2806.         }
  2807.     } else if ( (k.fbStatus & SHIFT_KEY_IN) && (k.fsState & NUMLOCK) )
  2808.       rc = (k.fsState & NUMLOCK_ON) ? 0x2FE : 0x1FE;
  2809.  
  2810.     else if ( (k.fbStatus & SHIFT_KEY_IN) && (k.fsState & SCROLLLOCK) )
  2811.       rc = (k.fsState & SCROLLLOCK_ON) ? 0x2FF : 0x1FF;
  2812.  
  2813.     if ( rc >= 0 )
  2814.       putkey( rc ) ;
  2815.     }
  2816.  
  2817.     _endthread();  /* we never get here */
  2818. }
  2819.  
  2820. #endif /* __32BIT__ */
  2821.  
  2822. #ifdef OS2PM
  2823. #define CKPM_PIPE_NAME        "\\PIPE\\CKPM"
  2824. #define MAX_PIPE_NAME_LEN        80
  2825. #define DEFAULT_MAKE_MODE        NP_ACCESS_DUPLEX
  2826. #define DEFAULT_PIPE_MODE        NP_WMESG | NP_RMESG | 0x01
  2827. #define DEFAULT_OPEN_FLAG        OPEN_ACTION_OPEN_IF_EXISTS
  2828. #define DEFAULT_OPEN_MODE        OPEN_FLAGS_WRITE_THROUGH | \
  2829.                                  OPEN_FLAGS_FAIL_ON_ERROR | \
  2830.                                  OPEN_FLAGS_RANDOM |        \
  2831.                                  OPEN_SHARE_DENYNONE |      \
  2832.                                  OPEN_ACCESS_READWRITE
  2833. #define DEFAULT_OUTB_SIZE        0x1000
  2834. #define DEFAULT_INPB_SIZE        0x1000
  2835. #define DEFAULT_TIME_OUTV        20000L
  2836. #define TOKEN_F2_SWITCH          0x0000003CL
  2837. #define TOKEN_F3_DISCON          0x0000003DL
  2838. #define RETURN_CHAR              0x0D
  2839. #define LINE_FEED_CHAR           0x0A
  2840. #define FUNC_KEYS_CHAR           0x00
  2841. #define EXTD_KEYS_CHAR           0xE0
  2842. #define HAND_SHAKE_LEN           0x08
  2843. #define HAND_SHAKE_INP           "CKermit VIO"
  2844. #define HAND_SHAKE_OUT           "CKermit PM"
  2845. #define HAND_SHAKE_ERROR         -1
  2846. #define PROGRAM_ERROR            999
  2847.  
  2848. CHAR    achPipeName [MAX_PIPE_NAME_LEN] ;
  2849. HPIPE   hpRdPipe ;
  2850. CHAR    chToken ;
  2851.  
  2852. APIRET
  2853. ConnectToPM( void ) {
  2854.     CHAR   achInitBuf [HAND_SHAKE_LEN + 1] ;
  2855.     ULONG  ulOpenFlag ;
  2856.     ULONG  ulOpenMode ;
  2857.     ULONG  ulActionTaken ;
  2858.     APIRET arReturn ;
  2859.     ULONG  ulBytesDone ;
  2860.  
  2861.     memset ( achInitBuf, 0, sizeof ( achInitBuf )) ;
  2862.  
  2863.     ulOpenFlag = DEFAULT_OPEN_FLAG ;
  2864.     ulOpenMode = DEFAULT_OPEN_MODE ;
  2865.  
  2866.     arReturn = DosOpen ( CKPM_PIPE_NAME,
  2867.                         &hpRdPipe,
  2868.                         &ulActionTaken,
  2869.                         0,
  2870.                         0,
  2871.                         ulOpenFlag,
  2872.                         ulOpenMode,
  2873.                         0 ) ;
  2874.  
  2875.     if ( arReturn ) {
  2876.     printf ( "\n  The Pipe Open / Connection API "
  2877.         "returned rc = %02x\n",
  2878.         arReturn ) ;
  2879.     printf ( "\n  Make sure CKermit PM is running.\n\n" ) ;
  2880.     } /* endif */
  2881.  
  2882.     return arReturn ;
  2883. }
  2884.  
  2885. APIRET
  2886. ReadFromPM( void ) {
  2887.    APIRET arReturn ;
  2888.    CHAR   chr ;
  2889.    ULONG  ulBytesDone ;
  2890.    int i ;
  2891.             
  2892.    arReturn = DosRead ( hpRdPipe, &chr, 1L, &ulBytesDone ) ;
  2893.                
  2894.    if ( !arReturn ) {
  2895.       return chr ;
  2896.       }
  2897.    return -1 ;
  2898.    }
  2899.  
  2900. #endif /* OS2PM */
  2901.