[comp.sources.misc] v10i057: MSDOS Shell

istewart@datlog.co.uk (Ian Stewartson) (02/14/90)

Posting-number: Volume 10, Issue 57
Submitted-by: istewart@datlog.co.uk (Ian Stewartson)
Archive-name: sh_dos/part05

#!/bin/sh
# this is part 4 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file shell/sh6.c continued
#
CurArch=4
if test ! -r s2_seq_.tmp
then echo "Please unpack part 1 first!"
     exit 1; fi
( read Scheck
  if test "$Scheck" != $CurArch
  then echo "Please unpack part $Scheck next!"
       exit 1;
  else exit 0; fi
) < s2_seq_.tmp || exit 1
echo "x - Continuing file shell/sh6.c"
sed 's/^X//' << 'SHAR_EOF' >> shell/sh6.c
XVar_List	*path;		/* search path for commands		*/
XVar_List	*ps1;		/* Prompt 1				*/
XVar_List	*ps2;		/* Prompt 2				*/
XVar_List	*C_dir;		/* Current directory			*/
Xchar		*last_prompt;	/* Last prompt output			*/
XVar_List	*ifs;		/* Inter-field separator		*/
Xchar		*home = "HOME";
Xchar		*shell = "SHELL";
Xchar		*history_file = "HISTFILE";
Xchar		*hsymbol = "#";
Xchar		*msymbol = "-";
Xchar		*spcl2 = "$`'\"";
X
X				/* I/O stacks				*/
XIO_Args		ioargstack[NPUSH];
XIO_State	iostack[NPUSH];
X
X				/* Temporary I/O argument		*/
XIO_Args		temparg = {
X    (char *)NULL,		/* Word					*/
X    (char **)NULL,		/* Word list				*/
X    0,				/* File descriptor			*/
X    AFID_NOBUF,			/* Buffer id				*/
X    0L,				/* File position			*/
X    (IO_Buf *)NULL		/* Buffer				*/
X};
X
Xint		areanum;	/* Current allocation area		*/
Xint		inparse;	/* In parser flag			*/
Xlong		flags = 0L;	/* Command line flags			*/
Xchar		*null = "";
X
X				/* Current environment			*/
XEnviron	e = {
X    (char *)NULL,		/* Current line buffer			*/
X    (char *)NULL,		/* Current pointer in line		*/
X    (char *)NULL,		/* End of line pointer			*/
X    iostack,			/* I/O Stack pointers			*/
X    iostack - 1,
X    (int *)NULL,
X    FDBASE,			/* Base file handler			*/
X    (Environ *)NULL		/* Previous Env pointer			*/
X};
SHAR_EOF
echo "File shell/sh6.c is complete"
chmod 0644 shell/sh6.c || echo "restore of shell/sh6.c fails"
set `wc -c shell/sh6.c`;Sum=$1
if test "$Sum" != "3994"
then echo original size 3994, current size $Sum;fi
echo "x - extracting shell/sh7.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh7.c &&
X/* MS-DOS SHELL - Internal Command Processing
X *
X * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited and Charles Forsyth
X *
X * This code is based on (in part) the shell program written by Charles
X * Forsyth and is subject to the following copyright restrictions:
X *
X * 1.  Redistribution and use in source and binary forms are permitted
X *     provided that the above copyright notice is duplicated in the
X *     source form and the copyright notice in file sh6.c is displayed
X *     on entry to the program.
X *
X * 2.  The sources (or parts thereof) or objects generated from the sources
X *     (or parts of sources) cannot be sold under any circumstances.
X *
X *    $Header: sh7.c 1.1 90/01/29 17:46:25 MS_user Exp $
X *
X *    $Log:	sh7.c $
X * Revision 1.1  90/01/29  17:46:25  MS_user
X * Initial revision
X * 
X * 
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <process.h>
X#include <dos.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <ctype.h>
X#include <string.h>
X#include <unistd.h>
X#include <stdlib.h>
X#include <fcntl.h>
X#include <limits.h>
X#include <stdarg.h>
X#include "sh.h"
X
X#define	SECS		60L
X#define	MINS		3600L
X#define IS_OCTAL(a)	(((a) >= '0') && ((a) <= '7'))
X
X/* Definitions for test */
X
X#define END_OF_INPUT	0
X#define FILE_READABLE	1
X#define FILE_WRITABLE	2
X#define FILE_REGULAR	3
X#define FILE_DIRECTORY	4
X#define FILE_NONZERO	5
X#define FILE_TERMINAL	6
X#define STRING_ZERO	7
X#define STRING_NONZERO	8
X#define STRING_EQUAL	9
X#define STRING_NOTEQUAL	10
X#define NUMBER_EQUAL	11
X#define NUMBER_NOTEQUAL	12
X#define NUMBER_EQ_GREAT	13
X#define NUMBER_GREATER	14
X#define NUMBER_EQ_LESS	15
X#define NUMBER_LESS	16
X#define UNARY_NOT	17
X#define BINARY_AND	18
X#define BINARY_OR	19
X#define LPAREN		20
X#define RPAREN		21
X#define OPERAND		22
X#define FILE_EXECUTABLE	23
X#define FILE_USER	24
X#define FILE_GROUP	25
X#define FILE_TEXT	26
X#define FILE_BLOCK	27
X#define FILE_CHARACTER	28
X#define FILE_FIFO	29
X
X#define UNARY_OP	1
X#define BINARY_OP	2
X#define B_UNARY_OP	3
X#define B_BINARY_OP	4
X#define PAREN		5
X
Xstatic struct test_op {
X    char	*op_text;
X    short 	op_num;
X    short 	op_type;
X} test_ops[] = {
X    {"-r",	FILE_READABLE,		UNARY_OP},
X    {"-w",	FILE_WRITABLE,		UNARY_OP},
X    {"-x",	FILE_EXECUTABLE,	UNARY_OP},
X    {"-f",	FILE_REGULAR,		UNARY_OP},
X    {"-d",	FILE_DIRECTORY,		UNARY_OP},
X    {"-s",	FILE_NONZERO,		UNARY_OP},
X    {"-t",	FILE_TERMINAL,		UNARY_OP},
X    {"-z",	STRING_ZERO,		UNARY_OP},
X    {"-n",	STRING_NONZERO,		UNARY_OP},
X    {"=",	STRING_EQUAL,		BINARY_OP},
X    {"!=",	STRING_NOTEQUAL,	BINARY_OP},
X    {"-eq",	NUMBER_EQUAL,		BINARY_OP},
X    {"-ne",	NUMBER_NOTEQUAL,	BINARY_OP},
X    {"-ge",	NUMBER_EQ_GREAT,	BINARY_OP},
X    {"-gt",	NUMBER_GREATER,		BINARY_OP},
X    {"-le",	NUMBER_EQ_LESS,		BINARY_OP},
X    {"-lt",	NUMBER_LESS,		BINARY_OP},
X    {"!",	UNARY_NOT,		B_UNARY_OP},
X    {"-a",	BINARY_AND,		B_BINARY_OP},
X    {"-o",	BINARY_OR,		B_BINARY_OP},
X    {"(",	LPAREN,			PAREN},
X    {")",	RPAREN,			PAREN},
X#ifdef S_IFCHR
X    {"-c",	FILE_CHARACTER,		UNARY_OP},
X#endif
X#ifdef S_IFBLK
X    {"-b",	FILE_BLOCK,		UNARY_OP},
X#endif
X#ifdef S_ISUID
X    {"-u",	FILE_USER,		UNARY_OP},
X#endif
X#ifdef S_ISGID
X    {"-g",	FILE_GROUP,		UNARY_OP},
X#endif
X#ifdef S_ISVTX
X    {"-k",	FILE_TEXT,		UNARY_OP},
X#endif
X#ifdef S_IFIFO
X    {"-p",	FILE_FIFO,		UNARY_OP},
X#endif
X    {(char *)NULL,	NULL,		NULL}
X};
X
Xstatic int		expr (int);
Xstatic int		bexpr (int);
Xstatic int		primary (int);
Xstatic int		lex (char *);
Xstatic long		num (char *);
Xstatic void		syntax (void);
Xstatic int		dolabel (C_Op *);
Xstatic int		dochdir (C_Op *);
Xstatic int		dodrive (C_Op *);
Xstatic int		doshift (C_Op *);
Xstatic int		doumask (C_Op *);
Xstatic int		dodot (C_Op *);
Xstatic int		doecho (C_Op *);
Xstatic int		dogetopt (C_Op *);
Xstatic int		dopwd (C_Op *);
Xstatic int		doswap (C_Op *);
Xstatic int		dounset (C_Op *);
Xstatic int		dotype (C_Op *);
Xstatic int		dotest (C_Op *);
Xstatic int		dover (C_Op *);
Xstatic int		doread (C_Op *);
Xstatic int		doeval (C_Op *);
Xstatic int		dotrap (C_Op *);
Xstatic int		getsig (char *);
Xstatic int		dobreak (C_Op *);
Xstatic int		docontinue (C_Op *);
Xstatic int		brkcontin (char *, int);
Xstatic int		doexit (C_Op *);
Xstatic int		doexec (C_Op *);
Xstatic int		doreturn (C_Op *);
Xstatic int		doexport (C_Op *);
Xstatic int		domsdos (C_Op *);
Xstatic int		doreadonly (C_Op *);
Xstatic int		doset (C_Op *);
Xstatic int		dohistory (C_Op *);
Xstatic void		setsig (int, int (*)());
Xstatic int		rdexp (char **, int, char *);
Xstatic void		v1_putsn (char *, int);
X
Xstatic char		**test_alist;
Xstatic struct test_op	*test_op;
Xstatic jmp_buf		test_jmp;
X
X/*
X * built-in commands: doX
X */
X
Xstatic int	dolabel (t)
XC_Op		*t;
X{
X    return 0;
X}
X
X/*
X * Getopt - split arguments.  getopts pattern args
X */
X
Xstatic int	dogetopt (t)
Xregister C_Op	*t;
X{
X    int			argc;
X    char		**argv = t->words;
X    int			c;
X
X/* Count arguments */
X
X    optind = 1;				/* Reset the optind flag	*/
X    opterr = 1;				/* Reset the error flag		*/
X
X    for (argc = 0; t->words[argc] != (char *)NULL; argc++);
X
X    if (argc < 2)
X    {
X	S_puts ("usage: getopt legal-args $*\n");
X	return 2;
X    }
X
X    argc -= 2;
X    argv += 2;
X
X/* Scan each argument */
X
X    while ((c = getopt (argc, argv, t->words[1])) != EOF)
X    {
X	if (c == '?')
X	    return 2;
X
X	v1printf ("-%c ", c);
X
X/* Check for addition parameter */
X
X	if (*(strchr (t->words[1], c) + 1) == ':')
X	{
X	    v1_puts (optarg);
X	    v1_putc (SP);
X	}
X    }
X
X    v1_puts ("-- ");
X    argv += optind;
X
X    while (optind++ < argc)
X    {
X	v1_puts (*argv++);
X	v1_putc ((char)((optind == argc) ? NL : SP));
X    }
X
X    return 0;
X}
X
X/*
X * Echo the parameters
X */
X
Xstatic int	doecho (t)
Xregister C_Op	*t;
X{
X    int		n = 1;
X    int		no_eol = 0;		/* No EOL			*/
X    char	*ip;			/* Input pointer		*/
X    int		c_val;			/* Current character		*/
X    char	c;
X    bool	end_s;
X    char	*cp = e.linep;
X					/* Always leave room for NL	*/
X    char	*ep = &e.linep[LINE_MAX - 3];
X
X    while ((ip = t->words[n++]) != (char *)NULL)
X    {
X	if ((n == 2) && (strcmp (ip, "-n") == 0))
X	{
X	    no_eol++;
X	    continue;
X	}
X
X/* Process the string */
X
X	end_s = FALSE;
X
X	do
X	{
X
X/* Any special character processing ? */
X
X	    if ((c = *(ip++)) == '\\')
X	    {
X		if ((c_val = Process_Escape (&ip)) == -1)
X		{
X		    no_eol = 1;
X		    continue;
X		}
X
X		c = (char)c_val;
X	    }
X
X/* End of string - check to see if a space if required */
X
X	    else if (c == 0)
X	    {
X		end_s = TRUE;
X
X		if (t->words[n] != (char *)NULL)
X		    c = SP;
X
X		else
X		    continue;
X	    }
X
X/* Output the character */
X
X	    if (cp < ep)
X		*(cp++) = c;
X
X	    else
X	    {
X		v1_putsn (e.linep, (int)(cp - e.linep));
X		cp = e.linep;
X	    }
X
X	} while (!end_s);
X    }
X
X/* Is EOL required ? */
X
X    if (!no_eol)
X	*(cp++) = NL;
X
X/* Flush buffer */
X
X    if ((n = (int)(cp - e.linep)))
X	v1_putsn (e.linep, n);
X
X    return 0;
X}
X
X/*
X * Process_Escape - Convert an escaped character to a binary value.
X *
X * Returns the binary value and updates the string pointer.
X */
X
Xint	Process_Escape (cp)
Xchar	**cp;					/* Pointer to character */
X{
X    int		c_val = **cp;			/* Current character    */
X
X    if (c_val)
X        (*cp)++;
X
X/* Process escaped characters */
X
X    switch (c_val)
X    {
X        case 'b':			/* Backspace                    */
X            return 0x08;
X
X        case 'f':			/* Form Feed                    */
X            return 0x0c;
X
X        case 'v':			/* Vertical Tab                 */
X            return 0x0b;
X
X        case 'n':			/* New Line                     */
X            return 0x0a;
X
X        case 'r':			/* Carriage return              */
X            return 0x0d;
X
X        case 't':			/* Forward tab                  */
X	    return 0x09;
X
X        case '\\':			/* Backslash                    */
X	    return '\\';
X
X        case 'c':			/* no eol			*/
X	    return -1;
X    }
X
X/* Check for an octal string */
X
X    if ((c_val >= 0) && (c_val < 8))
X    {
X	while ((IS_OCTAL (**cp)))
X	    c_val = (c_val * 8) + *((*cp)++) - '0';
X
X	return c_val;
X    }
X
X    return c_val;
X}
X
X/*
X * Display the current version
X */
X
Xstatic int	dover (t)
XC_Op		*t;
X{
X    v1printf (Copy_Right1, _osmajor, _osminor);
X    v1a_puts (Copy_Right2);
X    return 0;
X}
X
Xstatic char	*swap_device[] = {"disk", "extend", "expand"};
X
X/*
X * Modify swapping information: swap options
X */
X
Xstatic int	doswap (t)
Xregister C_Op	*t;
X{
X    register int	n = 1;
X    char		*cp;
X
X/* Display current values ? */
X
X    if (t->words[1] == (char *)NULL)
X    {
X	if (Swap_Mode == SWAP_OFF)
X	    v1a_puts ("Swapping disabled");
X
X	else
X	{
X	    register int	j;
X
X	    v1_puts ("Swap devices: ");
X
X	    for (j = 0, n = 1; j < 3; ++j, n <<= 1)
X	    {
X		if (Swap_Mode & n)
X		{
X		    v1printf ("%s ", swap_device[j]);
X
X		    if (n == SWAP_EXTEND)
X			v1printf ("(0x%.6lx) ", SW_EMstart);
X		}
X	    }
X
X	    v1_putc (NL);
X	}
X
X	return 0;
X    }
X
X/* Set up new values */
X
X    Swap_Mode = SWAP_OFF;
X
X    while ((cp = t->words[n++]) != (char *)NULL)
X    {
X	if (strcmp (cp, "off") == 0)
X	    Swap_Mode = SWAP_OFF;
X
X	else if (strcmp (cp, "on") == 0)
X	    Swap_Mode = SWAP_DISK | SWAP_EXPAND | SWAP_EXTEND;
X
X/* Scan for valid arguments */
X
X	else
X	{
X	    register int	j, k;
X
X	    for (j = 0, k = 1; j < 3; ++j, k <<= 1)
X	    {
X		if (strcmp (cp, swap_device[j]) == 0)
X		{
X		    Swap_Mode |= k;
X
X/* If extended memory, they can specify the start address as a hex number */
X
X		    if (k == SWAP_EXTEND)
X		    {
X			char	*sp;
X			long	start;
X
X/* Check for not changed */
X
X			if ((sp = t->words[n]) == (char *)NULL)
X			    break;
X
X/* Convert hex number */
X
X			start = strtol (sp, &sp, 16);
X
X/* If not completely a hex number, ignore */
X
X			if (*sp)
X			    break;
X
X/* Set used and saved new value */
X
X			SW_EMstart = start;
X			++n;
X
X			if ((SW_EMstart < 0x100000L) ||
X			    (SW_EMstart > 0xf00000L))
X			    SW_EMstart = 0x100000L;
X
X			v1printf ("Extend memory start set to 0x%.6lx\n",
X				  SW_EMstart);
X		    }
X
X		    break;
X		}
X	    }
X	}
X    }
X
X    return 0;
X}
X
X/*
X * Output the current path: pwd
X */
X
Xstatic int	dopwd (t)
Xregister C_Op	*t;
X{
X    v1a_puts (C_dir->value);
X    return 0;
X}
X
X/*
X * Unset a variable: unset <flag..> <variable name...>
X */
X
Xstatic int	dounset (t)
Xregister C_Op	*t;
X{
X    register int	n = 1;
X
X    while (t->words[n] != (char *)NULL)
X        unset (t->words[n++], FALSE);
X
X    return 0;
X}
X
X/* Delete a variable or function.  If all is set, system variables can be
X * deleted.  This is used to delete the trap functions
X */
X
Xvoid		unset (cp, all)
Xregister char	*cp;
Xbool		all;
X{
X    register Var_List		*vp;
X    register Var_List		*pvp;
X
X/* Unset a flag */
X
X    if (*cp == '-')
X    {
X	while (*(++cp) != 0)
X	{
X	    if (islower (*cp))
X		FL_CLEAR (*cp);
X	}
X
X	setdash ();
X	return;
X    }
X
X/* Ok - unset a variable and not a local value */
X
X    if (!all && !(isalpha (*cp)))
X	return;
X
X/* Check in list */
X
X    pvp = (Var_List *)NULL;
X
X    for (vp = vlist; (vp != (Var_List *)NULL) && !eqname (vp->name, cp);
X	 vp = vp->next)
X	pvp = vp;
X
X/* If not found, delete the function if it exists */
X
X    if (vp == (Var_List *)NULL)
X    {
X	Fun_Ops 	*fp;
X
X	if ((fp = Fun_Search (cp)) != (Fun_Ops *)NULL)
X	    Save_Function (fp->tree, TRUE);
X
X	return;
X    }
X
X/* Error if read-only */
X
X    if (vp->status & (RONLY | PONLY))
X    {
X	if ((cp = strchr (vp->name, '=')) != (char *)NULL)
X	    *cp = 0;
X
X	S_puts (vp->name);
X
X	if (cp != (char *)NULL)
X	    *cp = '=';
X
X	S_puts ((vp->status & PONLY) ? ": cannot unset\n" : " is read-only\n");
X	return;
X    }
X
X/* Delete it */
X
X    if (vp->status & GETCELL)
X	DELETE (vp->name);
X
X    if (pvp == (Var_List *)NULL)
X	vlist = vp->next;
X
X    else
X	pvp->next = vp->next;
X
X    DELETE (vp);
X}
X
X/*
X * Execute a test: test <arguments>
X */
X
Xstatic int	dotest (t)
Xregister C_Op	*t;
X{
X    int		st = 0;
X
X    if (*(test_alist = &t->words[1]) == (char *)NULL)
X	return 1;
X
X/* If [ <arguments> ] form, check for end ] and remove it */
X
X    if (strcmp (t->words[0], "[") == 0)
X    {
X	while (t->words[++st] != (char *)NULL)
X	    ;
X
X	if (strcmp (t->words[--st], "]") != 0)
X	{
X	    print_error ("test: missing ']'\n");
X	    return 1;
X	}
X
X	else
X	    t->words[st] = (char *)NULL;
X    }
X
X/* Set abort address */
X
X    if (setjmp (test_jmp))
X	return 1;
X
X    st = !expr (lex (*test_alist));
X
X    if (*(++test_alist) != (char *)NULL)
X	syntax ();
X
X    return (st);
X}
X
Xstatic int	expr (n)
Xint		n;
X{
X    int		res;
X
X    if (n == END_OF_INPUT)
X	syntax ();
X
X    res = bexpr (n);
X
X    if (lex (*(++test_alist)) == BINARY_OR)
X	return expr (lex (*(++test_alist))) || res;
X
X    test_alist--;
X    return res;
X}
X
Xstatic int	bexpr (n)
Xint		n;
X{
X    int res;
X
X    if (n == END_OF_INPUT)
X	syntax ();
X
X    res = primary (n);
X    if (lex (*(++test_alist)) == BINARY_AND)
X	return bexpr (lex (*(++test_alist))) && res;
X
X    test_alist--;
X    return res;
X}
X
Xstatic int	primary (n)
Xint		n;
X{
X    register char	*opnd1, *opnd2;
X    struct stat		s;
X    int			res;
X
X    if (n == END_OF_INPUT)
X	syntax ();
X
X    if (n == UNARY_NOT)
X	return !expr (lex (*(++test_alist)));
X
X    if (n == LPAREN)
X    {
X	res = expr (lex (*(++test_alist)));
X
X	if (lex (*(++test_alist)) != RPAREN)
X	    syntax ();
X
X	return res;
X    }
X
X    if (n == OPERAND)
X    {
X	opnd1 = *test_alist;
X	(void) lex (*(++test_alist));
X
X	if ((test_op != (C_Op *)NULL) && test_op->op_type == BINARY_OP)
X	{
X	    struct test_op *op = test_op;
X
X	    if ((opnd2 = *(++test_alist)) == (char *)NULL)
X		syntax ();
X
X	    switch (op->op_num)
X	    {
X		case STRING_EQUAL:
X		    return strcmp (opnd1, opnd2) == 0;
X
X		case STRING_NOTEQUAL:
X		    return strcmp (opnd1, opnd2) != 0;
X
X		case NUMBER_EQUAL:
X		    return num (opnd1) == num (opnd2);
X
X		case NUMBER_NOTEQUAL:
X		    return num (opnd1) != num (opnd2);
X
X		case NUMBER_EQ_GREAT:
X		    return num (opnd1) >= num (opnd2);
X
X		case NUMBER_GREATER:
X		    return num (opnd1) > num (opnd2);
X
X		case NUMBER_EQ_LESS:
X		    return num (opnd1) <= num (opnd2);
X
X		case NUMBER_LESS:
X		    return num (opnd1) < num (opnd2);
X	    }
X	}
X
X	test_alist--;
X	return strlen (opnd1) > 0;
X    }
X
X/* unary expression */
X
X    if (test_op->op_type != UNARY_OP || *++test_alist == 0)
X	syntax ();
X
X    switch (n)
X    {
X	case STRING_ZERO:
X	    return strlen (*test_alist) == 0;
X
X	case STRING_NONZERO:
X	    return strlen (*test_alist) != 0;
X
X	case FILE_READABLE:
X	    return access (*test_alist, R_OK) == 0;
X
X	case FILE_WRITABLE:
X	    return access (*test_alist, W_OK) == 0;
X
X	case FILE_EXECUTABLE:
X	    return access (*test_alist, X_OK) == 0;
X
X	case FILE_REGULAR:
X	    return stat (*test_alist, &s) == 0 && S_ISREG(s.st_mode);
X
X	case FILE_DIRECTORY:
X	    return stat (*test_alist, &s) == 0 && S_ISDIR(s.st_mode);
X
X	case FILE_NONZERO:
X	    return stat (*test_alist, &s) == 0 && (s.st_size > 0L);
X
X	case FILE_TERMINAL:
X	    return isatty ((int)num (*test_alist));
X
X#ifdef S_ISUID
X	case FILE_USER:
X	    return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISUID);
X#endif
X
X#ifdef S_ISGID
X	case FILE_GROUP:
X	    return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISGID);
X#endif
X
X#ifdef S_ISVTX
X	case FILE_TEXT:
X	    return stat (*test_alist, &s) == 0 && (s.st_mode & S_ISVTX);
X#endif
X
X#ifdef S_IFBLK
X	case FILE_BLOCK:
X	    return stat (*test_alist, &s) == 0 && S_ISBLK(s.st_mode);
X#endif
X
X#ifdef S_IFCHR
X	case FILE_CHARACTER:
X	    return stat (*test_alist, &s) == 0 && S_ISCHR(s.st_mode);
X#endif
X
X#ifdef S_IFIFO
X	case FILE_FIFO:
X	    return stat (*test_alist, &s) == 0 && S_ISFIFO(s.st_mode);
X#endif
X    }
X}
X
Xstatic int	lex (s)
Xregister char	*s;
X{
X    register struct test_op	*op = test_ops;
X
X    if (s == (char *)NULL)
X	return END_OF_INPUT;
X
X    while (op->op_text)
X    {
X	if (strcmp (s, op->op_text) == 0)
X	{
X	    test_op = op;
X	    return op->op_num;
X	}
X
X	op++;
X    }
X
X    test_op = (struct test_op *)NULL;
X    return OPERAND;
X}
X
X/*
X * Get a long numeric value
X */
X
Xstatic long	num (s)
Xregister char	*s;
X{
X    char	*ep;
X    long	l = strtol (s, &ep, 10);
X
X    if (!*s || *ep)
X	syntax ();
X
X    return l;
X}
X
X/*
X * test syntax error - abort
X */
X
Xstatic void	syntax ()
X{
X    print_error ("test: syntax error\n");
X    longjmp (test_jmp, 1);
X}
X
X/*
X * Select a new drive: x:
X *
X * Select the drive, get the current directory and check that we have
X * actually selected the drive
X */
X
Xstatic int	dodrive (t)
Xregister C_Op	*t;
X{
X    unsigned int	cdrive;
X    unsigned int	ndrive = tolower (**t->words) - 'a' + 1;
X
X    _dos_setdrive (ndrive, &cdrive);
X    Getcwd ();
X    _dos_getdrive (&cdrive);
X    return (ndrive == cdrive) ? 0 : 1;
X}
X
X/*
X * Select a new directory: cd
X */
X
Xstatic int	dochdir (t)
Xregister C_Op	*t;
X{
X    char		*p;
X    char		*nd;
X    register char	*cp;
X    int			first = 0;
X    unsigned int	dummy;
X    unsigned int	cdrive;
X
X/* If restricted shell - illegal */
X
X    if (check_rsh ("cd"))
X	return 1;
X
X/* Use default ? */
X
X    if (((p = t->words[1]) == (char *)NULL) &&
X	((p = lookup (home, FALSE)->value) == null))
X    {
X	print_error ("cd: no home directory\n");
X	return 1;
X    }
X
X/* Save the current drive */
X
X    _dos_getdrive (&cdrive);
X
X/* Scan for the directory.  If there is not a / or : at start, use the
X * CDPATH variable
X */
X
X    cp = (*p == '/') ? null : lookup ("CDPATH", FALSE)->value;
X    cp = (*(p + 1) == ':') ? null : cp;
X
X    do
X    {
X	cp = path_append (cp, p, e.linep);
X
X/* Check for new disk drive */
X
X	nd = e.linep;
X
X	if (*(nd+ 1) == ':')
X	{
X	    _dos_setdrive (tolower (*nd) - 'a' + 1, &dummy);
X	    nd += 2;
X	}
X
X/* Was the change successful? */
X
X	if ((!*nd) || (chdir (nd) == 0))
X	{
X
X/* OK - reset the current directory (in the shell) and display the new
X * path if appropriate
X */
X
X	    Getcwd ();
X
X	    if (first || (strchr (p, '/') != (char *)NULL))
X		dopwd (t);
X
X	    return 0;
X	}
X
X	first = 1;
X
X    } while (cp != (char *)NULL);
X
X/* Restore our original drive and restore directory info */
X
X    _dos_setdrive (cdrive, &dummy);
X    Getcwd ();
X
X    print_error ("%s: bad directory\n", p);
X    return 1;
X}
X
X/*
X * Extract the next path from a string and build a new path from the
X * extracted path and a file name
X */
Xchar		*path_append (s1, s2, si)
Xregister char	*s1;			/* Path string			*/
Xregister char	*s2;			/* File name string		*/
Xchar		*si;			/* Output path			*/
X{
X    register char	*s;
X
X    s = si;
X
X    while (*s1 && *s1 != ';')
X	*s++ = *s1++;
X
X    if ((si != s) && (*(s - 1) != '/'))
X	*s++ = '/';
X
X    *s = '\0';
X
X    if (s2 != (char *)NULL)
X	strcpy (s, s2);
X
X    return (*s1 ? ++s1 : (char *)NULL);
X}
X
X/*
X * Execute a shift command: shift <n>
X */
X
Xstatic int	doshift (t)
Xregister C_Op	*t;
X{
X    register int	n;
X
X    n = (t->words[1] != (char *)NULL) ? getn (t->words[1]) : 1;
X
X    if (dolc < n)
X    {
X	print_error ("sh: nothing to shift\n");
X	return 1;
X    }
X
X    dolv[n] = dolv[0];
X    dolv += n;
X    dolc -= n;
X    setval (lookup ("#", TRUE), putn (dolc));
X    return 0;
X}
X
X/*
X * Execute a umask command: umask <n>
X */
X
Xstatic int	doumask (t)
Xregister C_Op	*t;
X{
X    register int	i;
X    register char	*cp;
X
X    if ((cp = t->words[1]) == (char *)NULL)
X    {
X	i = umask (0);
X	umask (i);
X	v1printf ("%o\n", i);
X    }
X
X    else
X    {
X	i = 0;
X	while (IS_OCTAL (*cp))
X	    i = i * 8 + (*(cp++) - '0');
X
X	umask (i);
X    }
X
X    return 0;
X}
X
X/*
X * Execute an exec command: exec <arguments>
X */
X
Xstatic int	doexec (t)
Xregister C_Op	*t;
X{
X    register int	i;
X    jmp_buf		ex;
X    int			*ofail;
X
X    t->ioact = (IO_Actions **)NULL;
X
X    for (i = 0; (t->words[i] = t->words[i + 1]) != (char *)NULL; i++)
X	;
X
X    if (i == 0)
X	return 0;
X
X    execflg = 1;
X    ofail = failpt;
X
X/* Set execute function recursive level to zero */
X
X    Execute_stack_depth = 0;
X
X    if (setjmp (failpt = ex) == 0)
X	execute (t, NOPIPE, NOPIPE, FEXEC);
X
X    failpt = ofail;
X    execflg = 0;
X    return 1;
X}
X
X/*
X * Execute a script in the current shell
X */
X
Xstatic int	dodot (t)
XC_Op		*t;
X{
X    register int	i;
X    register char	*sp;
X    char		*cp;
X
X    if ((cp = t->words[1]) == (char *)NULL)
X	return 0;
X
X    sp = any ('/', cp) ? null : path->value;
X
X    do
X    {
X	sp = path_append (sp, cp, e.linep);
X
X	if ((i = O_for_execute (e.linep)) >= 0)
X	{
X	    exstat = 0;
X	    next (remap (i));
X	    return exstat;
X	}
X    } while (sp != (char *)NULL);
X
X    print_error ("%s: not found\n", cp);
X    return 1;
X}
X
X/*
X * Read from standard input into a variable list
X */
X
Xstatic int	doread (t)
XC_Op		*t;
X{
X    register char	*cp, **wp;
X    register int	nb;
X
X    if (t->words[1] == (char *)NULL)
X    {
X	print_error ("Usage: read name ...\n");
X	return 1;
X    }
X
X    for (wp = t->words + 1; *wp != (char *)NULL; wp++)
X    {
X	for (cp = e.linep; cp < e.eline - 1; cp++)
X	{
X	    if (((nb = read (STDIN_FILENO, cp, 1)) != 1) || (*cp == NL) ||
X		((wp[1] != (char *)NULL) && any (*cp, ifs->value)))
X
X	    break;
X	}
X
X	*cp = 0;
X
X	if (nb <= 0)
X	    break;
X
X	setval (lookup (*wp, TRUE), e.linep);
X    }
X
X    return (nb <= 0);
X}
X
X/*
X * Evaluate an expression
X */
X
Xstatic int	doeval (t)
Xregister C_Op	*t;
X{
X    return RUN (awordlist, t->words + 1, wdchar);
X}
X
X/*
X * Execute a trap
X */
X
Xstatic int	dotrap (t)
Xregister C_Op	*t;
X{
X    register int	n, i;
X    register int	resetsig;
X    char		tval[10];
X    char		*cp;
X
X
X    if (t->words[1] == (char *)NULL)
X    {
X
X/* Display trap - look up each trap and print those we find */
X
X	for (i = 0; i < NSIG; i++)
X	{
X	    sprintf (tval, "~%d", i);
X
X	    if ((cp = lookup (tval, FALSE)->value) != null)
X	    {
X		v1printf ("%u: ", i);
X		v1a_puts (cp);
X	    }
X	}
X
X	return 0;
X    }
X
X    resetsig = isdigit (*t->words[1]);		/* Reset signal?	*/
X
X    for (i = resetsig ? 1 : 2; t->words[i] != (char *)NULL; ++i)
X    {
X
X/* Generate the variable name */
X
X	sprintf (tval, "~%d", (n = getsig (t->words[i])));
X
X	if (n == -1)
X	    return 1;
X
X	unset (tval, TRUE);
X
X/* Re-define signal processing */
X
X	if (!resetsig)
X	{
X	    if (*t->words[1] != '\0')
X	    {
X		setval (lookup (tval, TRUE), t->words[1]);
X		setsig (n, sig);
X	    }
X
X	    else
X		setsig (n, SIG_IGN);
X	}
X
X/* Clear signal processing */
X
X	else if (talking)
X	{
X	    if (n == SIGINT)
X		setsig (n, onintr);
X
X	    else
X#ifdef SIGQUIT
X		setsig (n, n == SIGQUIT ? SIG_IGN : SIG_DFL);
X#else
X		setsig (n, SIG_DFL);
X#endif
X	}
X
X	else
X	    setsig (n, SIG_DFL);
X    }
X
X    return 0;
X}
X
X/*
X * Get a signal number
X */
X
Xstatic int	getsig (s)
Xchar		*s;
X{
X    register int	n;
X
X    if (((n = getn (s)) < 0) || (n >= NSIG))
X    {
X	print_error ("trap: bad signal number\n");
X	n = -1;
X    }
X
X    return n;
X}
X
X/*
X * Set up a signal function
X */
X
Xstatic void	setsig (n, f)
Xregister int	n;
Xint		(*f)();
X{
X    if (n == 0)
X	return;
X
X    if ((signal (n, SIG_IGN) != SIG_IGN) || (ourtrap & (1L << n)))
X    {
X	ourtrap |= (1L << n);
X	signal (n, f);
X    }
X}
X
X/* Convert a string to a number */
X
Xint	getn (as)
Xchar	*as;
X{
X    char	*s;
X    int		n = (int)strtol (as, &s, 10);
X
X    if (*s)
X	print_error ("%s: bad number\n", as);
X
X    return n;
X}
X
X/*
X * BREAK and CONTINUE processing
X */
X
Xstatic int	dobreak (t)
XC_Op		*t;
X{
X    return brkcontin (t->words[1], BC_BREAK);
X}
X
Xstatic int	docontinue (t)
XC_Op		*t;
X{
X    return brkcontin (t->words[1], BC_CONTINUE);
X}
X
Xstatic int	brkcontin (cp, val)
Xregister char	*cp;
Xint		val;
X{
X    register Break_C	*Break_Loc;
X    register int	nl;
X
X    if ((nl = (cp == (char *)NULL) ? 1 : getn (cp)) <= 0)
X	nl = 999;
X
X    do
X    {
X	if ((Break_Loc = Break_List) == (Break_C *)NULL)
X	    break;
X
X	Break_List = Break_Loc->nextlev;
X
X    } while (--nl);
X
X    if (nl)
X    {
X	print_error ("sh: bad break/continue level\n");
X	return 1;
X    }
X
X    longjmp (Break_Loc->brkpt, val);
X
X/* NOTREACHED */
X}
X
X/*
X * Exit function
X */
X
Xstatic int	doexit (t)
XC_Op		*t;
X{
X    Break_C	*SShell_Loc = SShell_List;
X
X    execflg = 0;
X
X/* Set up error codes */
X
X    if (t->words[1] != (char *)NULL)
X    {
X	exstat = getn (t->words[1]);
X	setval (lookup ("?", TRUE), t->words[1]);
X    }
X
X/* Are we in a subshell.  Yes - do a longjmp instead of an exit */
X
X    if (SShell_Loc != (Break_C *)NULL)
X    {
X	SShell_List = SShell_Loc->nextlev;
X	longjmp (SShell_Loc->brkpt, 1);
X    }
X
X    leave ();
X    return 1;
X}
X
X/*
X * Function return - set exit value and return via a long jmp
X */
X
Xstatic int	doreturn (t)
XC_Op		*t;
X{
X    Break_C	*Return_Loc = Return_List;
X
X    if  (t->words[1] != (char *)NULL)
X	setval (lookup ("?", TRUE), t->words[1]);
X
X/* If the return address is defined - return to it.  Otherwise, return
X * the value
X */
X
X    if (Return_Loc != (Break_C *)NULL)
X    {
X	Return_List = Return_Loc->nextlev;
X	longjmp (Return_Loc->brkpt, 1);
X    }
X
X    return getn (t->words[1]);
X}
X
X/*
X * MSDOS, EXPORT and READONLY functions
X */
X
Xstatic int	doexport (t)
XC_Op		*t;
X{
X    return rdexp (t->words + 1, EXPORT, "export ");
X}
X
Xstatic int	doreadonly (t)
XC_Op		 *t;
X{
X    return rdexp (t->words + 1, RONLY, "readonly ");
X}
X
Xstatic int	domsdos (t)
XC_Op		*t;
X{
X    return rdexp (t->words + 1, C_MSDOS, "msdos ");
X}
X
Xstatic int	rdexp (wp, key, tstring)
Xregister char	**wp;
Xint		key;
Xchar		*tstring;
X{
X    char	*cp;
X    bool	valid;
X
X    if (*wp != (char *)NULL)
X    {
X	for (; *wp != (char *)NULL; wp++)
X	{
X	    cp = *wp;
X	    valid = TRUE;
X
X/* Check for a valid name */
X
X	    if (!isalpha (*(cp++)))
X		valid = FALSE;
X
X	    else
X	    {
X		while (*cp)
X		{
X		    if (!isalnum (*(cp++)))
X		    {
X			valid = FALSE;
X			break;
X		    }
X		}
X	    }
X
X/* If valid - update, otherwise print a message */
X
X	    if (valid)
X		s_vstatus (lookup (*wp, TRUE), key);
X
X	    else
X		print_error ("%s: bad identifier\n", *wp);
X	}
X    }
X
X    else
X    {
X	register Var_List	*vp;
X
X	for (vp = vlist; vp != (Var_List *) NULL; vp = vp->next)
X	{
X	    if ((vp->status & key) && isalpha (*vp->name))
X	    {
X		v1_puts (tstring);
X		v1_putsn (vp->name, (int)(findeq (vp->name) - vp->name));
X		v1_putc (NL);
X	    }
X	}
X    }
X
X    return 0;
X}
X
X/*
X * Sort Compare function for displaying variables
X */
X
Xint	sort_compare (s1, s2)
Xchar	**s1;
Xchar	**s2;
X{
X    return strcmp (*s1, *s2);
X}
X
X/*
X * Set function
X */
X
Xstatic int	doset (t)
Xregister C_Op	*t;
X{
X    register Var_List	*vp;
X    register char	*cp;
X    register int	n, j;
X    Fun_Ops		*fp;
X    char		sign;
X    char		**list;
X
X/* Display ? */
X
X    if ((cp = t->words[1]) == (char *)NULL)
X    {
X
X/* Count the number of entries to print */
X
X	for (n = 0, vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
X	{
X	    if (isalnum (*vp->name))
X		n++;
X	}
X
X/* Build a local array of name */
X
X	list = (char **)space (sizeof (char *) * n);
X
X	for (n = 0, vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
X	{
X	    if (isalnum (*vp->name))
X	    {
X		if (list == (char **)NULL)
X		    v1a_puts (vp->name);
X
X		else
X		    list[n++] = vp->name;
X	    }
X	}
X
X/* Sort them and then print */
X
X	if (list != (char **)NULL)
X	{
X	    qsort (list, n, sizeof (char *), sort_compare);
X
X	    for (j = 0; j < n; j++)
X		v1a_puts (list[j]);
X
X	    DELETE (list);
X	}
X
X/* Print the list of functions */
X
X	for (fp = fun_list; fp != (Fun_Ops *)NULL; fp = fp->next)
X	    Print_ExTree (fp->tree);
X
X	return 0;
X    }
X
X/* Set/Unset a flag ? */
X
X    if (((sign = *cp) == '-') || (*cp == '+'))
X    {
X	for (n = 0; (t->words[n] = t->words[n + 1]) != (char *)NULL; n++)
X	    ;
X
X	for (; *cp; cp++)
X	{
X	    if (*cp == 'r')
X	    {
X		print_error ("set: -r bad option\n");
X		return 1;
X	    }
X
X	    if (*cp == 'e')
X	    {
X		if (!talking)
X		{
X		    if (sign == '-')
X			FL_SET ('e');
X
X		    else
X			FL_CLEAR ('e');
X		}
X	    }
X
X	    else if (islower (*cp))
X	    {
X		if (sign == '-')
X		    FL_SET (*cp);
X
X		else
X		    FL_CLEAR (*cp);
X	    }
X	}
X
X	setdash ();
X    }
X
X/* Set up parameters ? */
X
X    if (t->words[1])
X    {
X	t->words[0] = dolv[0];
X
X	for (n = 1; t->words[n] != (char *)NULL; n++)
X	    setarea ((char *)t->words[n], 0);
X
X	dolc = n-1;
X	dolv = t->words;
X	setval (lookup ("#", TRUE), putn (dolc));
X	setarea ((char *)(dolv - 1), 0);
X    }
X
X    return 0;
X}
X
X/*
X * History functions - display, initialise, enable, disable
X */
X
Xstatic int	dohistory (t)
XC_Op		*t;
X{
X    char	*cp;
X
X    if (!talking)
X	return 1;
X
X    if ((cp = t->words[1]) == (char *)NULL)
X	Display_History ();
X
X    else if (strcmp (cp, "-i") == 0)
X	Clear_History ();
X
X    else if (strcmp (cp, "-d") == 0)
X	History_Enabled = FALSE;
X
X    else if (strcmp (cp, "-e") == 0)
X	History_Enabled = TRUE;
X
X    return 0;
X}
X
X/*
X * Type fucntion: For each name, indicate how it would be interpreted
X */
X
Xstatic char	*type_ext[] = {
X    "", ".exe", ".com", ".sh"
X};
X
Xstatic int	dotype (t)
Xregister C_Op	*t;
X{
X    register char	*sp;			/* Path pointers	*/
X    char		*cp;
X    char		*ep;
X    char		*xp;			/* In file name pointers */
X    char		*xp1;
X    int			n = 1;			/* Argument count	*/
X    int			i, fp;	
X    bool		found;			/* Found flag		*/
X
X    while ((cp = t->words[n++]) != (char *)NULL)
X    {
X	sp = any ('/', cp) ? null : path->value;
X	found = FALSE;
X
X	do
X	{
X	    sp = path_append (sp, cp, e.linep);
X	    ep = &e.linep[strlen (e.linep)];
X
X/* Get start of file name */
X
X	    if ((xp1 = strrchr (e.linep, '/')) == (char *)NULL)
X		xp1 = e.linep;
X	    
X	    else
X		++xp1;
X
X/* Look up all 4 types */
X
X	    for (i = 0; (i < 4) && !found; i++)
X	    {
X		strcpy (ep, type_ext[i]);
X
X		if (access (e.linep, F_OK) == 0)
X		{
X
X/* If no extension or .sh extension, check for shell script */
X
X		    if (((xp = strchr (xp1, '.')) == (char *)NULL) ||
X			(stricmp (xp, ".sh") == 0))
X		    {
X			if ((fp = Check_Script (e.linep)) < 0)
X			    continue;
X
X			S_close (fp, TRUE);
X		    }
X
X		    else if ((stricmp (xp, ".exe") != 0) &&
X			     (stricmp (xp, ".com") != 0))
X			continue;
X
X		    print_error ("%s is %s\n", cp, e.linep);
X		    found = TRUE;
X		}
X	    }
X	} while ((sp != (char *)NULL) && !found);
X
X	if (!found)
X	    print_error ("%s not found\n", cp);
X    }
X
X    return 0;
X}
X
X/* Table of internal commands */
X
Xstatic struct	builtin	builtin[] = {
X	".",		dodot,
X	":",		dolabel,
X	"[",		dotest,
X	"break",	dobreak,
X	"cd",		dochdir,
X	"continue",	docontinue,
X	"echo",		doecho,
X	"eval",		doeval,
X	"exec",		doexec,
X	"exit",		doexit,
X	"export",	doexport,
X	"getopt",	dogetopt,
X	"history",	dohistory,
X	"msdos",	domsdos,
X	"pwd",		dopwd,
X	"read",		doread,
X	"readonly",	doreadonly,
X	"return",	doreturn,
X	"set",		doset,
X	"shift",	doshift,
X	"swap",		doswap,
X	"test",		dotest,
X	"trap",		dotrap,
X	"type",		dotype,
X	"umask",	doumask,
X	"unset",	dounset,
X	"ver",		dover,
X	(char *)NULL,
X};
X
X/*
X * Look up a built in command
X */
X
Xint		(*inbuilt (s))()
Xregister char	*s;
X{
X    register struct builtin	*bp;
X
X    if ((strlen (s) == 2) && isalpha (*s) && (*s != '_') && (*(s + 1) == ':'))
X	return dodrive;
X
X    for (bp = builtin; bp->command != (char *)NULL; bp++)
X    {
X	if (stricmp (bp->command, s) == 0)
X	    return bp->fn;
X    }
X
X    return NULL;
X}
X
X/* Write to stdout functions - printf, fputs, fputc, and a special */
X
X/*
X * Equivalent of printf without using streams
X */
X
Xvoid	v1printf (fmt)
Xchar	*fmt;
X{
X    va_list	ap;
X    char	x[100];
X
X    va_start (ap, fmt);
X    vsprintf (x, fmt, ap);
X    v1_puts (x);
X    va_end (ap);
X}
X
X/*
X * Write string to STDOUT
X */
X
Xvoid		v1_puts (s)
Xchar		*s;
X{
X    write (STDOUT_FILENO, s, strlen (s));
X}
X
X/*
X * Write string to STDOUT with a NL at end
X */
X
Xvoid		v1a_puts (s)
Xchar		*s;
X{
X    char	c = NL;
X
X    write (STDOUT_FILENO, s, strlen (s));
X    write (STDOUT_FILENO, &c, 1);
X}
X
X/*
X * Write n characters to STDOUT
X */
X
Xstatic void	v1_putsn (s, n)
Xchar		*s;
Xint		n;
X{
X    write (STDOUT_FILENO, s, n);
X}
X
X/*
X * Write 1 character to STDOUT
X */
X
Xvoid		v1_putc (c)
Xchar		c;
X{
X    write (STDOUT_FILENO, &c, 1);
X}
SHAR_EOF
chmod 0644 shell/sh7.c || echo "restore of shell/sh7.c fails"
set `wc -c shell/sh7.c`;Sum=$1
if test "$Sum" != "30980"
then echo original size 30980, current size $Sum;fi
echo "x - extracting shell/sh8.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh8.c &&
X/* MS-DOS SHELL - Unix File I/O Emulation
X *
X * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited
X *
X * This code is subject to the following copyright restrictions:
X *
X * 1.  Redistribution and use in source and binary forms are permitted
X *     provided that the above copyright notice is duplicated in the
X *     source form and the copyright notice in file sh6.c is displayed
X *     on entry to the program.
X *
X * 2.  The sources (or parts thereof) or objects generated from the sources
X *     (or parts of sources) cannot be sold under any circumstances.
X *
X *    $Header: sh8.c 1.1 90/01/29 17:46:37 MS_user Exp $
X *
X *    $Log:	sh8.c $
X * Revision 1.1  90/01/29  17:46:37  MS_user
X * Initial revision
X * 
X * 
X */
X
X#include <sys/types.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <stdlib.h>
X#include <fcntl.h>
X#include <io.h>
X#include <stdarg.h>
X#include <string.h>
X#include <unistd.h>
X#include <limits.h>
X#include "sh.h"
X
X#define F_START		4
X
Xstatic char		*nopipe = "can't create pipe - try again\n";
X
X/* List of open files to allow us to simulate the Unix open and unlink
X * operation for temporary files
X */
X
Xtypedef struct flist {
X    struct flist	*fl_next;	/* Next link			*/
X    char		*fl_name;	/* File name			*/
X    bool		fl_close;	/* Delete on close flag		*/
X    int			fl_size;	/* Size of fl_fd array		*/
X    int			fl_count;	/* Number of entries in array	*/
X    int			fl_mode;	/* File open mode		*/
X    int			*fl_fd;		/* File ID array (for dup)	*/
X} s_flist;
X
Xstatic s_flist		*list_start = (s_flist *)NULL;
Xstatic s_flist		*find_entry (int);
X
X/*
X * Open a file and add it to the Open file list.  Errors are the same as
X * for a normal open.
X */
X
Xint	S_open (d_flag, name, mode)
Xbool	d_flag;
Xchar	*name;
Xint	mode;
X{
X    va_list		ap;
X    int			pmask;
X    s_flist		*fp = (struct s_flist *)NULL;
X    int			*f_list = (int *)NULL;
X    char		*f_name = (char *)NULL;
X
X/* Check the permission mask if it exists */
X
X    va_start (ap, mode);
X    pmask =  va_arg (ap, int);
X    va_end (ap);
X
X/* Grap some space.  If it fails, free space and return an error */
X
X    if (((fp = (s_flist *) space (sizeof (s_flist))) == (s_flist *)NULL) ||
X	((f_list = (int *) space (sizeof (int) * F_START)) == (int *)NULL) ||
X	((f_name = strsave (name, 0)) == null))
X    {
X	if (f_list == (int *)NULL)
X	    DELETE (f_list);
X
X	if (fp == (s_flist *)NULL)
X	    DELETE (fp);
X
X	errno = ENOMEM;
X	return -1;
X    }
X
X/* Set up the structure */
X
X    fp->fl_name  = strcpy (f_name, name);
X    fp->fl_close = d_flag;
X    fp->fl_size  = F_START;
X    fp->fl_count = 1;
X    fp->fl_fd    = f_list;
X    fp->fl_mode  = mode;
X
X/* Open the file */
X
X    if ((fp->fl_fd[0] = open (name, mode, pmask)) < 0)
X    {
X	pmask = errno;
X	DELETE (f_name);
X	DELETE (f_list);
X	DELETE (fp);
X	errno = pmask;
X	return -1;
X    }
X
X/* Make sure everything is in area 0 */
X
X    setarea ((char *)fp, 0);
X    setarea ((char *)f_list, 0);
X
X/* List into the list */
X
X    fp->fl_next   = list_start;
X    list_start = fp;
X
X/* Return the file descriptor */
X
X    return fp->fl_fd[0];
X}
X
X/*
X * Scan the File list for the appropriate entry for the specified ID
X */
X
Xstatic s_flist		*find_entry (fid)
Xint			fid;
X{
X    s_flist	*fp = list_start;
X    int		i;
X
X    while (fp != (s_flist *)NULL)
X    {
X	for (i = 0; i < fp->fl_count; i++)
X	{
X	    if (fp->fl_fd[i] == fid)
X		return fp;
X	}
X
X	fp = fp->fl_next;
X    }
X
X    return (s_flist *)NULL;
X}
X
X/* Close the file
X *
X * We need a version of close that does everything but close for dup2 as
X * new file id is closed.  If c_flag is TRUE, close the file as well.
X */
X
Xint	S_close (fid, c_flag)
Xint	fid;
Xbool	c_flag;
X{
X    s_flist	*fp = find_entry (fid);
X    s_flist	*last = (s_flist *)NULL;
X    s_flist	*fp1 = list_start;
X    int		i, serrno;
X    bool	release = TRUE;
X    bool	delete = FALSE;
X    char	*fname;
X
X/* Find the entry for this ID */
X
X    if (fp != (s_flist *)NULL)
X    {
X	for (i = 0; i < fp->fl_count; i++)
X	{
X	    if (fp->fl_fd[i] == fid)
X		fp->fl_fd[i] = -1;
X
X	    if (fp->fl_fd[i] != -1)
X		release = FALSE;
X	}
X
X/* Are all the Fids closed ? */
X
X	if (release)
X	{
X	    fname = fp->fl_name;
X	    delete = fp->fl_close;
X	    DELETE (fp->fl_fd);
X
X/* Scan the list and remove the entry */
X
X	    while (fp1 != (s_flist *)NULL)
X	    {
X		if (fp1 != fp)
X		{
X		    last = fp1;
X		    fp1 = fp1->fl_next;
X		    continue;
X		}
X
X		if (last == (s_flist *)NULL)
X		    list_start = fp->fl_next;
X
X		else
X		    last->fl_next = fp->fl_next;
X
X		break;
X	    }
X
X/* OK - delete the area */
X
X	    DELETE (fp);
X	}
X    }
X
X/* Close the file anyway */
X
X    if (c_flag)
X    {
X	i = close (fid);
X	serrno = errno;
X    }
X
X/* Delete the file ? */
X
X    if (delete)
X    {
X	unlink (fname);
X	DELETE (fname);
X    }
X
X/* Restore results and error code */
X
X    errno = serrno;
X    return i;
X}
X
X/*
X * Duplicate file handler.  Add the new handler to the ID array for this
X * file.
X */
X
Xint	S_dup (old_fid)
Xint	old_fid;
X{
X    int		new_fid;
X
X    if ((new_fid = dup (old_fid)) >= 0)
X	S_Remap (old_fid, new_fid);
X
X    return new_fid;
X}
X
X/*
X * Add the ID to the ID array for this file
X */
X
Xvoid	S_Remap (old_fid, new_fid)
Xint	old_fid, new_fid;
X{
X    s_flist	*fp = find_entry (old_fid);
X    int		*flist;
X    int		i;
X
X    if (fp == (s_flist *)NULL)
X	return;
X
X/* Is there an empty slot ? */
X
X    for (i = 0; i < fp->fl_count; i++)
X    {
X	if (fp->fl_fd[i] == -1)
X	{
X	    fp->fl_fd[i] = new_fid;
X	    return;
X	}
X    }
X
X/* Is there any room at the end ? No - grap somemore space and effect a
X * re-alloc.  What to do if the re-alloc fails - should really get here.
X * Safty check only??
X */
X
X    if (fp->fl_count == fp->fl_size)
X    {
X	if ((flist = (int *) space ((fp->fl_size + F_START) * sizeof (int)))
X	    == (int *)NULL)
X	    return;
X
X	memcpy ((char *)flist, (char *)fp->fl_fd, sizeof (int) * fp->fl_size);
X	DELETE (fp->fl_fd);
X
X	fp->fl_fd   = flist;
X	fp->fl_size += F_START;
X    }
X
X    fp->fl_fd[fp->fl_count++] = new_fid;
X}
X
X/*
X * Set Delete on Close flag
X */
X
Xvoid	S_Delete (fid)
Xint	fid;
X{
X    s_flist	*fp = find_entry (fid);
X
X    if (fp != (s_flist *)NULL)
X	fp->fl_close = TRUE;
X}
X
X/*
X * Duplicate file handler onto specific handler
X */
X
Xint	S_dup2 (old_fid, new_fid)
Xint	old_fid;
Xint	new_fid;
X{
X    int		res = 0;
X    int		i;
X    Save_IO	*sp;
X
X/* If duping onto stdin, stdout or stderr, Search the Save IO stack for an
X * entry matching us
X */
X
X    if ((new_fid >= STDIN_FILENO) && (new_fid <= STDERR_FILENO))
X    {
X	for (sp = SSave_IO, i = 0; (i < NSave_IO_E) &&
X				   (SSave_IO[i].depth < Execute_stack_depth);
X	     i++);
X
X/* If depth is greater the Execute_stack_depth - we should panic as this
X * should not happen.  However, for the moment, I'll ignore it
X */
X
X/* If there an entry for this depth ? */
X
X	if (i == NSave_IO_E)
X	{
X
X/* Do we need more space? */
X
X	    if (NSave_IO_E == MSave_IO_E)
X	    {
X		sp = (Save_IO *)space ((MSave_IO_E + SSAVE_IO_SIZE) * sizeof (Save_IO));
X
X/* Check for error */
X
X		if (sp == (Save_IO *)NULL)
X		{
X		    errno = ENOMEM;
X		    return -1;
X		}
X
X/* Save original data */
X
X		if (MSave_IO_E != 0)
X		{
X		    memcpy (sp, SSave_IO, sizeof (Save_IO) * MSave_IO_E);
X		    DELETE (SSave_IO);
X		}
X
X		setarea ((char *)sp, 1);
X		SSave_IO = sp;
X		MSave_IO_E += SSAVE_IO_SIZE;
X	    }
X
X/* Initialise the new entry */
X
X	    sp = &SSave_IO[NSave_IO_E++];
X	    sp->depth             = Execute_stack_depth;
X	    sp->fp[STDIN_FILENO]  = -1;
X	    sp->fp[STDOUT_FILENO] = -1;
X	    sp->fp[STDERR_FILENO] = -1;
X	}
X
X	if (sp->fp[new_fid] == -1)
X	    sp->fp[new_fid] = remap (new_fid);
X    }
X
X/* OK - Dup the descriptor */
X
X    if ((old_fid != -1) && ((res = dup2 (old_fid, new_fid)) >= 0))
X    {
X	S_close (new_fid, FALSE);
X	S_Remap (old_fid, new_fid);
X    }
X
X    return res;
X}
X
X/*
X * Restore the Stdin, Stdout and Stderr to original values
X */
X
Xint	restore_std (rv)
Xint	rv;
X{
X    int		j, i;
X    Save_IO	*sp;
X
X/* Start at the top and remove any entries above the current execute stack
X * depth
X */
X
X    for (j = NSave_IO_E; j > 0; j--)
X    {
X       sp = &SSave_IO[j - 1];
X
X       if (sp->depth < Execute_stack_depth)
X	   break;
X
X/* Reduce number of entries */
X
X	--NSave_IO_E;
X
X/* Close and restore any files */
X
X	for (i = STDIN_FILENO; i <= STDERR_FILENO; i++)
X	{
X	    if (sp->fp[i] != -1)
X	    {
X		S_close (i, TRUE);
X		dup2 (sp->fp[i], i);
X		S_close (sp->fp[i], TRUE);
X	    }
X	}
X    }
X
X    return rv;
X}
X
X/*
X * Create a Pipe
X */
X
Xint		openpipe ()
X{
X    register int	i;
X
X    if ((i = S_open (TRUE, g_tempname (), O_PMASK, 0600)) < 0)
X	print_error (nopipe);
X
X    return i;
X}
X
X/*
X * Close a pipe
X */
X
Xvoid		closepipe (pv)
Xregister int	pv;
X{
X    if (pv != -1)
X	S_close (pv, TRUE);
X}
X
X/*
X * Write a character to STDERR
X */
X
Xvoid	S_putc (c)
Xint	c;
X{
X    write (STDERR_FILENO, (char *)&c, 1);
X}
X
X/*
X * Write a string to STDERR
X */
X
Xvoid	S_puts (s)
Xchar	*s;
X{
X    write (STDERR_FILENO, s, strlen (s));
X}
X
X/*
X * Check for restricted shell
X */
X
Xbool	check_rsh (s)
Xchar	*s;
X{
X    if (r_flag)
X    {
X	print_error ("%s: restricted\n", s);
X	return TRUE;
X    }
X
X    return FALSE;
X}
X
X/*
X * Check to see if a file is a shell script.  If it is, return the file
X * handler for the file
X */
X
Xint	O_for_execute (path)
Xchar	*path;
X{
X    int		i, end;
X    char	local_path[FFNAME_MAX];
X
X/* Work on a copy of the path */
X
X    strcpy (local_path, path);
X
X/* Try the file name and then with a .sh appended */
X
X    for (end = 0; end < 2; end++)
X    {
X	if ((i = Check_Script (local_path)) >= 0)
X	    return i;
X
X	if (!end)
X	    strcat (local_path, ".sh");
X    }
X
X    return -1;
X}
X
X/*
X * Check for shell script
X */
X
Xint	Check_Script (path)
Xchar	*path;
X{
X    char	buf[5];
X    int		i;
X
X    if (((i = S_open (FALSE, path, O_RMASK)) >= 0) &&
X	((read (i, buf, 6) == 5) && (strncmp (buf, "#!sh\n", 5) == 0)))
X	return i;
X
X    if (i != -1)
X	S_close (i, TRUE);
X
X    return -1;
X}
X
X/*
X * Convert slashes to backslashes for MSDOS
X */
X
Xvoid	Convert_Slashes (sp)
Xchar	*sp;
X{
X    while (*sp)
X    {
X	if (*sp == '/')
X	    *sp = '\\';
X
X	++sp;
X    }
X}
SHAR_EOF
chmod 0644 shell/sh8.c || echo "restore of shell/sh8.c fails"
set `wc -c shell/sh8.c`;Sum=$1
if test "$Sum" != "9874"
then echo original size 9874, current size $Sum;fi
echo "x - extracting shell/sh9.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh9.c &&
X/* MS-DOS SHELL - History Processing
X *
X * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited
X *
X * This code is subject to the following copyright restrictions:
X *
X * 1.  Redistribution and use in source and binary forms are permitted
X *     provided that the above copyright notice is duplicated in the
X *     source form and the copyright notice in file sh6.c is displayed
X *     on entry to the program.
X *
X * 2.  The sources (or parts thereof) or objects generated from the sources
X *     (or parts of sources) cannot be sold under any circumstances.
X *
X *    $Header: sh9.c 1.1 90/01/26 17:25:19 MS_user Exp $
X *
X *    $Log:	sh9.c $
X * Revision 1.1  90/01/26  17:25:19  MS_user
X * Initial revision
X * 
X * 
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <conio.h>
X#include <string.h>
X#include <memory.h>
X#include <ctype.h>
X#include <signal.h>
X#include <stdlib.h>
X#include <stddef.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <limits.h>
X#include <dos.h>
X#include <unistd.h>
X#include "sh.h"
X
Xstatic bool	alpha_numeric (int);
Xstatic bool	function (int);
Xstatic bool	Process_History (int);
Xstatic bool	Scan_History (void);
Xstatic void	Redisplay_Line (void);
Xstatic void	Process_Stdin (void);
Xstatic void	Page_History (int);
Xstatic bool	UpDate_CLine (char *);
Xstatic bool	Re_start (char *);
Xstatic void	memrcpy (char *, char *, int);
Xstatic int	read_cursor_position (void);
Xstatic void	set_cursor_position (int);
Xstatic void	gen_cursor_position (void);
X
Xstatic bool	insert_mode = FALSE;
Xstatic char	*c_buffer_pos;		/* Position in command line	*/
Xstatic char	*end_buffer;		/* End of command line		*/
Xstatic int	s_cursor;		/* Start cursor position	*/
Xstatic int	c_history = -1;		/* Current entry		*/
Xstatic int	l_history = 0;		/* End of history array		*/
Xstatic int	M_length = -1;		/* Match length			*/
Xstatic char	l_buffer[LINE_MAX + 1];
Xstatic char	*No_prehistory   = "history: No previous commands\033[2K\n";
Xstatic char	*No_MatchHistory = "history: No history match found\033[2K\n";
Xstatic char	*No_posthistory  = "history: No more commands\033[2K\n";
Xstatic char	*History_2long   = "history: History line too long\033[2K\n";
Xstatic char	*H_TooLongI = "History file line too long - ignored (%d)\n";
X
X/* Arrary of history Items */
X
Xstatic struct	cmd_history {
X    int		number;
X    char	*command;
X} cmd_history[HISTORY_MAX];
X
X/* Processing standard input */
X
Xint			Get_stdin (ap)
Xregister IO_Args	*ap;
X{
X    int		coff = (int)ap->afpos;
X    char	rv;
X
X/* Is there anything in the input buffer.  If not, add the previous line to
X * the history buffer and get the next line
X */
X
X    if (!coff)
X	Process_Stdin ();			/* No - get input	*/
X
X/* Get the next character */
X
X    if ((rv = l_buffer[coff]) == NL)
X    {
X	l_buffer[coff] = 0;
X	ap->afpos = 0L;
X    }
X
X/* Check for end of file */
X
X    else if (rv == 0x1a)
X    {
X	l_buffer[coff] = 0;
X	ap->afpos = 0L;
X	rv = 0;
X    }
X
X    else
X	ap->afpos++;
X
X    return rv;
X}
X
X/* Input processing function */
X
Xstatic void	Process_Stdin ()
X{
X    int		i;
X    char	*control = "^x";
X
X/* Set to last history item */
X
X    c_history = l_history - 1;
X
X/* Process the input */
X
X    while (TRUE)
X    {
X	c_buffer_pos = l_buffer;	/* Initialise			*/
X	end_buffer = l_buffer;
X	insert_mode = FALSE;
X	M_length = -1;
X	s_cursor = read_cursor_position ();
X
X	while (((i = getch ()) != 0x1a) && (i != NL) && (i != '\r'))
X	{
X
X/* Re-position the line? */
X
X	    if (((i) ? alpha_numeric (i) : function (getch ())))
X		Redisplay_Line ();
X
X/* Reposition the cursor */
X
X	    gen_cursor_position ();
X	}
X
X/* Terminate the line */
X
X	*end_buffer = 0;
X	v1_putc (NL);
X
X/* Line input - check for history */
X
X	if ((*l_buffer == '!') && Process_History (0))
X	{
X	    v1a_puts (l_buffer);
X	    break;
X	}
X	
X	else if (*l_buffer != '!')
X	    break;
X
X/* Output prompt and try again */
X
X	Re_start ((char *)NULL);
X    }
X
X    *end_buffer = (char)((i == '\r') ? NL : i);
X}
X
X/* Handler Alpha_numeric characters */
X
Xstatic bool	alpha_numeric (c)
Xint		c;
X{
X    bool	redisplay = FALSE;
X
X/* Backspace processing */
X
X    if (c == 0x08)
X    {
X	if (c_buffer_pos == l_buffer)
X	{
X	    v1_putc (0x07);		/* Ring bell			*/
X	    return FALSE;
X	}
X
X/* Decrement current position */
X
X	if ((c_buffer_pos--) == end_buffer)
X	    --end_buffer;
X
X	else
X	    *c_buffer_pos = ' ';
X
X        return TRUE;
X    }
X
X/* Normal character processing */
X
X    if ((c_buffer_pos - l_buffer) == LINE_MAX)
X    {
X	v1_putc (0x07);			/* Ring bell			*/
X	return FALSE;
X    }
X
X    else if (!insert_mode)
X    {
X	if (c_buffer_pos == end_buffer)
X	    ++end_buffer;
X
X	else if (iscntrl (*c_buffer_pos) || iscntrl (c))
X	    redisplay = TRUE;
X
X	*(c_buffer_pos++) = (char)c;
X
X	if (redisplay || (c == '\t'))
X	    return TRUE;
X
X	if (iscntrl (c))
X	{
X	    v1_putc ('^');
X	    c += '@';
X	}
X
X	v1_putc ((char)c);
X	return FALSE;
X    }
X
X    else if ((end_buffer - l_buffer) == LINE_MAX)
X    {
X	v1_putc (0x07);			/* Ring bell - line full	*/
X	return FALSE;
X    }
X
X    else
X    {
X	if (c_buffer_pos != end_buffer)
X	    memrcpy (end_buffer + 1, end_buffer, end_buffer - c_buffer_pos + 1);
X
X	++end_buffer;
X	*(c_buffer_pos++) = (char)c;
X	return TRUE;
X    }
X}
X
X/* Process function keys */
X
Xstatic bool	function (fn)
Xint		fn;
X{
X    switch (fn)
X    {
X	case 'I':			/* Scan back command line	*/
X	case 'Q':			/* Scan up command line		*/
X	    if (M_length == -1)
X		break;
X
X	    Page_History ((fn == 'I') ? -1 : 1);
X	    return TRUE;
X
X	case 'H':			/* Previous command line	*/
X	    Process_History (-1);
X	    return TRUE;
X
X	case 'P':			/* Next command line		*/
X	    Process_History (1);
X	    return TRUE;
X
X	case 'K':			/* Cursor left			*/
X	    if (c_buffer_pos != l_buffer)
X		--c_buffer_pos;
X
X	    else
X		v1_putc (0x07);
X
X	    return FALSE;
X
X	case 'M':			/* Cursor right			*/
X	    if (c_buffer_pos != end_buffer)
X		++c_buffer_pos;
X
X	    else
X		v1_putc (0x07);
X
X	    return FALSE;
X
X	case 's':			/* Cursor left a word		*/
X	    if (c_buffer_pos != l_buffer)
X	    {
X		--c_buffer_pos;		/* Reposition on previous char	*/
X
X		while (isspace (*c_buffer_pos) && (c_buffer_pos != l_buffer))
X		    --c_buffer_pos;
X
X		while (!isspace (*c_buffer_pos) && (c_buffer_pos != l_buffer))
X		    --c_buffer_pos;
X
X		if (c_buffer_pos != l_buffer)
X		    ++c_buffer_pos;
X	    }
X
X	    else
X		v1_putc (0x07);
X
X	    return FALSE;
X
X	case 't':			/* Cursor right a word		*/
X	    if (c_buffer_pos != end_buffer)
X	    {
X
X/* Skip to the end of the current word */
X
X		while (!isspace (*c_buffer_pos) && (c_buffer_pos != end_buffer))
X		    ++c_buffer_pos;
X
X/* Skip over the white space */
X
X		while (isspace (*c_buffer_pos) && (c_buffer_pos != end_buffer))
X		    ++c_buffer_pos;
X	    }
X
X	    else
X		v1_putc (0x07);
X
X	    return FALSE;
X
X	case 'G':			/* Cursor home			*/
X	    c_buffer_pos = l_buffer;
X	    return FALSE;
X
X	case 'u':			/* Flush to end			*/
X	    memset (c_buffer_pos, ' ', end_buffer - c_buffer_pos);
X	    end_buffer = c_buffer_pos;
X	    return TRUE;
X
X	case 'O':			/* Cursor end of command	*/
X	    if (*l_buffer == '!')
X	    {
X		*end_buffer = 0;
X		Process_History (2);
X		return TRUE;
X	    }
X
X	    c_buffer_pos = end_buffer;
X	    return FALSE;
X
X	case 'R':			/* Switch insert mode		*/
X	    insert_mode = (insert_mode) ? FALSE : TRUE;
X	    return FALSE;
X
X	case 'S':			/* Delete character		*/
X	    if (c_buffer_pos != end_buffer)
X		memcpy (c_buffer_pos, c_buffer_pos + 1,
X			end_buffer - c_buffer_pos);
X
X	    if (end_buffer == l_buffer)
X	    {
X		v1_putc (0x07);
X		return TRUE;
X	    }
X
X	    if (--end_buffer < c_buffer_pos)
X		--c_buffer_pos;
X
X   	    return TRUE;
X    }
X
X    v1_putc (0x07);
X    return FALSE;
X}
X
X/* Read Cursor position */
X
Xstatic int	read_cursor_position ()
X{
X    union REGS	r;
X
X    r.h.ah = 0x03;				/* Read cursor position	*/
X    r.h.bh = 0;					/* Page zero		*/
X    int86 (0x10, &r, &r);
X    return (r.h.dh * 80) + r.h.dl;
X}
X
X/* Re-position the cursor */
X
Xstatic void	set_cursor_position (new)
Xint		new;
X{
X    union REGS	r;
X
X    r.h.ah = 0x02;				/* Set new position	*/
X    r.h.bh = 0;					/* Page zero		*/
X    r.h.dh = (unsigned char)(new / 80);
X    r.h.dl = (unsigned char)(new % 80);
X
X/* Are we at the bottom of the page? */
X
X    if (r.h.dh == 25)
X    {
X	r.h.dh = 24;
X	s_cursor -= 80;
X    }
X
X    int86 (0x10, &r, &r);
X}
X
X/* Generate the new cursor position */
X
Xstatic void	gen_cursor_position ()
X{
X    char	*cp = l_buffer - 1;
X    int		off = s_cursor;
X
X/* Search to current position */
X
X    while (++cp != c_buffer_pos)
X    {
X	if (*cp == '\t')
X	    while ((++off) % 8);
X
X	else if (iscntrl (*cp))
X	    off += 2;
X
X	else
X	    ++off;
X    }
X
X/* Position the cursor */
X
X    set_cursor_position (off);
X}
X
X/* Redisplay the current line */
X
Xstatic void	Redisplay_Line ()
X{
X    char	*control = "^x";
X    char	*cp = l_buffer;
X    int		off = s_cursor;
X
X/* Reposition to start of line */
X
X    set_cursor_position (s_cursor);
X
X/* Output the line */
X
X    while (cp != end_buffer)
X    {
X	if (*cp == '\t')
X	{
X	    do
X	    {
X		v1_putc (SP);
X	    } while ((++off) % 8);
X	}
X
X	else if (iscntrl (*cp))
X	{
X	    control[1] = *cp + '@';
X	    v1_puts (control);
X	    off += 2;
X	}
X
X	else
X	{
X	    ++off;
X	    v1_putc (*cp);
X	}
X
X	++cp;
X    }
X
X    v1_puts ("\033[2K");		/* clear to end of line	*/
X}
X
X/* Process history command
X *
X * -1: Previous command
X *  1: Next command
X *  0: Current command
X *  2: Current command with no options processing
X */
X
Xstatic bool	Process_History (direction)
Xint		direction;
X{
X    char	*optionals = null;
X
X    c_buffer_pos = l_buffer;
X    end_buffer = l_buffer;
X    c_history += (direction == 2) ? 0 : direction;
X
X    switch (direction)
X    {
X	case -1:			/* Move up one line		*/
X	    if (c_history < 0)
X	    {
X		++c_history;
X		return Re_start (No_prehistory);
X	    }
X
X	    break;
X
X	case 1:				/* Move to next history line	*/
X	    if (c_history >= l_history)
X	    {
X		--c_history;
X		return Re_start (No_posthistory);
X	    }
X
X	    break;
X
X	case 0:				/* Check out l_buffer		*/
X	    optionals = l_buffer;	/* Are there any additions to	*/
X					/* the history line		*/
X
X/* Find the end of the first part */
X
X	    while (!isspace (*optionals) && *optionals)
X		++optionals;
X
X/* Terminate the history command */
X
X	    if (*optionals)
X		*(optionals++) = 0;
X
X/* Find the end of the space separator part which gives the start of the
X * optionals
X */
X
X	    while (isspace (*optionals))
X		++optionals;
X	    
X
X/* Copy selected item into line buffer */
X
X	case 2:
X	    M_length = strlen (l_buffer) - 1;
X	    if (!Scan_History ())
X		return FALSE;
X
X	    break;
X    }
X
X    return UpDate_CLine (optionals);
X}
X
X/* Ok c_history points to the new line.  Move optionals after history
X * and the copy in history and add a space
X */
X
Xstatic bool	UpDate_CLine (optionals)
Xchar		*optionals;
X{
X    int		opt_len;
X
X    end_buffer = &l_buffer[strlen (cmd_history[c_history].command)];
X
X    if ((end_buffer - l_buffer + (opt_len = strlen (optionals)) + 1) >= LINE_MAX)
X	return Re_start (History_2long);
X
X    if (end_buffer > optionals)
X	memrcpy (end_buffer + 1 + opt_len, optionals + opt_len, opt_len + 1);
X    
X    else
X	strcpy (end_buffer + 1, optionals);
X
X    strcpy (l_buffer, cmd_history[c_history].command);
X
X    if (opt_len)
X	*end_buffer = ' ';
X
X    end_buffer = &l_buffer[strlen (l_buffer)];
X    return TRUE;
X}
X
X/* Scan the line buffer for a history match */
X
Xstatic bool	Scan_History ()
X{
X    char	*cp = &l_buffer[1];
X    int		c_len = strlen (cp);
X    char	*ep;
X    int		i = (int)strtol (cp, &ep, 10);
X
X/* Get the previous command ? (single !) */
X
X    if (c_len == 0)
X    {
X	if (c_history < 0)
X	{
X	    M_length = -1;
X	    return Re_start (No_prehistory);
X	}
X
X	return TRUE;
X    }
X
X/* Request for special history number item.  Check History file empty */
X
X    if (l_history == 0)
X    {
X	M_length = -1;
X	return Re_start (No_MatchHistory);
X    }
X
X/* Check for number */
X
X    if (!*ep)
X    {
X	M_length = -1;
X
X	for (c_history = l_history - 1;
X	    (cmd_history[c_history].number != i) && (c_history >= 0);
X	    --c_history);
X    }
X
X/* No - scan for a match */
X
X    else
X    {
X	for (c_history = l_history - 1;
X	    (strncmp (cp, cmd_history[c_history].command, c_len) != 0)
X	     && (c_history >= 0);
X	    --c_history);
X    }
X
X/* Anything found ? */
X
X    if (c_history == -1)
X    {
X	c_history = l_history - 1;
X	return Re_start (No_MatchHistory);
X    }
X
X    return TRUE;
X}
X
X/* Scan back or forward from current history */
X
Xstatic void	Page_History (direction)
Xint		direction;
X{
X    c_buffer_pos = l_buffer;
X    end_buffer = l_buffer;
X
X    if (l_history == 0)
X    {
X	M_length = -1;
X	Re_start (No_MatchHistory);
X	return;
X    }
X
X/* scan for a match */
X
X    while (((c_history += direction) >= 0) && (c_history != l_history) &&
X	   (strncmp (l_buffer, cmd_history[c_history].command, M_length) != 0));
X
X/* Anything found ? */
X
X    if ((c_history < 0) || (c_history == l_history))
X    {
X	c_history = l_history - 1;
X	Re_start (No_MatchHistory);
X    }
X
X    else
X	UpDate_CLine (null);
X}
X
X/* Load history file */
X
Xvoid	Load_History ()
X{
X    FILE		*fp;
X    char		*cp;
X    int			i = 0;
X    Var_List		*lset;
X
X/* Initialise history array */
X
X    memset (cmd_history, 0, sizeof (struct cmd_history) * HISTORY_MAX);
X    c_history = -1;			/* Current entry		*/
X    l_history = 0;			/* End of history array		*/
X
X    if ((lset = lookup (history_file, TRUE))->value == null)
X	setval (lset, strcat (strcpy (l_buffer, lookup (home, FALSE)->value),
X			      "history.sh"));
X
X    if (!History_Enabled || ((fp = fopen (lset->value, "rt")) == (FILE *)NULL))
X	return;
X
X/* Read in file */
X
X    while (fgets (l_buffer, LINE_MAX, fp) != (char *)NULL)
X    {
X	++i;
X
X	if ((cp = strchr (l_buffer, NL)) == (char *)NULL)
X	    print_warn (H_TooLongI, i);
X
X	else
X	{
X	    *cp = 0;
X	    Add_History (TRUE);
X	}
X    }
X
X    fclose (fp);
X}
X
X/* Add entry to history file */
X
Xvoid	Add_History (past)
Xbool	past;				/* Past history?	*/
X{
X    int			i;
X
X    if ((!History_Enabled) || (strlen (l_buffer) == 0))
X	return;
X
X/* If adding past history, decrement all numbers previous */
X
X    if ((past) && l_history)
X    {
X	for (i = 0; i < l_history; i++)
X	    --(cmd_history[i].number);
X    }
X
X/* If the array is full, remove the last item */
X
X    if (l_history == HISTORY_MAX)
X    {
X	if (cmd_history[0].command != null)
X	    DELETE (cmd_history[0].command);
X
X	--l_history;
X	memcpy (&cmd_history[0], &cmd_history[1],
X		sizeof (struct cmd_history) * (HISTORY_MAX - 1));
X    }
X
X/* If there are any items in the array */
X
X    c_history = l_history;
X    Current_Event = (l_history) ? cmd_history[l_history - 1].number + 1 : 0;
X    cmd_history[l_history].number = Current_Event;
X
X/* Save the string */
X
X    cmd_history[l_history++].command = strsave (l_buffer, 0);
X}
X
X/* Print history */
X
Xvoid	Display_History ()
X{
X    int			i;
X    struct cmd_history	*cp = cmd_history;
X
X    if (!l_history)
X	return;
X
X    for (i = 0; i < l_history; ++cp, ++i)
X    {
X	v1printf ("%5d: ", cp->number);
X	v1a_puts (cp->command);
X    }
X}
X
X/* Dump history to file */
X
Xvoid	Dump_History ()
X{
X    int			i;
X    struct cmd_history	*cp = cmd_history;
X    FILE		*fp;
X
X    if (!History_Enabled ||
X	((fp = fopen (lookup (history_file, FALSE)->value, "wt")) ==
X	 (FILE *)NULL))
X	return;
X
X    for (i = 0; i < l_history; ++cp, ++i)
X    {
X	fputs (cp->command, fp);
X	fputc (NL, fp);
X    }
X
X    fclose (fp);
X}
X
X/* Clear out history */
X
Xvoid	Clear_History ()
X{
X    int			i;
X    struct cmd_history	*cp = cmd_history;
X
X    for (i = 0; i < l_history; ++cp, ++i)
X    {
X	if (cp->command != null)
X	    DELETE (cp->command);
X    }
X
X    memset (cmd_history, 0, sizeof (struct cmd_history) * HISTORY_MAX);
X
X    c_history = -1;			/* Current entry		*/
X    l_history = 0;			/* End of history array		*/
X    Current_Event = 0;
X}
X
X/* Output warning message and prompt */
X
Xstatic bool	Re_start (cp)
Xchar		*cp;
X{
X    if (cp != (char *)NULL)
X	print_warn (cp);
X
X    put_prompt (last_prompt);
X
X/* Re-initialise */
X
X    c_buffer_pos = l_buffer;
X    end_buffer = l_buffer;
X    s_cursor = read_cursor_position ();
X
X    return FALSE;
X}
X
X/* Copy backwards */
X
Xstatic void	memrcpy (sp1, sp, cnt)
Xchar		*sp1;
Xchar		*sp;
Xint		cnt;
X{
X    while (cnt--)
X	*(sp1--) =  *(sp--);
X}
SHAR_EOF
chmod 0644 shell/sh9.c || echo "restore of shell/sh9.c fails"
set `wc -c shell/sh9.c`;Sum=$1
if test "$Sum" != "15932"
then echo original size 15932, current size $Sum;fi
echo "x - extracting shell/sh10.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh10.c &&
X/* MS-DOS SHELL - Function Processing
X *
X * MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited
X *
X * This code is subject to the following copyright restrictions:
X *
X * 1.  Redistribution and use in source and binary forms are permitted
X *     provided that the above copyright notice is duplicated in the
X *     source form and the copyright notice in file sh6.c is displayed
X *     on entry to the program.
X *
X * 2.  The sources (or parts thereof) or objects generated from the sources
X *     (or parts of sources) cannot be sold under any circumstances.
X *
X *    $Header: sh10.c 1.1 90/01/25 13:40:54 MS_user Exp $
X *
X *    $Log:	sh10.c $
X * Revision 1.1  90/01/25  13:40:54  MS_user
X * Initial revision
X * 
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <stdio.h>
X#include <process.h>
X#include <dos.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <ctype.h>
X#include <string.h>
X#include <unistd.h>
X#include <stdlib.h>
X#include <fcntl.h>
X#include <limits.h>
X#include "sh.h"
X
X/* Function declarations */
X
Xstatic void	Print_Command (C_Op *);
Xstatic void	Print_IO (IO_Actions *);
Xstatic void	Print_Case (C_Op *);
Xstatic void	Print_IString (char *, int);
Xstatic void	Set_Free_ExTree (C_Op *, void (*)(char *));
Xstatic void	Set_Free_Command (C_Op *, void (*)(char *));
Xstatic void	Set_Free_Case (C_Op *, void (*)(char *));
Xstatic void	Set_ExTree (char *);
Xstatic void	Free_ExTree (char *);
X
Xstatic int	Print_indent;			/* Current indent level	*/
X
X/*
X * print the execute tree - used for displaying functions
X */
X
Xvoid		Print_ExTree (t)
Xregister C_Op	*t;
X{
X    char		**wp;
X
X    if (t == (C_Op *)NULL)
X	return;
X
X/* Check for start of print */
X
X    if (t->type == TFUNC)
X    {
X	Print_indent = 0;
X	v1_puts (*t->words);
X	v1a_puts (" ()");
X	Print_ExTree (t->left);
X	return;
X    }
X
X/* Otherwise, process the tree and print it */
X
X    switch (t->type) 
X    {
X	case TPAREN:			/* ()			*/
X	case TCOM:			/* A command process	*/
X	    Print_Command (t);
X	    return;
X
X	case TPIPE:			/* Pipe processing		*/
X	    Print_ExTree (t->left);
X	    Print_IString ("|\n", 0);
X	    Print_ExTree (t->right);
X	    return;
X
X	case TLIST:			/* Entries in a for statement	*/
X	    Print_ExTree (t->left);
X	    Print_ExTree (t->right);
X	    return;
X
X	case TOR:			/* || and &&			*/
X	case TAND:
X	    Print_ExTree (t->left);
X
X	    if (t->right != (C_Op *)NULL)
X	    {
X		Print_IString ((t->type == TAND) ? "&&\n" : "||\n", 0);
X		Print_ExTree (t->right);
X	    }
X
X	    return;
X
X	case TFOR:			/* First part of a for statement*/
X	    Print_IString ("for ", 0);
X	    v1_puts (t->str);
X
X	    if ((wp = t->words) != (char **)NULL)
X	    {
X		v1_puts (" in");
X
X		while (*wp != (char *)NULL)
X		{
X		    v1_putc (SP);
X		    v1_puts (*wp++);
X		}
X	    }
X
X	    v1_putc (NL);
X	    Print_IString ("do\n", 1);
X	    Print_ExTree (t->left);
X	    Print_IString ("done\n", -1);
X	    return;
X
X	case TWHILE:			/* WHILE and UNTIL functions	*/
X	case TUNTIL:
X	    Print_IString ((t->type == TWHILE) ? "while " : "until ", 1);
X	    Print_ExTree (t->left);
X	    Print_IString ("do\n", 0);
X	    Print_ExTree (t->right);
X	    Print_IString ("done\n", -1);
X	    return;
X
X	case TIF:			/* IF and ELSE IF functions	*/
X	case TELIF:
X	    if (t->type == TIF)
X		Print_IString ("if\n", 1);
X	    
X	    else
X		Print_IString ("elif\n", 1);
X	    
X	    Print_ExTree (t->left);
X
X	    Print_indent -= 1;
X	    Print_IString ("then\n", 1);
X	    Print_ExTree (t->right->left);
X
X	    if (t->right->right != (C_Op *)NULL)
X	    {
X		Print_indent -= 1;
X
X		if (t->right->right->type != TELIF)
X		    Print_IString ("else\n", 1);
X
X		Print_ExTree (t->right->right);
X	    }
X
X	    if (t->type == TIF)
X		Print_IString ("fi\n", -1);
X
X	    return;
X
X	case TCASE:			/* CASE function		*/
X	    Print_IString ("case ", 1);
X	    v1_puts (t->str);
X	    v1a_puts (" do");
X	    Print_Case (t->left);
X	    Print_IString (" esac\n", -1);
X	    return;
X
X	case TBRACE:			/* {} statement			*/
X	    Print_IString ("{\n", 1);
X	    if (t->left != (C_Op *)NULL)
X		Print_ExTree (t->left);
X
X	    Print_IString ("}\n", -1);
X	    return;
X    }
X}
X
X/*
X * Print a command line
X */
X
Xstatic void	Print_Command (t)
Xregister C_Op	*t;
X{
X    char	*cp;
X    IO_Actions	**iopp;
X    char	**wp = t->words;
X    char	**owp = wp;
X
X    if (t->type == TCOM) 
X    {
X	while ((cp = *wp++) != (char *)NULL)
X	    ;
X
X	cp = *wp;
X
X/* strip all initial assignments not correct wrt PATH=yyy command  etc */
X
X	if ((cp == (char *)NULL) && (t->ioact == (IO_Actions **)NULL))
X	{
X	    Print_IString (null, 0);
X
X	    while (*owp != (char *)NULL)
X		v1a_puts (*(owp++));
X
X	    return;
X	}
X    }
X
X/* Parenthesis ? */
X
X    if (t->type == TPAREN)
X    {
X	Print_IString ("(\n", 1);
X	Print_ExTree (t->left);
X	Print_IString (")", -1);
X    }
X
X    else
X    {
X	Print_IString (null, 0);
X
X	while (*owp != (char *)NULL)
X	{
X	    v1_puts (*owp++);
X
X	    if (*owp != (char *)NULL)
X		v1_putc (SP);
X	}
X    }
X
X/* Set up anyother IO required */
X
X    if ((iopp = t->ioact) != (IO_Actions **)NULL) 
X    {
X	while (*iopp != (IO_Actions *)NULL)
X	    Print_IO (*iopp++);
X    }
X
X    v1_putc (NL);
X}
X
X/*
X * Print the IO re-direction
X */
X
Xstatic void		Print_IO (iop)
Xregister IO_Actions	*iop;
X{
X    int		unit = iop->io_unit;
X    static char	*cunit = " x";
X
X    if (unit == IODEFAULT)	/* take default */
X	unit = (iop->io_flag & (IOREAD | IOHERE)) ? STDIN_FILENO
X						  : STDOUT_FILENO;
X
X/* Output unit number */
X
X    cunit[1] = (char)(unit + '0');
X    v1_puts (cunit);
X
X    switch (iop->io_flag) 
X    {
X	case IOHERE:
X	case IOHERE | IOXHERE:
X	    v1_putc ('<');
X
X	case IOREAD:
X	    v1_putc ('<');
X	    break;
X
X	case IOWRITE | IOCAT:
X	    v1_putc ('>');
X
X	case IOWRITE:
X	    v1_putc ('>');
X	    break;
X
X	case IODUP:
X	    v1_puts (">&");
X	    v1_putc (*iop->io_name);
X	    return;
X    }
X
X    v1_puts (iop->io_name);
X}
X
X/*
X * Print out the contents of a case statement
X */
X
Xstatic void	Print_Case (t)
XC_Op		*t;
X{
X    register C_Op	*t1;
X    register char	**wp;
X
X    if (t == (C_Op *)NULL)
X	return;
X
X/* type - TLIST - go down the left tree first and then processes this level */
X
X    if (t->type == TLIST) 
X    {
X	Print_Case (t->left);
X	t1 = t->right;
X    }
X    
X    else
X	t1 = t;
X
X/* Output the conditions */
X
X    Print_IString (null, 0);
X
X    for (wp = t1->words; *wp != (char *)NULL;)
X    {
X	v1_puts (*(wp++));
X
X	if (*wp != (char *)NULL)
X	    v1_puts (" | ");
X    }
X
X    v1a_puts (" )");
X    Print_indent += 1;
X
X/* Output the commands */
X
X    Print_ExTree (t1->left);
X    Print_IString (";;\n", -1);
X}
X
X/*
X * Print an indented string
X */
X
Xstatic void	Print_IString (cp, indent)
Xchar		*cp;
Xint		indent;
X{
X    int		i;
X
X    if (indent < 0)
X	Print_indent += indent;
X
X    for (i = 0; i < (Print_indent / 2); i++)
X	v1_putc ('\t');
X
X    if (Print_indent % 2)
X	v1_puts ("    ");
X    
X    v1_puts (cp);
X
X    if (indent > 0)
X	Print_indent += indent;
X}
X
X/*
X * Look up a function in the save tree
X */
X
XFun_Ops		*Fun_Search (name)
Xchar		*name;
X{
X    Fun_Ops	*fp;
X
X    for (fp = fun_list; fp != (Fun_Ops *)NULL; fp = fp->next)
X    {
X	if (strcmp (*(fp->tree->words), name) == 0)
X	    return fp;
X    }
X
X    return (Fun_Ops *)NULL;
X}
X
X/*
X * Save or delete a function tree
X */
X
Xvoid	Save_Function (t, delete_only)
XC_Op	*t;
Xbool	delete_only;			/* True - delete		*/
X{
X    char		*name = *t->words;
X    register Fun_Ops	*fp = Fun_Search (name);
X    Fun_Ops		*p_fp = (Fun_Ops *)NULL;
X
X/* Find the entry */
X
X    for (fp = fun_list; (fp != (Fun_Ops *)NULL) &&
X			(strcmp (*(fp->tree->words), name) != 0);
X			p_fp = fp, fp = fp->next);
X
X/* If it already exists, free the tree and delete the entry */
X
X    if (fp != (Fun_Ops *)NULL)
X    {
X	Set_Free_ExTree (fp->tree, Free_ExTree);
X
X	if (p_fp == (Fun_Ops *)NULL)
X	    fun_list = fp->next;
X
X	else
X	    p_fp->next = fp->next;
X
X	DELETE (fp);
X    }
X
X/* If delete only - exit */
X
X    if (delete_only)
X	return;
X
X/* Create new entry */
X
X    if ((fp = (Fun_Ops *)space (sizeof (Fun_Ops))) == (Fun_Ops *)NULL)
X	return;
X
X    setarea ((char *)fp, 0);
X    Set_Free_ExTree (t, Set_ExTree);
X
X    fp->tree = t;
X    fp->next = fun_list;
X    fun_list = fp;
X}
X
X/*
X * Set ExTree areas to zero function
X */
X
Xstatic void	Set_ExTree (s)
Xchar		*s;
X{
X    setarea (s, 0);
X}
X
X/*
X * Free the ExTree function
X */
X
Xstatic void	Free_ExTree (s)
Xchar		*s;
X{
X    DELETE (s);
X}
X
X/*
X * Set/Free function tree area by recursively processing of tree
X */
X
Xstatic void	Set_Free_ExTree (t, func)
XC_Op		*t;
Xvoid		(*func)(char *);
X{
X    char		**wp;
X
X    if (t == (C_Op *)NULL)
X	return;
X
X/* Check for start of print */
X
X    if (t->type == TFUNC)
X    {
X	(*func)(*t->words);
X	(*func)((char *)t->words);
X	Set_Free_ExTree (t->left, func);
X    }
X
X/* Otherwise, process the tree and print it */
X
X    switch (t->type) 
X    {
X	case TPAREN:			/* ()			*/
X	case TCOM:			/* A command process	*/
X	    Set_Free_Command (t, func);
X	    break;
X
X	case TPIPE:			/* Pipe processing		*/
X	case TLIST:			/* Entries in a for statement	*/
X	case TOR:			/* || and &&			*/
X	case TAND:
X	case TWHILE:			/* WHILE and UNTIL functions	*/
X	case TUNTIL:
X	    Set_Free_ExTree (t->left, func);
X	    Set_Free_ExTree (t->right, func);
X	    break;
X
X	case TFOR:			/* First part of a for statement*/
X	    (*func)(t->str);
X
X	    if ((wp = t->words) != (char **)NULL)
X	    {
X		while (*wp != (char *)NULL)
X		    (*func) (*wp++);
X
X		(*func)((char *)t->words);
X	    }
X
X	    Set_Free_ExTree (t->left, func);
X	    break;
X
X	case TIF:			/* IF and ELSE IF functions	*/
X	case TELIF:
X	    Set_Free_ExTree (t->right->left, func);
X	    Set_Free_ExTree (t->right->right, func);
X	    (*func)((char *)t->right);
X
X	case TBRACE:			/* {} statement			*/
X	    Set_Free_ExTree (t->left, func);
X	    break;
X
X	case TCASE:			/* CASE function		*/
X	    (*func)(t->str);
X	    Set_Free_Case (t->left, func);
X	    break;
X    }
X
X    (*func)((char *)t);
X}
X
X/*
X * Set/Free a command line
X */
X
Xstatic void	Set_Free_Command (t, func)
XC_Op		*t;
Xvoid		(*func)(char *);
X{
X    IO_Actions	**iopp;
X    char	**wp = t->words;
X
X/* Parenthesis ? */
X
X    if (t->type == TPAREN)
X	Set_Free_ExTree (t->left, func);
X
X    else
X    {
X	while (*wp != (char *)NULL)
X	    (*func)(*wp++);
X
X	(*func) ((char *)t->words);
X    }
X
X/* Process up any IO required */
X
X    if ((iopp = t->ioact) != (IO_Actions **)NULL) 
X    {
X	while (*iopp != (IO_Actions *)NULL)
X	{
X	    (*func)((char *)(*iopp)->io_name);
X	    (*func)((char *)*iopp);
X	    iopp++;
X	}
X
X	(*func)((char *)t->ioact);
X    }
X}
X
X/*
X * Set/Free the contents of a case statement
X */
X
Xstatic void	Set_Free_Case (t, func)
XC_Op		*t;
Xvoid		(*func)(char *);
X{
X    register C_Op	*t1;
X    register char	**wp;
X
X    if (t == (C_Op *)NULL)
X	return;
X
X/* type - TLIST - go down the left tree first and then processes this level */
X
X    if (t->type == TLIST) 
X    {
X	Set_Free_Case (t->left, func);
X	t1 = t->right;
X    }
X    
X    else
X	t1 = t;
X
X/* Set/Free the conditions */
X
X    for (wp = t1->words; *wp != (char *)NULL;)
X	(*func)(*(wp++));
X
X    (*func)((char *)t1->words);
X
X    Set_Free_ExTree (t1->left, func);
X}
SHAR_EOF
chmod 0644 shell/sh10.c || echo "restore of shell/sh10.c fails"
set `wc -c shell/sh10.c`;Sum=$1
if test "$Sum" != "10869"
then echo original size 10869, current size $Sum;fi
echo "x - extracting shell/sh0.asm (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh0.asm &&
X	TITLE   sh0.asm
X	NAME    sh0
X	.8087
X
X; MS-DOS SHELL - Swapper
X;
X; MS-DOS SHELL - Copyright (c) 1989 Data Logic Limited.
X;
X; MS-DOS SHELL - Copyright (c) 1990 Data Logic Limited
X;
X; This code is subject to the following copyright restrictions:
X;
X; 1.  Redistribution and use in source and binary forms are permitted
X;     provided that the above copyright notice is duplicated in the
X;     source form and the copyright notice in file sh6.c is displayed
X;     on entry to the program.
X;
X; 2.  The sources (or parts thereof) or objects generated from the sources
X;     (or parts of sources) cannot be sold under any circumstances.
X;
X;    $Header: sh0.asm 1.1 90/01/25 13:43:36 MS_user Exp $
X;
X;    $Log:	sh0.asm $
X;	Revision 1.1  90/01/25  13:43:36  MS_user
X;	Initial revision
X;	
X;
X
X;
X; Segment declarations
X;
X
XSH0_TEXT	segment word public 'CODE'
XSH0_TEXT	ends
X
X_DATA		segment word public 'DATA'
X_DATA		ends
X
XCONST		segment word public 'CONST'
XCONST		ends
X
X_BSS		segment word public 'BSS'
X_BSS		ends
X
XDGROUP		group	CONST, _BSS, _DATA
X
X;
X; Declare external functions and data
X;
X	extrn	_raise:far
X	extrn	__maperror:far
X	extrn	_errno:word
X	extrn	__psp:word
X
X;
X; Start of the spawn function
X;
X
XSH0_TEXT	segment
X		assume  cs: SH0_TEXT, ds: NOTHING, ss: DGROUP
X
X;
X; For this function, all the code and data space are in the code space
X;
X		public	_cmd_line
X		public	_path_line
X		public	_SW_intr
X		public	_SW_Blocks
X		public	_SW_fp
X		public	_SW_I0_V_BX
X		public	_SW_I0_V_ES
X		public	_SW_I23_V_ES
X		public	_SW_I23_V_BX
X		public	_SW_EMstart
X		public	_SW_Mode
X		public	_SW_EMSFrame
X
X_cmd_line	db	129 dup (?)	; Command line
X_path_line	db	80 dup (?)	; Path line
X_SW_Blocks	dw	0		; Number of blocks to read/write
X_SW_fp		dw	0		; File ID
X_SW_I23_V_ES	dw	0		; Interrupt 23 address
X_SW_I23_V_BX	dw	0
X_SW_I0_V_BX	dw	0		; Our Interrupt zero value
X_SW_I0_V_ES	dw	0
X_SW_EMstart	dd	0100000H	; Default Extended Mem start
X_SW_Mode	dw	0		; Type of swapping to do
X					;   1 - disk
X					;   2 - Extended memory
X					;   3 - Expanded memory
X_SW_EMSFrame	dw	0		; EMS Frame segment
X_SW_intr	dw	0		; Interrupt 23 detected.
X
X
X;
X; Some addition variables
X;
X
XSW_LMstart	dd	0		; Low Mem start for Extended Mem swap
XN_mcb		dw	0		; Start write address
XResult		dw	0		; Return value
XInShell		db	0		; In shell flag for Interrupt 23
X
X;
X; Stack save pointers
X;
X
XS_ss		dw	0			; Save Stack pointers
XS_sp		dw	0
XS_di		dw	0			; Save DI, SI
XS_si		dw	0
XS_ds		dw	0			; Save the original DS
X
X;
X; Two blank FCB
X;
X
XFCB1		dw	16	dup (?)
XFCB2		dw	16	dup (?)
X
X;
X; Extended Memory Global Descriptor tables
X;
X
XGD_table	equ	$
XGDT_Dummy	dw	4	dup (0)		; Dummy
XGDT_self	dw	4	dup (0)		; For self
XGDT_src		equ	$			; Source
X		dw	04000H			; Length - 16K bytes
XGDT_src_low	dw	0			;     Low Order address
XGDT_src_high	db	0			;     High Order address
X		db	093h			;     Access Rights
X		dw	0			;     Reserved
XGDT_dest	equ	$			; Destination
X		dw	04000H			;     Length - 16K bytes
XGDT_dest_low	dw	0			;     Low Order address
XGDT_dest_high	db	0			;     High Order address
X		db	093h			;     Access Rights
X		dw	0			;     Reserved
XGDT_bios	dw	4	dup (0)		; Bios
XGDT_stack	dw	4	dup (0)		; Stack
X
X;
X; Execute interrupt structure
X;
X
Xexec_parms	equ	$
Xexec_env	dw	0
X		dw	offset _cmd_line	; Command line address
Xexec_cseg	dw	?
X		dw	offset FCB1		; FCB1 address
Xexec_f1seg	dw	?
X		dw	offset FCB2		; FCB1 address
Xexec_f2seg	dw	?
X
XSwap_PANIC	db	'PANIC: Swap file re-load error - REBOOT', 0aH, 0dH
X		db	'$'
X
XSwap_DZERO	db	'PANIC: Divide by zero', 0aH, 0dH
X		db	'$'
X
X;
X; OK - exec requires a local stack, cause some programs overwrite it
X;
X		even
X		db	398 dup (0)
XLocal_Stack:
X		dw	0
X
X;
X; Code starts
X;
X	public	_SA_spawn
X
X_SA_spawn	proc	far
X
X	push	bp
X	mov	bp, sp
X
X;
X; Entry Offsets
X;
X;	Environment      = 6
X;
X
X	mov	word ptr cs:S_di, di		; Save registers
X	mov	word ptr cs:S_si, si
X	mov	word ptr cs:S_ds, ds
X
X;
X; Set up to ingnore Control C interrupts
X;
X
X	push	ds
X	mov	ax, 02523H		; Set Control C Interrupt
X	mov	dx, offset SA_IRET
X	push	cs
X	pop	ds
X	mov	byte ptr cs:InShell, 0	; Set In shell flag for Interrupt 23
X	int	021H
X
X	mov	ax, 02500H		; Set Divide Zero Interrupt
X	mov	dx, offset SA_DZERO
X	push	cs
X	pop	ds
X	int	021H
X
X	pop	ds
X
X;
X; Save the length of the current MCB block;
X;
X
X	mov	ax, word ptr ds:__psp
X	dec	ax
X	mov	word ptr cs:N_mcb, ax		; Save MCB address for swap out
X
X; Calculate low mem start for extended memory
X
X	mov	bx, ax				; Save copy
X	mov	cl, 4				; mult low order by 16
X	shl	ax, cl
X	mov	word ptr cs:SW_LMstart, ax	; Save low order
X	mov	cl, 12				; div by 16 ** 3
X	shr	bx, cl
X	mov	byte ptr cs:SW_LMstart + 2, bl	; Save low order
X
X;
X; Set up Environment segment in execute structure
X;
X
X	mov	bx, cs
X	mov	ax, offset Env_OWrite
X	mov	cl, 4
X	shr	ax, cl
X	add	ax, bx
X	mov	word ptr cs:exec_env, ax	; Save Env seg.
X
X;
X; Set up rest of execute structure
X;
X
X	mov	word ptr cs:exec_cseg, cs	; Command line address
X	mov	word ptr cs:exec_f1seg, cs	; FCB 1 address
X	mov	word ptr cs:exec_f2seg, cs	; FCB 2 address
X
X;
X; Generate the FCBs
X;
X
X	mov	ax, cs		; Set up segments
X	mov	ds, ax
X	mov	es, ax
X
X	mov	ax, 02901H	; Set up FCB interrupt
X	mov	si, offset _cmd_line + 1
X	mov	di, offset FCB1	; FCB 1;
X
X	int	021H		; Execute the interrupt
X
X	mov	ax, cs		; Set up segment
X	mov	es, ax
X
X	mov	ax, 02901H	; Reset AX cause errors are ignored
X	mov	di, offset FCB2	; FCB 2;
X
X	int	021H		; Execute the interrupt
X
X;
X; Copy out to the swap file
X;
X
X	mov	si, word ptr cs:_SW_Blocks	; Load Number of blocks to read
X	mov	bx, word ptr cs:_SW_fp		; Load file handler
X
X; load up extended memory GDT for destination
X
X	mov	ax, word ptr cs:_SW_EMstart
X	mov	dl, byte ptr cs:_SW_EMstart + 2
X	call	$GDT_dest_load
X
X;
X; set up DS register with start of start copy
X;
X
X	mov	ax, word ptr cs:N_mcb		; Load the start address
X	mov	ds, ax
X
X	mov	ax, word ptr cs:SW_LMstart 	; Load Full start address
X	mov	dl, byte ptr cs:SW_LMstart + 2
SHAR_EOF
echo "End of part 4"
echo "File shell/sh0.asm is continued in part 5"
echo "5" > s2_seq_.tmp
exit 0

-- 
Regards,

Ian Stewartson
Data Logic Ltd.