char *ckxv = "OS/2 console i/o, 8 Feb 92";
char *ckxsys = "OS/2";

/* C K O T I O  --  Kermit console i/o support for OS/2 systems */

/* Also contains code to emulate the Unix 'alarm' function under OS/2
   and a set of opendir/readdir/closedir etc. functions. */

/*
 Author: Chris Adie (C.Adie@uk.ac.edinburgh)
 Copyright (C) 1988 Edinburgh University Computing Service
 Permission is granted to any individual or institution to use, copy, or
 redistribute this software so long as it is not sold for profit, provided this
 copyright notice is retained.

 Many changes for 5A by Kai Uwe Rommel (rommel@informatik.tu-muenchen.de)
*/

/* Includes */

#include "ckcdeb.h"			/* Typedefs, debug formats, etc */
#include "ckcker.h"			/* Kermit definitions */
#include <ctype.h>			/* Character types */
#include <stdio.h>			/* Standard i/o */
#include <io.h>				/* File io function declarations */
#include <process.h>			/* Process-control function declarations */
#include <string.h>			/* String manipulation declarations */
#include <stdlib.h>			/* Standard library declarations */
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>			/* Time functions */
#include <direct.h>			/* Directory function declarations */
#include <signal.h>
#include <assert.h>

#include "ckodir.h"

#define far _far
#define near _near
#define pascal _pascal
#define	INCL_NOPM
#define	INCL_ERRORS
#define	INCL_KBD
#define	INCL_DOSPROCESS
#define	INCL_DOSSIGNALS
#define	INCL_DOSDEVICES
#include <os2.h>	/* This pulls in a whole load of stuff */
#undef COMMENT


/*============================================================================*/

/*
 Variables available to outside world:

   dftty  -- Pointer to default tty name string, like "/dev/tty".
   dfloc  -- 0 if dftty is console, 1 if external line.
   dfprty -- Default parity
   dfflow -- Default flow control
   ckxech -- Flag for who echoes console typein:
     1 - The program (system echo is turned off)
     0 - The system (or front end, or terminal).
   functions that want to do their own echoing should check this flag
   before doing so.

 Functions for assigned communication line (either external or console tty):

   sysinit()               -- System dependent program initialization
   syscleanup()            -- System dependent program shutdown
   ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access.
   ttclos()                -- Close & reset the tty, releasing any access lock.
   ttpkt(speed,flow,parity)-- Put the tty in packet mode
				or in DIALING or CONNECT modem control state.
   ttvt(speed,flow)        -- Put the tty in virtual terminal mode.
   ttinl(dest,max,timo)    -- Timed read line from the tty.
   ttinc(timo)             -- Timed read character from tty.
   ttchk()                 -- See how many characters in tty input buffer.
   ttxin(n,buf)            -- Read n characters from tty (untimed).
   ttol(string,length)     -- Write a string to the tty.
   ttoc(c)                 -- Write a character to the tty.
   ttflui()                -- Flush tty input buffer.
   ttgspd()                -- Speed of tty line.

Functions for console terminal:

   conraw()  -- Set console into Raw mode
   concooked() -- Set console into Cooked mode
   conoc(c)  -- Unbuffered output, one character to console.
   conol(s)  -- Unbuffered output, null-terminated string to the console.
   conola(s) -- Unbuffered output, array of strings to the console.
   conxo(n,s) -- Unbuffered output, n characters to the console.
   conchk()  -- Check if characters available at console (bsd 4.2).
		Check if escape char (^\) typed at console (System III/V).
   coninc(timo)  -- Timed get a character from the console.
 Following routines are dummies:
   congm()   -- Get console terminal mode.
   concb()   -- Put console into single char mode with no echo.
   conres()  -- Restore console to congm mode.
   conint()  -- Enable console terminal interrupts.
   connoi()  -- No console interrupts.

Time functions

   sleep(t)  -- Like UNIX sleep
   msleep(m) -- Millisecond sleep
   ztime(&s) -- Return pointer to date/time string
   rtimer() --  Reset timer
   gtimer()  -- Get elapsed time since last call to rtimer()
*/


/* Defines */

#define DEVNAMLEN	14

/* Declarations */

/* dftty is the device name of the default device for file transfer */
/* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */

