ftp.nice.ch/pub/next/unix/editor/me.3.10.N.bs.tar.gz#/me-3.10.N.bs/src/msdos.c

This is msdos.c in view mode; [Download] [Up]

/*	MSDOS.C:	Operating specific I/O and Spawning functions
			under the MS/PCDOS operating system
			for MicroEMACS 3.10
			(C)opyright 1988 by Daniel M. Lawrence
*/

#include        <stdio.h>
#include	"estruct.h"
#include	"etype.h"

#ifdef	MSDOS
#include        "edef.h"
#include	"elang.h"
#include	<direct.h>

/* The Mouse driver only works with typeahead defined */
#if	MOUSE
#undef	TYPEAH
#define	TYPEAH	1
#endif

#if  TURBO
#include <conio.h>
#include <dir.h>
#include <dos.h>

struct ffblk fileblock;	/* structure for directory searches */
#endif

#if  MSC
#include <dos.h>

struct find_t fileblock;
#endif

#if     LATTICE | MSC | DTL | TURBO | AZTEC | MWC
union REGS rg;		/* cpu register for use of DOS calls */
struct SREGS segreg;	/* cpu segment registers	     */
int nxtchar = -1;	/* character held from type ahead    */
#endif

#if	MSC | TURBO
#include	<process.h>
#endif

/*	Some global variable	*/
#define INBUFSIZ	40
static int mexist;	/* is the mouse driver installed? */
static int nbuttons;	/* number of buttons on the mouse */
static int oldbut;	/* Previous state of mouse buttons */

/*	input buffers and pointers	*/

#define	IBUFSIZE	64	/* this must be a power of 2 */

unsigned char in_buf[IBUFSIZE];	/* input character buffer */
int in_next = 0;		/* pos to retrieve next input character */
int in_last = 0;		/* pos to place most recent input character */

in_init()	/* initialize the input buffer */

{
	in_next = in_last = 0;
}

in_check()	/* is the input buffer non-empty? */

{
	if (in_next == in_last)
		return(FALSE);
	else
		return(TRUE);
}

in_put(event)

int event;	/* event to enter into the input buffer */

{
	in_buf[in_last++] = event;
	in_last &= (IBUFSIZE - 1);
}

int in_get()	/* get an event from the input buffer */

{
	register int event;	/* event to return */

	event = in_buf[in_next++];
	in_next &= (IBUFSIZE - 1);
	return(event);
}

/*
 * This function is called once to set up the terminal device streams.
 */

PASCAL NEAR ttopen()

{
#if	MOUSE
	long miaddr;	/* mouse interupt routine address */
#endif

#if     (HP150 == 0) & LATTICE
	/* kill the ctrl-break interupt */
	rg.h.ah = 0x33;		/* control-break check dos call */
	rg.h.al = 1;		/* set the current state */
	rg.h.dl = 0;		/* set it OFF */
	intdos(&rg, &rg);	/* go for it! */
#endif
	/* on all screens we are not sure of the initial position
	   of the cursor					*/
	ttrow = 999;
	ttcol = 999;

#if	MOUSE
	/* check if the mouse drive exists first */
	rg.x.ax = 0x3533;	/* look at the interrupt 33 address */
#if	MSC | TURBO | DTL | LATTICE | MWC
	int86x(0x21, &rg, &rg, &segreg);
	miaddr = (((long)segreg.es) << 16) + (long)rg.x.bx;
	if (miaddr == 0 || *(char * far)miaddr == 0xcf) {
#endif
#if	AZTEC
	sysint(0x21, &rg, &rg);
	miaddr = (((long)rg.x.es) << 16) + (long)rg.x.bx;
	if (miaddr == 0 || *(char *)miaddr == 0xcf) {
#endif
		mexist = FALSE;
		return;
	}

	/* and then check for the mouse itself */
	rg.x.ax = 0;		/* mouse status flag */
	int86(0x33, &rg, &rg);	/* check for the mouse interupt */
	mexist = (rg.x.ax != 0);
	nbuttons = rg.x.bx;
	in_init();
	if (mexist == FALSE)
		return;

	/* if the mouse exists.. get it in the upper right corner */
	rg.x.ax = 4;		/* set mouse cursor position */
	rg.x.cx = (term.t_ncol - 1) << 3;	/* last col of display */
	rg.x.dx = 0;		/* top row */
	int86(0x33, &rg, &rg);

	/* and set its attributes */
	rg.x.ax = 10;		/* set text cursor */
	rg.x.bx = 0;		/* software text cursor please */
	rg.x.cx = 0x77ff;	/* screen mask */
	rg.x.dx = 0x7700;	/* cursor mask */
	int86(0x33, &rg, &rg);
#else
	mexist = 0;
#endif
}

maxlines(lines)		/* set number of vertical rows for mouse */

int lines;	/* # of vertical lines */

{
#if	MOUSE
	if (mexist) {
		rg.x.ax = 8;		/* set min/max vertical cursor position */
		rg.x.cx = 0;		/* start at 0 */
		rg.x.dx = (lines - 1)<<3; /* end at the end */
		int86(0x33, &rg, &rg);
	}
#endif
}

/*
 * This function gets called just before we go back home to the command
 * interpreter. On VMS it puts the terminal back in a reasonable state.
 * Another no-operation on CPM.
 */
PASCAL NEAR ttclose()
{
#if     (HP150 == 0) & LATTICE
	/* restore the ctrl-break interrupt */
	rg.h.ah = 0x33;		/* control-break check dos call */
	rg.h.al = 1;		/* set the current state */
	rg.h.dl = 1;		/* set it ON */
	intdos(&rg, &rg);	/* go for it! */
#endif
}

/*
 * Write a character to the display. On VMS, terminal output is buffered, and
 * we just put the characters in the big array, after checking for overflow.
 * On CPM terminal I/O unbuffered, so we just write the byte out. Ditto on
 * MS-DOS (use the very very raw console output routine).
 */
PASCAL NEAR ttputc(c)

{
#if     MWC
        putcnb(c);
#endif

#if	(LATTICE | AZTEC | TURBO | MSC) & ~IBMPC
	bdos(6, c, 0);
#endif
}

/*
 * Flush terminal buffer. Does real work where the terminal output is buffered
 * up. A no-operation on systems where byte at a time terminal I/O is done.
 */
PASCAL NEAR ttflush()
{
}

int doschar()	/* call the dos to get a char */

{
	register unsigned int c;	/* extended character to return */ 

	rg.h.ah = 7;		/* dos Direct Console Input call */
	intdos(&rg, &rg);
	if (rg.h.al == 0) {	/* function key!! */
		rg.h.ah = 7;	/* get the next character */
		intdos(&rg, &rg);
		c = extcode(rg.h.al);
		in_put(c >> 8);		/* prefix byte */
		in_put(c & 255);	/* event code byte */
		return(0);		/* extended escape sequence */
	}
	return(rg.h.al & 255);
}

/*
 * Read a character from the terminal, performing no editing and doing no echo
 * at all. Also mouse events are forced into the input stream here.
 */
PASCAL NEAR ttgetc()

{
	register int c;		/* character read */

ttc:	/* return any keystrokes waiting in the
	   type ahead buffer */
	if (in_check())
		return(in_get());

	if (typahead())
		return(doschar());

	/* with no mouse, this is a simple get char routine */
	if (mexist == FALSE || mouseflag == FALSE)
		return(doschar());

#if	MOUSE
	/* turn the mouse cursor on */
	rg.x.ax = 1;	/* Show Cursor */
	int86(0x33, &rg, &rg);

	/* loop waiting for something to happen */
	while (TRUE) {
		if (typahead())
			break;
		if (checkmouse())
			break;
	}

	/* turn the mouse cursor back off */
	rg.x.ax = 2;	/* Hide Cursor */
	int86(0x33, &rg, &rg);

	goto ttc;
#endif
}

#if	MOUSE
checkmouse()

{
	register int k;		/* current bit/button of mouse */
	register int event;	/* encoded mouse event */
	int newbut;		/* new state of the mouse buttons */
	int mousecol;		/* current mouse column */
	int mouserow;		/* current mouse row */
	int sstate;		/* current shift key status */

	/* check to see if any mouse buttons are different */
	rg.x.ax = 3;	/* Get button status and mouse position */
	int86(0x33, &rg, &rg);
	newbut   = rg.x.bx;
	mousecol = rg.x.cx >> 3;
	mouserow = rg.x.dx >> 3;

	/* get the shift key status as well */
	sstate = 0;
	rg.h.ah = 2;	/* return current shift status */
	int86(0x16, &rg, &rg);
	sstate = rg.h.al;

	for (k=1; k != (1 << nbuttons); k = k<<1) {
		/* For each button on the mouse */
		if ((oldbut&k) != (newbut&k)) {
			/* This button changed, generate an event */
			in_put(0);
			in_put(MOUS >> 8);
			in_put(mousecol);
			in_put(mouserow);
			event = ((newbut&k) ? 0 : 1);	/* up or down? */
			if (k == 2)			/* center button? */
				event += 4;
			if (k == 4)			/* right button? */
				event += 2;
			if (sstate & 3)			/* shifted */
				event += 'A';
			else if (sstate & 4)		/* controled? */
				event += 1;
			else
				event += 'a';		/* plain */
			in_put(event);
			oldbut = newbut;
			return(TRUE);
		}
	}
	return(FALSE);
}
#endif

#if	TYPEAH
/* typahead:	Check to see if any characters are already in the
		keyboard buffer
*/

PASCAL NEAR typahead()

{
	int flags;	/* cpu flags from dos call */

	rg.x.ax = 0x4406;	/* IOCTL input status */
	rg.x.bx = 0;		/* File handle = stdin */
#if	MSC | DTL
	int86(0x21,&rg,&rg);
	flags = rg.h.al;
#else
#if	LATTICE | AZTEC | TURBO
	flags = intdos(&rg, &rg);
#else
	intcall(&rg, &rg, 0x21);
	flags = rg.x.flags;
#endif
#endif
	if (flags)		/* AL = 0xFF if ready */
		return(TRUE);
	else
		return(FALSE);
}
#endif

/*
 * Create a subjob with a copy of the command intrepreter in it. When the
 * command interpreter exits, mark the screen as garbage so that you do a full
 * repaint. Bound to "^X C".
 */

PASCAL NEAR spawncli(f, n)

{
	char	OldScreenResolution[NBUFN];			   /* -- ZU */

	/* don't allow this command if restricted */
	if (restflag)
		return(resterr());

	strcpy( OldScreenResolution, sres );			   /* -- ZU */

        movecursor(term.t_nrow, 0);             /* Seek to last line.   */
        TTflush();
	TTkclose();

	TTclose();						   /* -- ZU */
	shellprog("");

	TTopen();						   /* -- ZU */
	strcpy( sres, OldScreenResolution );			   /*     . */
	TTrez( sres );						   /* -- UZ */

	TTkopen();
        sgarbf = TRUE;
        return(TRUE);
}

/*
 * Run a one-liner in a subjob. When the command returns, wait for a single
 * character to be typed, then mark the screen as garbage so a full repaint is
 * done. Bound to "C-X !".
 */
PASCAL NEAR spawn(f, n)
{
        register int s;
        char line[NLINE];

	/* don't allow this command if restricted */
	if (restflag)
		return(resterr());

        if ((s=mlreply("!", line, NLINE)) != TRUE)
                return(s);
	movecursor(term.t_nrow - 1, 0);
	TTkclose();
        shellprog(line);
	TTkopen();
	/* if we are interactive, pause here */
	if (clexec == FALSE) {
	        mlputs(TEXT6);
/*                     "\r\n\n[End]" */
        	tgetc();
        }
        sgarbf = TRUE;
        return (TRUE);
}

/*
 * Run an external program with arguments. When it returns, wait for a single
 * character to be typed, then mark the screen as garbage so a full repaint is
 * done. Bound to "C-X $".
 */

PASCAL NEAR execprg(f, n)

{
        register int s;
        char line[NLINE];
	char OldScreenResolution[NBUFN];			   /* -- ZU */


	/* don't allow this command if restricted */
	if (restflag)
		return(resterr());

        if ((s=mlreply("$", line, NLINE)) != TRUE)
                return(s);

	strcpy( OldScreenResolution, sres );			   /* -- ZU */

	movecursor(term.t_nrow - 1, 0);
	TTkclose();
	TTclose();						   /* -- ZU */
        execprog(line);
	TTopen();						   /* -- ZU */
	strcpy( sres, OldScreenResolution );			   /*     . */
	TTrez( sres );						   /* -- UZ */
	TTkopen();
	/* if we are interactive, pause here */
	if (clexec == FALSE) {
	        mlputs(TEXT6);
/*                     "\r\n\n[End]" */
        	tgetc();
        }
        sgarbf = TRUE;
        return (TRUE);
}

/*
 * Pipe a one line command into a window
 * Bound to ^X @
 */
PASCAL NEAR pipecmd(f, n)
{
	register WINDOW *wp;	/* pointer to new window */
	register BUFFER *bp;	/* pointer to buffer to zot */
	register char *tmp;	/* ptr to TMP DOS environment variable */
	FILE *fp;
        char line[NLINE];	/* command line send to shell */
	static char bname[] = "command";
	static char filnam[NSTRING] = "command";
	char *getenv();
	FILE *fopen();

	/* don't allow this command if restricted */
	if (restflag)
		return(resterr());

	if ((tmp = getenv("TMP")) == NULL)
		filnam[0] = 0;
	else {
		strcpy(filnam, tmp);
		if (filnam[strlen(filnam) - 1] != '\\')
			strcat(filnam, "\\");
        }
	strcat(filnam,"command");

	/* get the command to pipe in */
        if (mlreply("@", line, NLINE) != TRUE)
                return(FALSE);

	/* get rid of the command output buffer if it exists */
        if ((bp=bfind(bname, FALSE, 0)) != FALSE) {
		/* try to make sure we are off screen */
		wp = wheadp;
		while (wp != NULL) {
			if (wp->w_bufp == bp) {
				onlywind(FALSE, 1);
				break;
			}
			wp = wp->w_wndp;
		}
		/* get rid of the existing command buffer */
		if (zotbuf(bp) != TRUE)
			return(FALSE);
	}

	/* redirect the command output to the output file */
	strcat(line, " >>");
	strcat(line, filnam);
	movecursor(term.t_nrow - 1, 0);

	/* execute the command */
	TTkclose();
        shellprog(line);
	TTkopen();
        sgarbf = TRUE;

        /* did the output file get generated? */
	if ((fp = fopen(filnam, "r")) == NULL)
		return(FALSE);
	fclose(fp);

	/* split the current window to make room for the command output */
	if (splitwind(FALSE, 1) == FALSE)
			return(FALSE);

	/* and read the stuff in */
	if (getfile(filnam, FALSE) == FALSE)
		return(FALSE);

	/* make this window in VIEW mode, update all mode lines */
	curwp->w_bufp->b_mode |= MDVIEW;
	wp = wheadp;
	while (wp != NULL) {
		wp->w_flag |= WFMODE;
		wp = wp->w_wndp;
	}

	/* and get rid of the temporary file */
	unlink(filnam);
	return(TRUE);
}

/*
 * filter a buffer through an external DOS program
 * Bound to ^X #
 */
PASCAL NEAR filter(f, n)

{
        register int    s;	/* return status from CLI */
	register BUFFER *bp;	/* pointer to buffer to zot */
        char line[NLINE];	/* command line send to shell */
	char tmpnam[NFILEN];	/* place to store real file name */
	static char bname1[] = "fltinp";

	static char filnam1[] = "fltinp";
	static char filnam2[] = "fltout";

	/* don't allow this command if restricted */
	if (restflag)
		return(resterr());

	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
		return(rdonly());	/* we are in read only mode	*/

	/* get the filter name and its args */
        if ((s=mlreply("#", line, NLINE)) != TRUE)
                return(s);

	/* setup the proper file names */
	bp = curbp;
	strcpy(tmpnam, bp->b_fname);	/* save the original name */
	strcpy(bp->b_fname, bname1);	/* set it to our new one */

	/* write it out, checking for errors */
	if (writeout(filnam1) != TRUE) {
		mlwrite(TEXT2);
/*                      "[Cannot write filter file]" */
		strcpy(bp->b_fname, tmpnam);
		return(FALSE);
	}

	strcat(line," <fltinp >fltout");
	movecursor(term.t_nrow - 1, 0);
	TTkclose();
        shellprog(line);
	TTkopen();
        sgarbf = TRUE;
	s = TRUE;

	/* on failure, escape gracefully */
	if (s != TRUE || (readin(filnam2,FALSE) == FALSE)) {
		mlwrite(TEXT3);
/*                      "[Execution failed]" */
		strcpy(bp->b_fname, tmpnam);
		unlink(filnam1);
		unlink(filnam2);
		return(s);
	}

	/* reset file name */
	strcpy(bp->b_fname, tmpnam);	/* restore name */
	bp->b_flag |= BFCHG;		/* flag it as changed */

	/* and get rid of the temporary file */
	unlink(filnam1);
	unlink(filnam2);
	return(TRUE);
}

#if	LATTICE
extern int _oserr;
#endif

#if	AZTEC | MWC
extern int errno;
#endif

#if	MSC
extern int _doserrno;
#endif

/*	SHELLPROG: Execute a command in a subshell		*/

PASCAL NEAR shellprog(cmd)

char *cmd;	/*  Incoming command line to execute  */

{
	char *shell;		/* Name of system command processor */
	char swchar;		/* switch character to use */
	union REGS regs;	/* parameters for dos call */
	char comline[NSTRING];	/* constructed command line */
	char *getenv();

	/*  detect current switch character and set us up to use it */
	regs.h.ah = 0x37;	/*  get setting data  */
	regs.h.al = 0x00;	/*  get switch character  */
	intdos(&regs, &regs);
	swchar = (char)regs.h.dl;

	/*  get name of system shell  */
	if ((shell = getenv("COMSPEC")) == NULL) {
		return(FALSE);		/*  No shell located  */
	}

	/* trim leading whitespace off the command */
	while (*cmd == ' ' || *cmd == '\t')	/*  find out if null command */
		cmd++;

	/**  If the command line is not empty, bring up the shell  **/
	/**  and execute the command.  Otherwise, bring up the     **/
	/**  shell in interactive mode.   **/

	if (*cmd) {
		strcpy(comline, shell);
		strcat(comline, " ");
		comline[strlen(comline) + 1] = 0;
		comline[strlen(comline)] = swchar;
		strcat(comline, "c ");
		strcat(comline, cmd);
		return(execprog(comline));
	} else
		return(execprog(shell));
}

/*	EXECPROG:	A function to execute a named program
			with arguments
*/

#if	LATTICE | AZTEC | MWC
#define	CFLAG	1
#endif

PASCAL NEAR execprog(cmd)

char *cmd;	/*  Incoming command line to execute  */

{
	char *sp;		/* temporary string pointer */
	char f1[38];		/* FCB1 area (not initialized */
	char f2[38];		/* FCB2 area (not initialized */
	char prog[NSTRING];	/* program filespec */
	char tail[NSTRING];	/* command tail with length byte */
	char curdir[NFILEN];	/* current directory save space */
	union REGS regs;	/* parameters for dos call  */
#if	MWC == 0
	struct SREGS segreg;	/* segment registers for dis call */
#endif
	struct Pblock {		/* EXEC parameter block */
		short envptr;	/* 2 byte pointer to environment string */
		char *cline;	/* 4 byte pointer to command line */
		char *fcb1;	/* 4 byte pointer to FCB at PSP+5Ch */
		char *fcb2;	/* 4 byte pointer to FCB at PSP+6Ch */
	} pblock;

	/* parse the command name from the command line */
	sp = prog;
	while (*cmd && (*cmd != ' ') && (*cmd != '\t'))
		*sp++ = *cmd++;
	*sp = 0;

	/* and parse out the command tail */
	while (*cmd && ((*cmd == ' ') || (*cmd == '\t')))
		++cmd;
	*tail = (char)(strlen(cmd)); /* record the byte length */
	strcpy(&tail[1], cmd);
	strcat(&tail[1], "\r");

	/* look up the program on the path trying various extentions */
	if ((sp = flook(prog, TRUE)) == NULL)
		if ((sp = flook(strcat(prog, ".exe"), TRUE)) == NULL) {
			strcpy(&prog[strlen(prog)-4], ".com");
			if ((sp = flook(prog, TRUE)) == NULL)
				return(FALSE);
		}
	strcpy(prog, sp);

	getcwd( curdir, 66 );		/* save current directory */

#if	MWC == 0
	/* get a pointer to this PSPs environment segment number */
	segread(&segreg);
#endif

	/* set up the EXEC parameter block */
	pblock.envptr = 0;	/* make the child inherit the parents env */
	pblock.fcb1 = f1;		/* point to a blank FCB */
	pblock.fcb2 = f2;		/* point to a blank FCB */
        pblock.cline = tail;		/* parameter line pointer */

	/* and make the call */
	regs.h.ah = 0x4b;	/* EXEC Load or Execute a Program */
	regs.h.al = 0x00;	/* load end execute function subcode */
#if	AZTEC | MWC
	regs.x.ds = ((unsigned long)(prog) >> 16);	/* program name ptr */
	regs.x.dx = (unsigned int)(prog);
	regs.x.es = regs.x.ds;
	/*regs.x.es = ((unsigned long)(&pblock) >> 16);	* set up param block ptr */
	regs.x.bx = (unsigned int)(&pblock);
#endif
#if	LATTICE | MSC | TURBO | DTL
	segreg.ds = ((unsigned long)(prog) >> 16);	/* program name ptr */
	regs.x.dx = (unsigned int)(prog);
	segreg.es = ((unsigned long)(&pblock) >> 16);	/* set up param block ptr */
	regs.x.bx = (unsigned int)(&pblock);
#endif

#if	NOVELL
	rval = execpr(prog, &pblock);
#endif
	
#if	LATTICE && (NOVELL == 0)
	if ((intdosx(&regs, &regs, &segreg) & CFLAG) == 0) {
		regs.h.ah = 0x4d;	/* get child process return code */
		intdos(&regs, &regs);	/* go do it */
		rval = regs.x.ax;	/* save child's return code */
	} else
		rval = -_oserr;		/* failed child call */
#endif
#if	AZTEC && (NOVELL == 0)
	if ((sysint(0x21, &regs, &regs) & CFLAG) == 0) {
		regs.h.ah = 0x4d;	/* get child process return code */
		sysint(0x21, &regs, &regs);	/* go do it */
		rval = regs.x.ax;	/* save child's return code */
	} else
		rval = -errno;		/* failed child call */
#endif
#if	MWC && (NOVELL == 0)
	intcall(&regs, &regs, DOSINT);
	if ((regs.x.flags & CFLAG) == 0) {
		regs.h.ah = 0x4d;	/* get child process return code */
		intcall(&regs, &regs, DOSINT);	/* go do it */
		rval = regs.x.ax;	/* save child's return code */
	} else
		rval = -errno;		/* failed child call */
#endif
#if	(TURBO | MSC | DTL) && (NOVELL == 0)
	intdosx(&regs, &regs, &segreg);
	if (regs.x.cflag == 0) {
		regs.h.ah = 0x4d;	/* get child process return code */
		intdos(&regs, &regs);	/* go do it */
		rval = regs.x.ax;	/* save child's return code */
	} else
		rval = -_doserrno;	/* failed child call */
#endif
	chdir( curdir );		/* restore current directory */

	return((rval < 0) ? FALSE : TRUE);
}

/* return a system dependant string with the current time */

char *PASCAL NEAR timeset()

{
#if	MWC | TURBO | MSC
	register char *sp;	/* temp string pointer */
	char buf[16];		/* time data buffer */
	extern char *ctime();

	time(buf);
	sp = ctime(buf);
	sp[strlen(sp)-1] = 0;
	return(sp);
#else
	return(errorm);
#endif
}

/*	extcode:	resolve MSDOS extended character codes
			encoding the proper sequences into emacs
			printable character specifications
*/

int extcode(c)

unsigned c;	/* byte following a zero extended char byte */

{
	/* function keys 1 through 9 */
	if (c >= 59 && c < 68)
		return(SPEC | c - 58 + '0');

	/* function key 10 */
	if (c == 68)
		return(SPEC | '0');

	/* shifted function keys */
	if (c >= 84 && c < 93)
		return(SPEC | SHFT | c - 83 + '0');
	if (c == 93)
		return(SPEC | SHFT | '0');

	/* control function keys */
	if (c >= 94 && c < 103)
		return(SPEC | CTRL | c - 93 + '0');
	if (c == 103)
		return(SPEC | CTRL | '0');

	/* ALTed function keys */
	if (c >= 104 && c < 113)
		return(SPEC | ALTD | c - 103 + '0');
	if (c == 113)
		return(SPEC | ALTD | '0');

	/* ALTed number keys */
	if (c >= 120 && c < 129)
		return(ALTD | c - 119 + '0');
	if (c == 130)
		return(ALTD | '0');

	/* some others as well */
	switch (c) {
		case 3:		return(0);		/* null */
		case 15:	return(SHFT | CTRL | 'I');	/* backtab */

		case 16:	return(ALTD | 'Q');
		case 17:	return(ALTD | 'W');
		case 18:	return(ALTD | 'E');
		case 19:	return(ALTD | 'R');
		case 20:	return(ALTD | 'T');
		case 21:	return(ALTD | 'Y');
		case 22:	return(ALTD | 'U');
		case 23:	return(ALTD | 'I');
		case 24:	return(ALTD | 'O');
		case 25:	return(ALTD | 'P');

		case 30:	return(ALTD | 'A');
		case 31:	return(ALTD | 'S');
		case 32:	return(ALTD | 'D');
		case 33:	return(ALTD | 'F');
		case 34:	return(ALTD | 'G');
		case 35:	return(ALTD | 'H');
		case 36:	return(ALTD | 'J');
		case 37:	return(ALTD | 'K');
		case 38:	return(ALTD | 'L');

		case 44:	return(ALTD | 'Z');
		case 45:	return(ALTD | 'X');
		case 46:	return(ALTD | 'C');
		case 47:	return(ALTD | 'V');
		case 48:	return(ALTD | 'B');
		case 49:	return(ALTD | 'N');
		case 50:	return(ALTD | 'M');

		case 71:	return(SPEC | '<');	/* HOME */
		case 72:	return(SPEC | 'P');	/* cursor up */
		case 73:	return(SPEC | 'Z');	/* page up */
		case 75:	return(SPEC | 'B');	/* cursor left */
		case 77:	return(SPEC | 'F');	/* cursor right */
		case 79:	return(SPEC | '>');	/* end */
		case 80:	return(SPEC | 'N');	/* cursor down */
		case 81:	return(SPEC | 'V');	/* page down */
		case 82:	return(SPEC | 'C');	/* insert */
		case 83:	return(SPEC | 'D');	/* delete */
		case 115:	return(SPEC | CTRL | 'B');	/* control left */
		case 116:	return(SPEC | CTRL | 'F');	/* control right */
		case 117:	return(SPEC | CTRL | '>');	/* control END */
		case 118:	return(SPEC | CTRL | 'V');	/* control page down */
		case 119:	return(SPEC | CTRL | '<');	/* control HOME */
		case 132:	return(SPEC | CTRL | 'Z');	/* control page up */
	}

	return(ALTD | c);
}

#if	COMPLET & (TURBO | MSC)
/*	FILE Directory routines		*/

char path[NFILEN];	/* path of file to find */
char rbuf[NFILEN];	/* return file buffer */

/*	do a wild card directory search (for file name completion) */

char *PASCAL NEAR getffile(fspec)

char *fspec;	/* pattern to match */

{
	register int index;		/* index into various strings */
	register int point;		/* index into other strings */
	register int extflag;		/* does the file have an extention? */
	char fname[NFILEN];		/* file/path for DOS call */

	/* first parse the file path off the file spec */
	strcpy(path, fspec);
	index = strlen(path) - 1;
	while (index >= 0 && (path[index] != '/' &&
				path[index] != '\\' && path[index] != ':'))
		--index;
	path[index+1] = 0;

	/* check for an extension */
	point = strlen(fspec) - 1;
	extflag = FALSE;
	while (point >= 0) {
		if (fspec[point] == '.') {
			extflag = TRUE;
			break;
		}
		point--;
	}

	/* construct the composite wild card spec */
	strcpy(fname, path);
	strcat(fname, &fspec[index+1]);
	strcat(fname, "*");
	if (extflag == FALSE)
		strcat(fname, ".*");

	/* and call for the first file */
#if	TURBO
	if (findfirst(fname, &fileblock, 0) == -1)
		return(NULL);
#endif
#if	MSC
	if (_dos_findfirst(fname, _A_NORMAL, &fileblock) != 0)
		return(NULL);
#endif


	/* return the first file name! */
	strcpy(rbuf, path);
#if	TURBO
	strcat(rbuf, fileblock.ff_name);
#endif
#if	MSC
	strcat(rbuf, fileblock.name);
#endif
	mklower(rbuf);
	return(rbuf);
}

char *PASCAL NEAR getnfile()

{
	register int index;		/* index into various strings */
	register int point;		/* index into other strings */
	register int extflag;		/* does the file have an extention? */
	char fname[NFILEN];		/* file/path for DOS call */

	/* and call for the first file */
#if	TURBO
	if (findnext(&fileblock) == -1)
		return(NULL);
#endif
#if	MSC
	if (_dos_findnext(&fileblock) != 0)
		return(NULL);
#endif

	/* return the first file name! */
	strcpy(rbuf, path);
#if	TURBO
	strcat(rbuf, fileblock.ff_name);
#endif
#if	MSC
	strcat(rbuf, fileblock.name);
#endif
	mklower(rbuf);
	return(rbuf);
}
#else
char *PASCAL NEAR getffile(fspec)

char *fspec;	/* file to match */

{
	return(NULL);
}

char *PASCAL NEAR getnfile()

{
	return(NULL);
}
#endif
#endif

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.