extern int parity;

    char *dftty = "com1";
    int dfloc = 1;

    int dfprty = 0;			/* Default parity (0 = none) */
    int ttprty = 0;			/* Parity in use. */
    int ttmdm = 0;			/* Modem in use. */
    int dfflow = 1;			/* Xon/Xoff flow control */
    int backgrd = 0;			/* Assume in foreground (no '&' ) */
    int ttcarr = CAR_AUT;		/* Carrier handling mode. */

int ckxech = 1; /* 0 if system normally echoes console characters, else 1 */

/* Declarations of variables global within this module */

static struct rdchbuf_rec {		/* Buffer for serial characters */
	unsigned char buffer[256];
	USHORT length, index;
} rdchbuf;

static long tcount;			/* Elapsed time counter */

static HFILE ttyfd = -1;		/* TTY file descriptor */

DCBINFO ttydcb;

static int conmode, consaved;


void cc_trap()
{
  signal(SIGINT, cc_trap);
}


/*  S Y S I N I T  --  System-dependent program initialization.  */

sysinit() {
    signal(SIGINT, cc_trap);
    return(0);
}


/*  S Y S C L E A N U P  --  System-dependent program cleanup.  */

syscleanup() {
    signal(SIGINT, SIG_DFL);
    return(0);
}


/*  O S 2 S E T F L O W -- set flow state of tty */

/*  If flow = 0 turn receive flow XON/XOFF control off, otherwise turn it on.
    If successful, return 0, otherwise return -1.  */

os2setflow(int flow)
{
    /* Get the current settings */
    if (DosDevIOCtl(&ttydcb,NULL,ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd))
        return(-1);

    ttydcb.fbFlowReplace &= ~MODE_AUTO_RECEIVE;       /* Set RX flow off */
    if (flow != 0)
        ttydcb.fbFlowReplace |= MODE_AUTO_RECEIVE;    /* Set RX flow on */

    /* Set DCB */
    if (DosDevIOCtl(NULL,&ttydcb,ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
        return(-1);
    return(0);
}


/*  O S 2 S E T D T R -- set state of DTR signal */

os2setdtr(int on)
{
    MODEMSTATUS ms;
    USHORT data;

    if (ttyfd == -1) return(0);
    ms.fbModemOn = on ? DTR_ON : 0;
    ms.fbModemOff = on ? 255 : DTR_OFF;
    return(DosDevIOCtl(&data,&ms,ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd));
}


/*  T T S E T T I N G S  --  Set the device driver parity and stop bits */

ttsettings(int par, int stop) {
    LINECONTROL lc;

    if (DosDevIOCtl(&lc,NULL,ASYNC_GETLINECTRL,IOCTL_ASYNC,ttyfd))
      return(-1); /* Get line */

    switch (par) {
	case 'o':
	    lc.bDataBits = 7;	/* Data bits */
	    lc.bParity   = 1;
	    break;
    	case 'e':
	    lc.bDataBits = 7;	/* Data bits */
	    lc.bParity   = 2;
	    break;
	case 'm':
	    lc.bDataBits = 7;	/* Data bits */
	    lc.bParity   = 3;
	    break;
	case 's':
	    lc.bDataBits = 7;	/* Data bits */
	    lc.bParity   = 4;
	    break;
	default :
	    lc.bDataBits = 8;	/* Data bits */
	    lc.bParity   = 0;	/* No parity */
    }
    switch (stop) {
    	case 2:
    	    lc.bStopBits = 2;	/* Two stop bits */
    	    break;
    	case 1:
    	    lc.bStopBits = 0;	/* One stop bit */
    	    break;
    	default: 		/* No change */
    	    break;
    }
    if (DosDevIOCtl(NULL,&lc,ASYNC_SETLINECTRL,IOCTL_ASYNC,ttyfd))
      return(-1); /* Set line */
    return(0);
}

/*  T T O P E N  --  Open a tty for exclusive access.  */

/*  Returns 0 on success, -1 on failure.  */
/*
  If called with lcl < 0, sets value of lcl as follows:
  0: the terminal named by ttname is the job's controlling terminal.
  1: the terminal named by ttname is not the job's controlling terminal.
  But watch out: if a line is already open, or if requested line can't
  be opened, then lcl remains (and is returned as) -1.
*/
ttopen(char *ttname, int *lcl, int modem, int spare)
{
    char *x; extern char* ttyname();
    char cname[DEVNAMLEN+4];
    USHORT action, res;

    if (ttyfd != -1) return(0);		/* If already open, ignore this call */
    if (*lcl == 0) return(-1);		/* Won't open in local mode */

    ttmdm = modem;			/* Make this available to other fns */
    if (res = DosOpen(ttname,&ttyfd,&action,0L,0,FILE_OPEN,
                      OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE |
                      OPEN_FLAGS_FAIL_ON_ERROR ,0L)) {
	ttyfd = -1;
	return((res == ERROR_SHARING_VIOLATION) ? -5 : -1);
    }

    rdchbuf.length = rdchbuf.index = 0;
    debug(F111,"ttopen ok",ttname,*lcl);

/* Caller wants us to figure out if line is controlling tty */
    if (*lcl == -1) {
	*lcl = 1;			/* Can never transfer with console */
    }

    /* Read DCB */
    if (DosDevIOCtl(&ttydcb,NULL,ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd)) {
        /* Not a serial port */
        ttyfd = -1;
    	return(-4);
    }
    ttydcb.fbTimeout &= ~MODE_NOWAIT_READ_TIMEOUT;
    ttydcb.fbTimeout |= MODE_WAIT_READ_TIMEOUT;
    /* Read "some" data from line mode */
    DosDevIOCtl(NULL,&ttydcb,ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd);

    ttprty = dfprty;		/* Make parity the default parity */
    if (ttsettings(ttprty,0)) return(-1);
    return(ttflui());
}

/*  T T I S C O M  --  Is the given handle an open COM port? */

ttiscom(int f) {
    ttclos(0);
    ttyfd = f;
    /* Read DCB */
    if (DosDevIOCtl(&ttydcb,NULL,ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd)) {
        /* Not a serial port */
        ttyfd = -1;
    	return( 0 /* bad */ );
    }
    return( 1 /* good */ );
}

/*  T T C L O S  --  Close the TTY.  */

ttclos(int spare)
{
    if (ttyfd == -1) return(0);		/* Wasn't open. */
    DosClose(ttyfd);
    ttyfd = -1;
    return(0);
}

/*  T T G S P D  --  return speed of tt line, or of default line */

long ttgspd() {
/*  char df;  */
    long sp=0;

    if (ttyfd == -1) /* if (ttopen(dftty,&sp,0,0)) */ return(-1);
    if (DosDevIOCtl(&sp,NULL,ASYNC_GETBAUDRATE,IOCTL_ASYNC,ttyfd)) sp = -1;
    return(sp);
}

ttsetspd(long sp)
{
    if (ttyfd == -1) return(-1);	/* Not open */
    return DosDevIOCtl(NULL,&sp,ASYNC_SETBAUDRATE,IOCTL_ASYNC,ttyfd);
}



/*  T T H A N G -- Hangup phone line */

tthang() {
    return os2setdtr(0) ? -1 : 1;
}


/*  T T R E S  --  Restore terminal to "normal" mode.  */

ttres() {				/* Restore the tty to normal. */
    if (ttyfd == -1) return(-1);	/* Not open */
    return(0);
}

#define DIALING 4		/* for ttpkt parameter */
#define CONNECT 5

/*  T T P K T  --  Condition the communication line for packets. */
/*		or for modem dialing */

/*  If called with speed > -1, also set the speed.  */
/*  Returns 0 on success, -1 on failure.  */

ttpkt(long speed, int flow, int parity)
{
    int s;

    if (ttyfd < 0) return(-1);		/* Not open. */
    if (speed < 0) return(-1);

    os2setdtr(1);
    if (ttsetspd(speed)) return(-1);

    ttprty = parity;
    if (ttsettings(ttprty,0)) return(-1);
    debug(F101,"ttpkt setting ttprty","",ttprty);

    if (flow == DIALING) {		/* Ignore DCD */
	debug(F100,"ttpkt DIALING","",0);
	/* Driver default is to do so */
    } else
    if (flow == CONNECT) {		/* Pay attention to DCD */
	debug(F100,"ttpkt CONNECT","",0);
	/* We're warned against doing so, for some reason, so no action */
    } else
    return(os2setflow(flow));
}


/*  T T V T -- Condition communication line for use as virtual terminal  */

ttvt(long speed, int flow)
{

    if (ttyfd < 0) return(-1);		/* Not open. */
    if (speed < 0) return(-1);

    os2setdtr(1);

    if (ttsetspd(speed)) return(-1);
    ttprty = parity;
    if (ttsettings(ttprty,0)) return(-1);

    return(os2setflow(flow));
}


/*  T T S S P D  --  Return the speed if OK, otherwise -1 */

ttsspd(int speed)
{
    long s;

    if (speed < 0) return(-1);
	switch (speed) {
	    case 11:
	    case 15:
	    case 30:
	    case 60:
	    case 120:
	    case 180:
	    case 240:
	    case 360:
	    case 480:
	    case 720:
	    case 960:
	    case 1440:
	    case 1920:
	    case 3840:
	    case 5760:
	        s = (long) speed * 10;
		ttsetspd(s);
                break;
	    default:
	    	return(-1);
		break;
	}
	return(0);
 }


/*  T T F L U I  --  Flush tty input buffer */

ttflui() {
    char parm=0;
    long int data;
    int i;

    rdchbuf.index = rdchbuf.length = 0;		/* Flush internal buffer */
    DosDevIOCtl(&data,&parm,DEV_FLUSHINPUT,IOCTL_GENERAL,ttyfd); /* Flush driver */
    return(0);
}


/*  T T C H K  --  Tell how many characters are waiting in tty input buffer  */

ttchk() {
    USHORT data[2];
    if(DosDevIOCtl(data,NULL,ASYNC_GETINQUECOUNT,IOCTL_ASYNC,ttyfd)) return(0);
    else return((rdchbuf.length-rdchbuf.index)+data[0]);
}


/*  T T X I N  --  Get n characters from tty input buffer  */

/*  Returns number of characters actually gotten, or -1 on failure  */

/*  Intended for use only when it is known that n characters are actually */
/*  available in the input buffer.  */

ttxin(int n, char *buf)
{
    int i, j;

    if (ttyfd < 0) return(-1);		/* Not open. */
    i = 0;
    while (i < n) {
    	if ((j = ttinc(0)) < 0) break;
    	buf[i++] = j;
    }
    return(i);
}

/*  T T O L  --  Similar to "ttinl", but for writing.  */

ttol(s,n) int n; char *s; {
    USHORT i;

    if (ttyfd < 0) return(-1);		/* Not open. */
    if (DosWrite(ttyfd,s,n,&i)) return(-1);
    else return(i);
}


/*  T T O C  --  Output a character to the communication line  */

ttoc(c) CHAR c; {
    USHORT i;

    if (ttyfd < 0) return(-1);		/* Not open. */
    if(DosWrite(ttyfd,&c,1,&i)) return(-1);
    else return(i);
}


/*  T T O C I  --  Output a character to the communication line immediately */

ttoci(c) CHAR c; {
    USHORT i;

    if (ttyfd < 0) return(-1);		/* Not open. */
    if(DosDevIOCtl(NULL,&c,ASYNC_TRANSMITIMM,IOCTL_ASYNC,ttyfd)) return(-1);
    else return(1);
}


/*  T T I N L  --  Read a record (up to break character) from comm line.  */
/*
  If no break character encountered within "max", return "max" characters,
  with disposition of any remaining characters undefined.  Otherwise, return
  the characters that were read, including the break character, in "dest" and
  the number of characters read as the value of the function, or 0 upon end of
  file, or -1 if an error occurred.  Times out & returns error if not completed
  within "timo" seconds.
*/
ttinl(dest,max,timo,eol) int max,timo; CHAR *dest, eol; {
    int x = 0, c, i, m;

    if (ttyfd < 0) return(-1);		/* Not open. */
    *dest = '\0';			/* Clear destination buffer */
    i = 0;				/* Next char to process */
    while (1) {
	if ((c = ttinc(timo)) == -1) {
	    x = -1;
	    break;
	}
        dest[i] = c;			/* Got one. */
	if (dest[i] == eol) {
		dest[++i] = '\0';
		return(i);
	}
	if (i++ > max) {
	    debug(F101,"ttinl buffer overflow","",i);
	    x = -1;
	    break;
	}
    }
    debug(F100,"ttinl timout","",0);	/* Get here on timeout. */
    debug(F111," with",dest,i);
    return(x);				/* and return error code. */
}


/*  T T I N C --  Read a character from the communication line  */

/* The time should be in secs for consitency with the other modules in      */
/* kermit.  To retain the option of using times of less than 1s a negative  */
/* parameter is interpreted as meaning multiples of 0.01s                  */

static rdch(void);

ttinc(timo) int timo; {
    int m, i;
    char ch = 0;

    m = (ttprty) ? 0177 : 0377;		/* Parity stripping mask. */
    if (ttyfd < 0) return(-1);		/* Not open. */

    if (timo == 0) {			/* Untimed. */
        ttydcb.usReadTimeout = 9;       /* Test every  0.1s per call */
        if (DosDevIOCtl(NULL,&ttydcb,ASYNC_SETDCBINFO,1,ttyfd))
          return(-1);
        do
          i = rdch();
        while (i < 0);   /* Wait for a character. */
        return(i & m);
    }

    if (timo < 0)
        timo= -timo - 1;
    else
        timo = timo * 100 - 1;

    if (timo != ttydcb.usReadTimeout + 1) { /* Set timeout value */
       ttydcb.usReadTimeout = timo;
       if (DosDevIOCtl(NULL,&ttydcb,ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
          return(-1);
    }

    i = rdch();

    if (i < 0) return(-1);
    else return(i & m);
}

/*  RDCH -- Read characters from the serial port, maintaining an internal
            buffer of characters for the sake of efficiency. */
static rdch() {

    if (rdchbuf.index == rdchbuf.length) {
	rdchbuf.index = 0;
        if (DosRead(ttyfd,rdchbuf.buffer,sizeof(rdchbuf.buffer),
                    &rdchbuf.length)) {
	    rdchbuf.length = 0;
	    return(-1);
        }
    }

    return( (rdchbuf.index < rdchbuf.length)
            ? rdchbuf.buffer[rdchbuf.index++] : -1 );
}

/*  T T S C A R R  --  Set ttcarr variable, controlling carrier handling.
 *
 *  0 = Off: Always ignore carrier. E.g. you can connect without carrier.
 *  1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect.
 *  2 = Auto: For "modem direct": The same as "Off".
 *            For real modem types: Heed carrier during connect, but ignore
 *                it anytime else.  Compatible with pre-5A C-Kermit versions.
 */

int
ttscarr(carrier) int carrier; {
    ttcarr = carrier;
    debug(F101, "ttscarr","",ttcarr);
    return(ttcarr);
}

/*  T T G M D M  --  Get modem signals  */
/*
 Looks for the modem signals CTS, DSR, and CTS, and returns those that are
 on in as its return value, in a bit mask as described for ttwmdm.  Returns:
 -3 Not implemented
 -2 if the line does not have modem control
 -1 on error.
 >= 0 on success, with a bit mask containing the modem signals that are on.
*/

int
ttgmdm() {
    BYTE instat, outstat;
    int modem = 0;

    if(DosDevIOCtl(&instat,NULL,ASYNC_GETMODEMINPUT,IOCTL_ASYNC,ttyfd))
       return(-1);
    if(DosDevIOCtl(&outstat,NULL,ASYNC_GETMODEMOUTPUT,IOCTL_ASYNC,ttyfd))
       return(-1);

    /* Clear To Send */
    if (instat & CTS_ON) modem |= BM_CTS;
    /* Data Set Ready */
    if (instat & DSR_ON) modem |= BM_DSR;
    /* Carrier */
    if (instat & DCD_ON) modem |= BM_DCD;
    /* Ring Indicate */
    if (instat & RI_ON)  modem |= BM_RNG;

    /* Data Terminal Ready */
    if (outstat & DTR_ON) modem |= BM_DTR;
    /* Request To Send */
    if (outstat & RTS_ON) modem |= BM_RTS;

    return(modem);
}

/*  T T S N D B  --  Send a BREAK signal  */

ttsndb() {
    USHORT i;

    DosDevIOCtl(&i,NULL,ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);	/* Break on */
    DosSleep(275L);					/* ZZZzzz */
    DosDevIOCtl(&i,NULL,ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);	/* Break off */
}

/*  T T S N D L B  --  Send a LONG BREAK signal  */

ttsndlb() {
    USHORT i;

    DosDevIOCtl(&i,NULL,ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);	/* Break on */
    DosSleep(1800L);					/* ZZZzzz */
    DosDevIOCtl(&i,NULL,ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);	/* Break off */
}

/*  S L E E P  --  Emulate the Unix sleep function  */

sleep(t) int t; {
    DosSleep((long)t*1000);
}


/*  M S L E E P  --  Millisecond version of sleep().  */

/* Intended only for small intervals.  For big ones, just use sleep(). */

msleep(m) int m; {
    DosSleep((long)m);
}


/*  R T I M E R --  Reset elapsed time counter  */

void rtimer() {
    tcount = time((long *)NULL);
}


/*  G T I M E R --  Get current value of elapsed time counter in seconds  */

gtimer() {
    int x;
    x = (int) (time( (long *) 0 ) - tcount);
    rtimer();
    return( (x < 0) ? 0 : x );
}


/*  Z T I M E  --  Return date/time string  */

void ztime(s) char **s; {
    long clock_storage;

    clock_storage = time( (long *) 0 );
    *s = ctime( &clock_storage );
}

/*  C O N O C  --  Output a character to the console terminal  */

conoc(c) char c; {
    write(1,&c,1);
}


/*  C O N X O  --  Write x characters to the console terminal  */

conxo(x,s) char *s; int x; {
    write(1,s,x);
}


/*  C O N O L  --  Write a line to the console terminal  */

conol(s) char *s; {
    int len;
    len = strlen(s);
    write(1,s,len);
}


/*  C O N O L A  --  Write an array of lines to the console terminal */

conola(s) char *s[]; {
    int i;
    for (i=0 ; *s[i] ; i++) conol(s[i]);
}


/*  C O N O L L  --  Output a string followed by CRLF  */

conoll(s) char *s; {
    conol(s);
    write(1,"\r\n",2);
}


/*  C O N C H K  --  Return how many characters available at console  */

conchk() {
    KBDKEYINFO k;

    KbdPeek(&k,0);
    return( (k.fbStatus & 0x40) ? 1 : 0 );
}


/*  C O N I N C  --  Get a character from the console  */

coninc(timo) int timo; {
    int c;

    while ( (c = congks(timo)) >= 0x100 );

    return c;
}

congks(timo) int timo; {
    /* timeout isn't handled */
    KBDKEYINFO k;
    int c;

    for (;;)
    {
        KbdCharIn(&k, 0, 0);

        if ( k.chChar || k.chScan )
        {
            c = k.chChar;

            if (c == 0x00) c = 0x100 | k.chScan;
            if (c == 0xE0) c = 0x200 | k.chScan;

            return c;
        }

        if ( (k.fbStatus & KBDTRF_SHIFT_KEY_IN) &&
             (k.fsState & KBDSTF_SCROLLLOCK) )
            if ( k.fsState & KBDSTF_SCROLLLOCK_ON )
                return 0x2FF;
            else
                return 0x1FF;
    }
}

conraw() {
    KBDINFO k;

    conmode = 1;
    k.cb = sizeof(k);
    KbdGetStatus(&k,0);
    k.fsMask = KEYBOARD_ECHO_OFF
             | KEYBOARD_BINARY_MODE
             | KEYBOARD_SHIFT_REPORT;
    return(KbdSetStatus(&k,0));
}

concooked() {
    KBDINFO k;

    conmode = 0;
    k.cb = sizeof(k);
    KbdGetStatus(&k,0);
    k.fsMask = KEYBOARD_ECHO_ON
             | KEYBOARD_ASCII_MODE;
    return(KbdSetStatus(&k,0));
}

/*  C O N B I N  --  Put console in binary mode  */

/*  Returns 0 if ok, -1 if not  */

conbin(char esc) {
    if (!isatty(0)) return(0);          /* only for real ttys */
    conraw();
}

/*  C O N G M  -- Get console terminal mode. */

congm() {}


/*  C O N C B  -- Put console into single char mode with no echo. */

concb(esc) char esc; {
    if (!isatty(0)) return(0);          /* only for real ttys */
    concooked();
}


/*  C O N R E S -- Restore console to congm mode. */

conres() {}


/*  C O N I N T -- Enable console terminal interrupts. */

void conint(f, s) SIGTYP (*f)(int), (*s)(int); {}


/*  C O N N O I -- No console interrupts. */

void connoi() {}

/* privilege dummy */

int priv_chk() {return 0;}


/* alarm() implementation */

#define STACK 2048
static PBYTE pstack;
static PID pid;
static BOOL initialized, active, running;
static USHORT delay;

#pragma check_stack(off)

static VOID FAR alarm_thread(VOID)
{
  for (;;)
  {
    DosSleep(1000L);
    DosEnterCritSec();

    if ( !active )
      break;

    if ( --delay == 0 )
      break;

    DosExitCritSec();
  }

  running = FALSE;
  DosExitCritSec();

  if ( active )
    DosFlagProcess(pid, FLGP_PID, PFLG_A, 1);

  active = FALSE;
  DosExit(EXIT_THREAD, 0);
}

#pragma check_stack()

static VOID PASCAL FAR alarm_signal(USHORT sigarg, USHORT signum)
{
  raise(SIGUSR1);
}

unsigned alarm(unsigned sec)
{
  PFNSIGHANDLER prev;
  USHORT action;
  PIDINFO pi;
  TID tid;
  unsigned old;

  if ( !initialized )
  {
    if ( pstack == NULL )
      pstack = malloc(STACK);
    assert(pstack != NULL);

    DosGetPID(&pi);
    pid = pi.pid;
    DosSetSigHandler(alarm_signal, &prev, &action, SIGA_ACCEPT, SIG_PFLG_A);
    initialized = TRUE;
  }

  DosEnterCritSec();
  old = delay;

  if ( active = ((delay = sec) > 0) )
    if ( !running )
    {
      running = TRUE;
      assert(DosCreateThread(alarm_thread, &tid, pstack + STACK) == 0);
    }

  DosExitCritSec();
  return old;
}


/*
 *  A public domain implementation of BSD directory routines for
 *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
 *  August 1897
 *  Ported to OS/2 by Kai Uwe Rommel
 *  December 1989, February 1990
 *  Change for HPFS support, October 1990
 */

int attributes = A_DIR | A_HIDDEN;

static char *getdirent(char *);
static void free_dircontents(struct _dircontents *);

static HDIR hdir;
static USHORT count;
static FILEFINDBUF find;


int IsFileSystemFAT(char *dir)
{
  USHORT nDrive;
  ULONG lMap;
  BYTE bData[64], bName[3];
  USHORT cbData;
  static USHORT nLastDrive = -1, nResult;

  if ( _osmode == DOS_MODE )
    return TRUE;
  else
  {
    /* We separate FAT and HPFS file systems here. */

    if ( isalpha(dir[0]) && (dir[1] == ':') )
      nDrive = toupper(dir[0]) - '@';
    else
      DosQCurDisk(&nDrive, &lMap);

    if ( nDrive == nLastDrive )
      return nResult;

    bName[0] = (char) (nDrive + '@');
    bName[1] = ':';
    bName[2] = 0;

    nLastDrive = nDrive;
    cbData = sizeof(bData);

    if ( !DosQFSAttach(bName, 0U, 1U, bData, &cbData, 0L) )
      nResult = !strcmp(bData + (*(USHORT *) (bData + 2) + 7), "FAT");
    else
      nResult = FALSE;

    /* End of this ugly code */
    return nResult;
  }
}


DIR *opendir(char *name)
{
  struct stat statb;
  DIR *dirp;
  char c;
  char *s;
  struct _dircontents *dp;
  char nbuf[MAXPATHLEN + 1];
  int len;

  strcpy(nbuf, name);
  len = strlen (nbuf);
  s = nbuf + len;

  if ( ((c = nbuf[strlen(nbuf) - 1]) == '\\' || c == '/') &&
       (strlen(nbuf) > 1) )
  {
    nbuf[strlen(nbuf) - 1] = 0;

    if ( nbuf[strlen(nbuf) - 1] == ':' )
      strcat(nbuf, "\\.");
  }
  else
    if ( nbuf[strlen(nbuf) - 1] == ':' )
      strcat(nbuf, ".");

  if (stat(nbuf, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
    return NULL;

  if ( (dirp = malloc(sizeof(DIR))) == NULL )
    return NULL;

  if ( nbuf[strlen(nbuf) - 1] == '.' )
    strcpy(nbuf + strlen(nbuf) - 1, "*.*");
  else
    if ( ((c = nbuf[strlen(nbuf) - 1]) == '\\' || c == '/') &&
         (strlen(nbuf) == 1) )
      strcat(nbuf, "*.*");
    else
      strcat(nbuf, "\\*.*");

  dirp -> dd_loc = 0;
  dirp -> dd_contents = dirp -> dd_cp = NULL;

  if ((s = getdirent(nbuf)) == NULL)
    return dirp;

  do
  {
    if (((dp = malloc(sizeof(struct _dircontents))) == NULL) ||
        ((dp -> _d_entry = malloc(strlen(s) + 1)) == NULL)      )
    {
      if (dp)
        free(dp);
      free_dircontents(dirp -> dd_contents);

      return NULL;
    }

    if (dirp -> dd_contents)
    {
      dirp -> dd_cp -> _d_next = dp;
      dirp -> dd_cp = dirp -> dd_cp -> _d_next;
    }
    else
      dirp -> dd_contents = dirp -> dd_cp = dp;

    strcpy(dp -> _d_entry, s);
    dp -> _d_next = NULL;

    dp -> _d_size = find.cbFile;
    dp -> _d_mode = find.attrFile;
    dp -> _d_time = *(unsigned *) &(find.ftimeLastWrite);
    dp -> _d_date = *(unsigned *) &(find.fdateLastWrite);
  }
  while ((s = getdirent(NULL)) != NULL);

  dirp -> dd_cp = dirp -> dd_contents;

  return dirp;
}


void closedir(DIR * dirp)
{
  free_dircontents(dirp -> dd_contents);
  free(dirp);
}


struct dirent *readdir(DIR * dirp)
{
  static struct dirent dp;

  if (dirp -> dd_cp == NULL)
    return NULL;

  dp.d_namlen = dp.d_reclen =
    strlen(strcpy(dp.d_name, dirp -> dd_cp -> _d_entry));

  dp.d_ino = 1;

  dp.d_size = dirp -> dd_cp -> _d_size;
  dp.d_mode = dirp -> dd_cp -> _d_mode;
  dp.d_time = dirp -> dd_cp -> _d_time;
  dp.d_date = dirp -> dd_cp -> _d_date;

  dirp -> dd_cp = dirp -> dd_cp -> _d_next;
  dirp -> dd_loc++;

  return &dp;
}


void seekdir(DIR * dirp, long off)
{
  long i = off;
  struct _dircontents *dp;

  if (off >= 0)
  {
    for (dp = dirp -> dd_contents; --i >= 0 && dp; dp = dp -> _d_next);

    dirp -> dd_loc = off - (i + 1);
    dirp -> dd_cp = dp;
  }
}


long telldir(DIR * dirp)
{
  return dirp -> dd_loc;
}


static void free_dircontents(struct _dircontents * dp)
{
  struct _dircontents *odp;

  while (dp)
  {
    if (dp -> _d_entry)
      free(dp -> _d_entry);

    dp = (odp = dp) -> _d_next;
    free(odp);
  }
}


static char *getdirent(char *dir)
{
  int done;
  static int lower;

  if (dir != NULL)
  {				       /* get first entry */
    lower = IsFileSystemFAT(dir);

    hdir = HDIR_CREATE;
    count = 1;
    done = DosFindFirst(dir, &hdir, attributes,
			&find, sizeof(find), &count, 0L);
  }
  else				       /* get next entry */
    done = DosFindNext(hdir, &find, sizeof(find), &count);

  if (done == 0)
  {
    if ( lower )
      strlwr(find.achName);

    return find.achName;
  }
  else
  {
    DosFindClose(hdir);
    return NULL;
  }
}

