v10i056: MSDOS Shell (sh) Implementation - Part 03 of 05

Ian Stewartson istewart at datlog.co.uk
Wed Feb 14 12:54:36 AEST 1990


Posting-number: Volume 10, Issue 56
Submitted-by: istewart at datlog.co.uk (Ian Stewartson)
Archive-name: sh_dos/part04

#!/bin/sh
# this is part 3 of a multipart archive
# do not concatenate these parts, unpack them in order with /bin/sh
# file shell/sh2.c continued
#
CurArch=3
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/sh2.c"
sed 's/^X//' << 'SHAR_EOF' >> shell/sh2.c
X	    {
X		if (iolist == (Word_B *)NULL)
X		    return (C_Op *)NULL;
X
X		(t = (C_Op *)tree (sizeof (C_Op)))->type = TCOM;
X	    }
X
X	    break;
X
X	case '(':
X	    t = nested (TPAREN, ')');
X	    break;
X
X	case '{':
X	    t = nested (TBRACE, '}');
X	    break;
X
X	case FOR:
X	    (t = (C_Op *)tree (sizeof (C_Op)))->type = TFOR;
X	    musthave (WORD, 0);
X	    startl = TRUE;
X	    t->str = yylval.cp;
X	    multiline++;
X	    t->words = wordlist ();
X
X	    if (((c = yylex (0)) != NL) && (c != ';'))
X		yyerror (syntax_err);
X
X	    t->left = dogroup (0);
X	    multiline--;
X	    break;
X
X	case WHILE:
X	case UNTIL:
X	    multiline++;
X	    t = (C_Op *)tree (sizeof (C_Op));
X	    t->type = (c == WHILE) ? TWHILE : TUNTIL;
X	    t->left = c_list (FALSE);
X	    t->right = dogroup (1);
X	    t->words = NULL;
X	    multiline--;
X	    break;
X
X	case CASE:
X	    (t = (C_Op *)tree (sizeof (C_Op)))->type = TCASE;
X	    musthave (WORD, 0);
X	    t->str = yylval.cp;
X	    startl = TRUE;
X	    multiline++;
X	    musthave (IN, CONTIN);
X	    startl = TRUE;
X	    t->left = caselist();
X	    musthave (ESAC, 0);
X	    multiline--;
X	    break;
X
X	case IF:
X	    multiline++;
X	    (t = (C_Op *)tree (sizeof (C_Op)))->type = TIF;
X	    t->left = c_list (FALSE);
X	    t->right = thenpart ();
X	    musthave (FI, 0);
X	    multiline--;
X	    break;
X    }
X
X    while (synio (0))
X	;
X
X    t = namelist (t);
X    iolist = iosave;
X    return t;
X}
X
Xstatic C_Op	*dogroup (onlydone)
Xint		onlydone;
X{
X    register int	c;
X    register C_Op	*list;
X
X    if (((c = yylex (CONTIN)) == DONE) && onlydone)
X	return (C_Op *)NULL;
X
X    if (c != DO)
X	yyerror (syntax_err);
X
X    list = c_list (FALSE);
X    musthave (DONE, 0);
X    return list;
X}
X
Xstatic C_Op	*thenpart ()
X{
X    register int	c;
X    register C_Op	*t;
X
X    if ((c = yylex (0)) != THEN) 
X    {
X	peeksym = c;
X	return (C_Op *)NULL;
X    }
X
X    (t = (C_Op *)tree (sizeof (C_Op)))->type = 0;
X
X    if ((t->left = c_list (FALSE)) == (C_Op *)NULL)
X	yyerror (syntax_err);
X
X    t->right = elsepart ();
X    return t;
X}
X
Xstatic C_Op	*elsepart ()
X{
X    register int	c;
X    register C_Op	*t;
X
X    switch (c = yylex (0)) 
X    {
X	case ELSE:
X	    if ((t = c_list (FALSE)) == (C_Op *)NULL)
X		yyerror (syntax_err);
X
X	    return t;
X
X	case ELIF:
X	    (t = (C_Op *)tree (sizeof (C_Op)))->type = TELIF;
X	    t->left = c_list (FALSE);
X	    t->right = thenpart ();
X	    return t;
X
X	default:
X	    peeksym = c;
X	    return (C_Op *)NULL;
X    }
X}
X
Xstatic C_Op	*caselist()
X{
X    register C_Op	*t = (C_Op *)NULL;
X
X    while ((peeksym = yylex (CONTIN)) != ESAC)
X	t = list (t, casepart ());
X
X    return t;
X}
X
Xstatic C_Op	*casepart ()
X{
X    register C_Op	*t = (C_Op *)tree (sizeof (C_Op));
X
X    t->type = TPAT;
X    t->words = pattern ();
X    musthave (')', 0);
X    t->left = c_list (FALSE);
X
X    if ((peeksym = yylex (CONTIN)) != ESAC)
X	musthave (BREAK, CONTIN);
X
X    return t;
X}
X
Xstatic char	**pattern()
X{
X    register int	c, cf;
X
X    cf = CONTIN;
X
X    do
X    {
X	musthave (WORD, cf);
X	word (yylval.cp);
X	cf = 0;
X    } while ((c = yylex(0)) == '|');
X
X    peeksym = c;
X    word (NOWORD);
X    return copyw();
X}
X
Xstatic char	**wordlist()
X{
X    register int	c;
X
X    if ((c = yylex(0)) != IN) 
X    {
X	peeksym = c;
X	return (char **)NULL;
X    }
X
X    startl = FALSE;
X    while ((c = yylex (0)) == WORD)
X	word (yylval.cp);
X
X    word (NOWORD);
X    peeksym = c;
X
X    return copyw();
X}
X
X/*
X * supporting functions
X */
X
Xstatic C_Op	*list (t1, t2)
Xregister C_Op	*t1, *t2;
X{
X    if (t1 == (C_Op *)NULL)
X	return t2;
X
X    if (t2 == (C_Op *)NULL)
X	return t1;
X
X    return block (TLIST, t1, t2, NOWORDS);
X}
X
Xstatic C_Op	*block (type, t1, t2, wp)
XC_Op		*t1, *t2;
Xchar			**wp;
X{
X    register C_Op *t = (C_Op *)tree (sizeof (C_Op));
X
X    t->type = type;
X    t->left = t1;
X    t->right = t2;
X    t->words = wp;
X    return t;
X}
X
Xstatic struct res {
X    char	*r_name;
X    int		r_val;
X} restab[] = {
X    {	"for",		FOR},		{"case",	CASE},
X	{"esac",	ESAC},		{"while",	WHILE},
X	{"do",		DO},		{"done",	DONE},
X	{"if",		IF},		{"in",		IN},
X	{"then",	THEN},		{"else",	ELSE},
X	{"elif",	ELIF},		{"until",	UNTIL},
X	{"fi",		FI},
X
X	{";;",		BREAK},		{"||",		LOGOR},
X	{"&&",		LOGAND},	{"{",		'{'},
X	{"}",		'}'},
X
X	{(char *)NULL,	0}
X};
X
Xstatic int	rlookup (n)
Xregister char	*n;
X{
X    register struct res		*rp = restab;
X
X    while ((rp->r_name != (char *)NULL) && strcmp (rp->r_name, n))
X	rp++;
X
X    return rp->r_val;
X}
X
Xstatic C_Op	*namelist(t)
Xregister C_Op	*t;
X{
X    if (iolist) 
X    {
X	iolist = addword ((char *)NULL, iolist);
X	t->ioact = copyio ();
X    }
X    
X    else
X	t->ioact = (IO_Actions **)NULL;
X
X    if ((t->type != TCOM) && (t->type != TFUNC))
X    {
X	if ((t->type != TPAREN) && (t->ioact != (IO_Actions **)NULL))
X	{
X	    t = block (TPAREN, t, NOBLOCK, NOWORDS);
X	    t->ioact = t->left->ioact;
X	    t->left->ioact = (IO_Actions **)NULL;
X	}
X    }
X
X    else
X    {
X	word (NOWORD);
X	t->words = copyw();
X    }
X
X    return t;
X}
X
Xstatic char	**copyw ()
X{
X    register char **wd = getwords (wdlist);
X
X    wdlist = (Word_B *)NULL;
X    return wd;
X}
X
Xstatic void	word (cp)
Xchar		*cp;
X{
X    wdlist = addword (cp, wdlist);
X}
X
Xstatic IO_Actions	**copyio ()
X{
X    IO_Actions	**iop = (IO_Actions **)getwords (iolist);
X
X    iolist = (Word_B *)NULL;
X    return iop;
X}
X
Xstatic IO_Actions	*io (u, f, cp)
Xint			f, u;
Xchar			*cp;
X{
X    register IO_Actions *iop = (IO_Actions *)tree (sizeof (IO_Actions));
X
X    iop->io_unit = u;
X    iop->io_flag = f;
X    iop->io_name = cp;
X    iolist = addword ((char *)iop, iolist);
X    return iop;
X}
X
Xstatic void	yyerror (s)
Xchar		*s;
X{
X    yynerrs++;
X
X    if (talking && e.iop <= iostack) 
X    {
X	multiline = 0;
X
X	while ((eofc () == 0) && (yylex (0) != NL))
X	    ;
X    }
X
X    print_error (s);
X    fail ();
X}
X
Xstatic int	yylex (cf)
Xint		cf;
X{
X    register int	c, c1;
X    bool		atstart;
X
X    if ((c = peeksym) > 0) 
X    {
X	peeksym = 0;
X
X	if (c == NL)
X	    startl = TRUE;
X
X	return c;
X    }
X
X    e.linep = e.cline;
X    atstart = startl;
X    startl = FALSE;
X    yylval.i = 0;
X
Xloop:
X    while ((c = Getc (0)) == SP || c == '\t')
X	;
X
X    switch (c) 
X    {
X	default:
X	    if (isdigit (c)) 
X	    {
X		unget (c1 = Getc(0));
X
X		if ((c1 == '<') || (c1 == '>'))
X		{
X		    iounit = c - '0';
X		    goto loop;
X		}
X
X		*e.linep++ = (char)c;
X		c = c1;
X	    }
X
X	    break;
X
X	case '#':
X	    while ((c = Getc(0)) != 0 && (c != NL))
X		;
X
X	    unget(c);
X	    goto loop;
X
X	case 0:
X	    return c;
X
X	case '$':
X	    *e.linep++ = (char)c;
X
X	    if ((c = Getc(0)) == '{') 
X	    {
X		if ((c = collect (c, '}')) != '\0')
X		    return (c);
X
X		goto pack;
X	    }
X
X	    break;
X
X	case '`':
X	case '\'':
X	case '"':
X	    if ((c = collect (c, c)) != '\0')
X		return c;
X
X	    goto pack;
X
X	case '|':
X	case '&':
X	case ';':
X	    if ((c1 = dual (c)) != '\0') 
X	    {
X		startl = TRUE;
X		return c1;
X	    }
X
X	case '(':
X	case ')':
X	    startl = TRUE;
X	    return c;
X
X	case '^':
X	    startl = TRUE;
X	    return '|';
X
X	case '>':
X	case '<':
X	    diag (c);
X	    return c;
X
X	case NL:
X	    gethere ();
X	    startl = TRUE;
X
X	    if (multiline || (cf & CONTIN))
X	    {
X		if (talking && e.iop <= iostack)
X		{
X		    Add_History (FALSE);
X		    put_prompt (ps2->value);
X		}
X
X		if (cf & CONTIN)
X		    goto loop;
X	    }
X
X	    return(c);
X    }
X
X    unget (c);
X
Xpack:
X    while (((c = Getc (0)) != 0) && (!any ((char)c, "`$ '\"\t;&<>()|^\n")))
X    {
X	if (e.linep >= e.eline)
X	    print_error ("sh: word too long\n");
X
X	else
X	    *e.linep++ = (char)c;
X    }
X
X    unget (c);
X
X    if (any ((char)c, spcl2))
X	goto loop;
X
X    *e.linep++ = '\0';
X
X    if (atstart && (c = rlookup (e.cline)) != 0) 
X    {
X	startl = TRUE;
X	return c;
X    }
X
X    yylval.cp = strsave (e.cline, areanum);
X    return WORD;
X}
X
Xstatic int	collect (c, c1)
Xregister int	c, c1;
X{
X    char *s = "x\n";
X
X    *e.linep++ = (char)c;
X
X    while ((c = Getc (c1)) != c1) 
X    {
X	if (c == 0) 
X	{
X	    unget (c);
X	    *s = (char)c1;
X	    S_puts ("sh: no closing ");
X	    yyerror (s);
X	    return YYERRCODE;
X	}
X
X	if (talking && (c == NL) && (e.iop <= iostack))
X	{
X	    Add_History (FALSE);
X	    put_prompt (ps2->value);
X	}
X
X	*e.linep++ = (char)c;
X    }
X
X    *e.linep++ = (char)c;
X    return 0;
X}
X
X/* Check for &&, || and ;; */
X
Xstatic int	dual (c)
Xregister int	c;
X{
X    char		s[3];
X    register char	*cp = s;
X
X/* Get the next character and set up double string.  Look up in valid
X * operators.  If invalid, unget character
X */
X
X    *cp++ = (char)c;
X    *cp++ = (char)Getc (0);
X    *cp = 0;
X
X    if ((c = rlookup (s)) == 0)
X	unget (*--cp);
X
X    return c;
X}
X
X/* Process I/O re-direction */
X
Xstatic void	diag (ec)
Xregister int	ec;
X{
X    register int	c;
X
X    if (((c = Getc (0)) == '>') || (c == '<'))
X    {
X	if (c != ec)
X	    yyerror (syntax_err);
X
X	yylval.i = (ec == '>') ? IOWRITE | IOCAT : IOHERE;
X	c = Getc(0);
X    }
X    
X    else
X	yylval.i = (ec == '>') ? IOWRITE : IOREAD;
X
X    if ((c != '&') || (yylval.i == IOHERE))
X	unget (c);
X
X    else
X	yylval.i |= IODUP;
X}
X
X/* Get a new tree leaf structure */
X
Xstatic char	*tree (size)
Xunsigned int	size;
X{
X    register char *t;
X
X    if ((t = getcell (size)) == (char *)NULL)
X    {
X	S_puts ("sh: command line too complicated\n");
X	fail ();
X    }
X
X    return t;
X}
SHAR_EOF
echo "File shell/sh2.c is complete"
chmod 0644 shell/sh2.c || echo "restore of shell/sh2.c fails"
set `wc -c shell/sh2.c`;Sum=$1
if test "$Sum" != "15090"
then echo original size 15090, current size $Sum;fi
echo "x - extracting shell/sh3.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh3.c &&
X/* MS-DOS SHELL - Parse Tree Executor
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: sh3.c 1.1 90/01/25 13:41:24 MS_user Exp $
X *
X *    $Log:	sh3.c $
X * Revision 1.1  90/01/25  13:41:24  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
X#include "sh.h"
X
X/* static Function and string declarations */
X
Xstatic int	forkexec (C_Op *, int, int, int, char **);
Xstatic bool	iosetup (IO_Actions *, int, int);
Xstatic C_Op	**find1case (C_Op *, char *);
Xstatic C_Op	*findcase (C_Op *, char *);
Xstatic void	echo (char **);
Xstatic void	setsig (int, int (*)());
Xstatic int	rexecve (char *, char **, char **, bool);
Xstatic int	Execute_program (char *, char **, char **, bool);
Xstatic int	S_spawnve (char *, char **, char **);
Xstatic void	get_sys_info (void);
Xstatic void	EMS_error (char *, int);
Xstatic int	EMS_Close (void);
Xstatic int	build_command_line (char *, char **, char **);
Xstatic void	Clear_Extended_File (void);
Xstatic int	setstatus (int);
X
Xstatic char	*AE2big = "arg/env list too big";
Xstatic char	*EMS_emsg = "Warning: EMS Error (%x)\n";
X			/* Extended Command line processing file name	*/
Xstatic char		*Extend_file = (char *)NULL;
Xstatic unsigned int	SW_EMsize;	/* Number of extend memory blks	*/
X
X/*
X * execute tree recursively
X */
X
Xint		execute (t, pin, pout, act)
Xregister C_Op	*t;
Xint		pin;
Xint		pout;
Xint		act;
X{
X    register C_Op	*t1;
X    int			i, localpipe;
X    char		*cp, **wp;
X    char		**Local_Tword;
X    Var_List		*vp;
X    Break_C		bc;
X    Break_C		*S_RList;		/* Save link pointers	*/
X    Break_C		*S_BList;
X    Break_C		*S_SList;
X    int			Local_depth;		/* Save local values	*/
X    int			Local_areanum;
X    int			rv = 0;
X
X/* End of tree ? */
X
X    if (t == (C_Op *)NULL)
X	return 0;
X
X/* Save original and Increment execute function recursive level */
X
X    Local_depth = Execute_stack_depth++;
X
X/* Save original and increment area number */
X
X    Local_areanum = areanum++;
X
X/* Save the exit points from SubShells, functions and for/whiles */
X
X    S_RList = Return_List;
X    S_BList = Break_List;
X    S_SList = SShell_List;
X
X/* Expand any arguments */
X
X    wp = (Local_Tword = t->words) != (char **)NULL
X	 ? eval (Local_Tword, (t->type == TCOM) ? DOALL : DOALL & ~DOKEY)
X	 : (char **)NULL;
X
X/* Switch on tree node type */
X
X    switch (t->type)
X    {
X	case TFUNC:			/* name () { list; }	*/
X	    Save_Function (t, FALSE);
X	    break;
X
X/* In the case of a () command string, we need to save and restore the
X * current environment, directory and traps (can't think of anything else).
X * For any other, we just restore the current directory.  Also, we don't
X * want changes in the Variable list header saved for SubShells, because
X * we are effectively back at execute depth zero.
X */
X	case TPAREN:			/* ()			*/
X	    if ((rv = Create_NG_VL ()) == -1)
X		break;
X
X	    if (setjmp (bc.brkpt) == 0)
X	    {
X		Return_List = (Break_C *)NULL;
X		Break_List  = (Break_C *)NULL;
X		bc.nextlev  = SShell_List;
X		SShell_List = &bc;
X		rv = forkexec (t, pin, pout, act, wp);
X	    }
X
X/* Restore the original environment */
X
X	    Return_List	= S_RList;
X	    Break_List	= S_BList;
X	    SShell_List	= S_SList;
X	    Restore_Environment (rv, Local_depth);
X	    break;
X
X/* After a normal command, we need to restore the original directory.  Note
X * that a cd will have updated the variable $~, so no problem
X */
X
X	case TCOM:			/* A command process	*/
X	    rv = forkexec (t, pin, pout, act, wp);
X	    Restore_Dir ();
X	    break;
X
X	case TPIPE:			/* Pipe processing		*/
X	    if ((rv = openpipe ()) < 0)
X		break;
X
X/* Create pipe, execute command, reset pipe, execute the other side, close
X * the pipe and fini
X */
X
X	    localpipe = remap (rv);
X	    execute (t->left, pin, localpipe, 0);
X	    lseek (localpipe, 0L, SEEK_SET);
X	    rv = execute (t->right, localpipe, pout, 0);
X	    closepipe (localpipe);
X	    break;
X
X	case TLIST:			/* Entries in a for statement	*/
X	    execute (t->left, pin, pout, 0);
X	    rv = execute (t->right, pin, pout, 0);
X	    break;
X
X	case TASYNC:			/* Async - not supported	*/
X	    rv = -1;
X	    S_puts ("sh: Async commands not supported\n");
X	    setstatus (rv);
X	    break;
X
X	case TOR:			/* || and &&			*/
X	case TAND:
X	    rv = execute (t->left, pin, pout, 0);
X
X	    if (((t1 = t->right) != (C_Op *)NULL) &&
X		((rv == 0) == (t->type == TAND)))
X		rv = execute (t1, pin, pout, 0);
X
X	    break;
X
X	case TFOR:			/* First part of a for statement*/
X
X/* for x do...done - use the parameter values.  Need to know how many as
X * it is not a NULL terminated array
X */
X
X	    if (wp == (char **)NULL)
X	    {
X		wp = dolv + 1;
X
X		if ((i = dolc) < 0)
X		    i = 0;
X	    }
X
X/* for x in y do...done - find the start of the variables and use them all */
X
X	    else
X	    {
X		i = -1;
X		while (*wp++ != (char *)NULL)
X		    ;
X	    }
X
X/* Create the loop variable. */
X
X	    vp = lookup (t->str, TRUE);
X
X/* Set up a long jump return point before executing the for function so that
X * the continue statement is executed, ie we reprocessor the for condition.
X */
X
X	    while (rv = setjmp (bc.brkpt))
X	    {
X
X/* Restore the current stack level and clear out any I/O */
X
X		Restore_Environment (0, Local_depth + 1);
X		Return_List = S_RList;
X		SShell_List = S_SList;
X
X/* If this is a break - clear the variable and terminate the while loop and
X * switch statement
X */
X
X		if (rv == BC_BREAK)
X		    break;
X	    }
X
X	    if (rv == BC_BREAK)
X		break;
X
X/* Process the next entry - Add to the break/continue chain */
X
X	    bc.nextlev = Break_List;
X	    Break_List = &bc;
X
X/* Execute the command tree */
X
X	    for (t1 = t->left; i-- && *wp != NULL;)
X	    {
X		setval (vp, *wp++);
X		rv = execute (t1, pin, pout, 0);
X	    }
X
X/* Remove this tree from the break list */
X
X	    Break_List = S_BList;
X	    break;
X
X/* While and Until function.  Similar to the For function.  Set up a
X * long jump return point before executing the while function so that
X * the continue statement is executed OK.
X */
X
X	case TWHILE:			/* WHILE and UNTIL functions	*/
X	case TUNTIL:
X	    while (rv = setjmp (bc.brkpt))
X	    {
X
X/* Restore the current stack level and clear out any I/O */
X
X		Restore_Environment (0, Local_depth + 1);
X		Return_List = S_RList;
X		SShell_List = S_SList;
X
X/* If this is a break, terminate the while and switch statements */
X
X		if (rv == BC_BREAK)
X		    break;
X	    }
X
X	    if (rv == BC_BREAK)
X		break;
X
X/* Set up links */
X
X	    bc.nextlev = Break_List;
X	    Break_List = &bc;
X	    t1 = t->left;
X
X	    while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE))
X		rv = execute (t->right, pin, pout, 0);
X
X	    Break_List = S_BList;
X	    break;
X
X	case TIF:			/* IF and ELSE IF functions	*/
X	case TELIF:
X	    rv = !execute (t->left, pin, pout, 0)
X	   		 ? execute (t->right->left, pin, pout, 0)
X			 : execute (t->right->right, pin, pout, 0);
X	    break;
X
X	case TCASE:			/* CASE function		*/
X	    if ((cp = evalstr (t->str, DOSUB | DOTRIM)) == (char *)NULL)
X		cp = null;
X
X	    if ((t1 = findcase (t->left, cp)) != (C_Op *)NULL)
X		rv = execute (t1, pin, pout, 0);
X
X	    break;
X
X	case TBRACE:			/* {} statement			*/
X	    if ((rv >= 0) && ((t1 = t->left) != (C_Op *)NULL))
X		rv = execute (t1, pin, pout, 0);
X
X	    break;
X    }
X
X/* Processing Completed - Restore environment */
X
X    t->words		= Local_Tword;
X    Execute_stack_depth = Local_depth;
X
X/* Remove unwanted malloced space */
X
X    freehere (areanum);
X    freearea (areanum);
X
X    areanum		= Local_areanum;
X
X/* Check for interrupts */
X
X    if (talking && SW_intr)
X    {
X	closeall ();
X	fail ();
X    }
X
X/* Check for traps */
X
X    if ((i = trapset) != 0)
X    {
X	trapset = 0;
X	runtrap (i);
X    }
X
X    return rv;
X}
X
X/*
X * Restore the original directory
X */
X
Xvoid	Restore_Dir ()
X{
X    unsigned int	dummy;
X
X    _dos_setdrive (tolower(*C_dir->value) - 'a' + 1, &dummy);
X
X    if (chdir (&C_dir->value[2]) != 0)
X    {
X	S_puts ("Warning: current directory reset to /\n");
X	chdir ("/");
X	Getcwd ();
X    }
X}
X
X/*
X * Ok - execute the program, resetting any I/O required
X */
X
Xstatic int	forkexec (t, pin, pout, act, wp)
Xregister C_Op	*t;
Xint		pin;
Xint		pout;
Xint		act;
Xchar		**wp;
X{
X    int		rv = -1;
X    int		(*shcom)(C_Op *) = (int (*)())NULL;
X    char	*cp;
X    IO_Actions	**iopp;
X    int		resetsig = 0;
X    char	**owp = wp;
X    bool	spawn = FALSE;
X    Fun_Ops	*fop;
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 (FL_TEST ('x'))
X	    echo (cp != (char *)NULL ? wp : owp);
X
X/* Is it only an assignement? */
X
X	if ((cp == (char *)NULL) && (t->ioact == (IO_Actions **)NULL))
X	{
X	    while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
X		;
X
X	    return setstatus (0);
X	}
X
X/* Check for built in commands */
X
X	else if (cp != (char *)NULL)
X	    shcom = inbuilt (cp);
X    }
X
X/* Unix fork simulation? */
X
X    t->words = wp;
X    if (shcom == NULL && (act & FEXEC) == 0)
X    {
X	spawn = TRUE;
X
X	if (talking)
X	{
X#ifdef SIGQUIT
X	    signal (SIGQUIT, SIG_IGN);
X#endif
X	    signal (SIGINT, SIG_IGN);
X	    resetsig = 1;
X	}
X    }
X
X/* Set any variables */
X
X    while (((cp = *owp++) != (char *)NULL) && assign (cp, COPYV))
X    {
X	if (shcom == NULL)
X	    s_vstatus (lookup (cp, TRUE), EXPORT);
X    }
X
X/* We cannot close the pipe, because once the exec/spawn has taken place
X * the processing of the pipe is not yet complete.
X */
X
X    if (pin != NOPIPE)
X    {
X	S_dup2 (pin, STDIN_FILENO);
X	lseek (STDIN_FILENO, 0L, SEEK_SET);
X    }
X
X    if (pout != NOPIPE)
X    {
X	S_dup2 (pout, STDOUT_FILENO);
X	lseek (STDOUT_FILENO, 0L, SEEK_END);
X    }
X
X/* Set up any other IO required */
X
X    if ((iopp = t->ioact) != (IO_Actions **)NULL)
X    {
X	while (*iopp != (IO_Actions *)NULL)
X	{
X	    if (iosetup (*iopp++, pin, pout))
X		return rv;
X	}
X    }
X
X    if (shcom)
X	return restore_std (setstatus ((*shcom)(t)));
X
X/* All fids above 10 are autoclosed in the exec file because we have used
X * the O_NOINHERIT flag.  Note I patched open.obj to pass this flag to the
X * open function.
X */
X
X    if (resetsig)
X    {
X#ifdef SIGQUIT
X	signal (SIGQUIT, SIG_IGN);
X#endif
X	signal (SIGINT, onintr);
X    }
X
X    if (t->type == TPAREN)
X	return restore_std (execute (t->left, NOPIPE, NOPIPE, FEXEC));
X
X/* Are we just changing the I/O re-direction for the shell ? */
X
X    if (wp[0] == NULL)
X    {
X	if (spawn)
X	    restore_std (0);
X
X	return 0;
X    }
X
X/* No - Check for a function the program.  At this point, we need to put
X * in some processing for return.
X */
X
X    if ((fop = Fun_Search (wp[0])) != (Fun_Ops *)NULL)
X    {
X	char			**s_dolv = dolv;
X	int			s_dolc   = dolc;
X	Break_C			*s_RList = Return_List;
X	Break_C			*s_BList = Break_List;
X	Break_C			*s_SList = SShell_List;
X	int			LS_depth = Execute_stack_depth;
X	Break_C			bc;
X
X/* Set up $0..$n for the function */
X
X	dolv = wp;
X	for (dolc = 0; dolv[dolc] != (char *)NULL; ++dolc);
X	setval (lookup ("#", TRUE), putn (dolc));
X
X	if (setjmp (bc.brkpt) == 0)
X	{
X	    Break_List  = (Break_C *)NULL;
X	    bc.nextlev  = Return_List;
X	    Return_List = &bc;
X	    rv = execute (fop->tree->left, NOPIPE, NOPIPE, FEXEC);
X	}
X
X/* A return has been executed - Unlike, while and for, we just need to
X * restore the local execute stack level and the return will restore
X * the correct I/O.
X */
X
X	else
X	    rv = getn (lookup ("?", FALSE)->value);
X
X/* Restore the old $0, and previous return address */
X
X	Break_List  = s_BList;
X	Return_List = s_RList;
X	SShell_List = s_SList;
X	dolv	    = s_dolv;
X	dolc	    = s_dolc;
X	Restore_Environment (rv, LS_depth);
X	setval (lookup ("#", TRUE), putn (dolc));
X	return rv;
X    }
X
X/* Ok - execute the program */
X
X    return restore_std (rexecve (wp[0], wp, makenv (), spawn));
X}
X
X/*
X * Restore Local Environment
X */
X
Xvoid	Restore_Environment (retval, stack)
Xint	retval;
Xint	stack;
X{
X    Execute_stack_depth = stack;
X    Delete_G_VL ();
X    Restore_Dir ();
X    restore_std (setstatus (retval));
X}
X
X/*
X * Set up I/O redirection.  0< 1> are ignored as required within pipelines.
X */
X
Xstatic bool		iosetup (iop, pipein, pipeout)
Xregister IO_Actions	*iop;
Xint			pipein;
Xint			pipeout;
X{
X    register int	u;
X    char		*cp, *msg;
X
X    if (iop->io_unit == IODEFAULT)	/* take default */
X	iop->io_unit = (iop->io_flag & (IOREAD | IOHERE)) ? STDIN_FILENO
X							  : STDOUT_FILENO;
X
X/* Check for pipes */
X
X    if ((pipein != NOPIPE) && (iop->io_unit == STDIN_FILENO))
X	return FALSE;
X
X    if ((pipeout != NOPIPE) && (iop->io_unit == STDOUT_FILENO))
X	return FALSE;
X
X    msg = (iop->io_flag & (IOREAD | IOHERE)) ? "open" : "create";
X
X    if ((iop->io_flag & IOHERE) == 0)
X    {
X	if ((cp = evalstr (iop->io_name, DOSUB | DOTRIM)) == (char *)NULL)
X	    return TRUE;
X    }
X
X    if (iop->io_flag & IODUP)
X    {
X	if ((cp[1]) || !isdigit (*cp) && *cp != '-')
X	{
X	    print_error ("%s: illegal >& argument\n", cp);
X	    return TRUE;
X	}
X
X	if (*cp == '-')
X	    iop->io_flag = IOCLOSE;
X
X	iop->io_flag &= ~(IOREAD | IOWRITE);
X    }
X
X/* Open the file in the appropriate mode */
X
X    switch (iop->io_flag)
X    {
X	case IOREAD:				/* <			*/
X	    u = S_open (FALSE, cp, O_RDONLY);
X	    break;
X
X	case IOHERE:				/* <<			*/
X	case IOHERE | IOXHERE:
X	    u = herein (iop->io_name, iop->io_flag & IOXHERE);
X	    cp = "here file";
X	    break;
X
X	case IOWRITE | IOCAT:			/* >>			*/
X	    if (check_rsh (cp))
X		return TRUE;
X
X	    if ((u = S_open (FALSE, cp, O_WRONLY | O_TEXT)) >= 0)
X	    {
X		lseek (u, 0L, SEEK_END);
X		break;
X	    }
X
X	case IOWRITE:				/* >			*/
X	    if (check_rsh (cp))
X		return TRUE;
X
X	    u = S_open (FALSE, cp, O_CMASK, 0666);
X	    break;
X
X	case IODUP:				/* >&			*/
X	    if (check_rsh (cp))
X		return TRUE;
X
X	    u = S_dup2 (*cp - '0', iop->io_unit);
X	    break;
X
X	case IOCLOSE:				/* >-			*/
X	    if ((iop->io_unit >= STDIN_FILENO) &&
X		(iop->io_unit <= STDERR_FILENO))
X		S_dup2 (-1, iop->io_unit);
X
X	    S_close (iop->io_unit, TRUE);
X	    return FALSE;
X    }
X
X    if (u < 0)
X    {
X	print_warn ("%s: cannot %s\n", cp, msg);
X	return TRUE;
X    }
X
X    else if (u != iop->io_unit)
X    {
X	S_dup2 (u, iop->io_unit);
X	S_close (u, TRUE);
X    }
X
X    return FALSE;
X}
X
X/*
X * -x flag - echo command to be executed
X */
X
Xstatic void	echo (wp)
Xregister char	**wp;
X{
X    register int	i;
X
X    S_putc ('+');
X
X    for (i = 0; wp[i] != (char *)NULL; i++)
X    {
X	S_putc (SP);
X	S_puts (wp[i]);
X    }
X
X    S_putc (NL);
X}
X
Xstatic C_Op	**find1case (t, w)
XC_Op		*t;
Xchar		*w;
X{
X    register C_Op	*t1;
X    C_Op		**tp;
X    register char	**wp, *cp;
X
X    if (t == (C_Op *)NULL)
X	return (C_Op **)NULL;
X
X    if (t->type == TLIST)
X    {
X	if ((tp = find1case (t->left, w)) != (C_Op *)NULL)
X	    return tp;
X
X	t1 = t->right;	/* TPAT */
X    }
X
X    else
X	t1 = t;
X
X    for (wp = t1->words; *wp != (char *)NULL;)
X    {
X	if ((cp = evalstr (*(wp++), DOSUB)) && gmatch (w, cp, FALSE))
X	    return &t1->left;
X    }
X
X    return (C_Op **)NULL;
X}
X
Xstatic C_Op	*findcase (t, w)
XC_Op		*t;
Xchar		*w;
X{
X    register C_Op **tp;
X
X    return ((tp = find1case (t, w)) != (C_Op **)NULL) ? *tp : (C_Op *)NULL;
X}
X
X/*
X * Set up the status on exit from a command
X */
X
Xstatic int	setstatus (s)
Xregister int	s;
X{
X    exstat = s;
X    setval (lookup ("?", TRUE), putn (s));
X    return s;
X}
X
X/*
X * PATH-searching interface to execve.  If getenv ("PATH") were kept
X * up-to-date, execvp might be used.
X */
X
Xstatic int	rexecve (c, v, envp, d_flag)
Xchar		*c;
Xchar		**v;
Xchar		**envp;
Xbool		d_flag;
X{
X    register char	*sp;
X    int			res;
X    char		*em;
X    bool		eloop;
X
X/* If the environment is null - It is too big - error */
X
X    if (envp == (char **)NULL)
X	em = AE2big;
X
X    else
X    {
X	sp = any ('/', c) ? null : path->value;
X
X	do
X	{
X	    sp = path_append (sp, c, e.linep);
X
X	    if ((res = Execute_program (e.linep, v, envp, d_flag)) != -1)
X		return res;
X
X	    eloop = TRUE;
X
X	    switch (errno)
X	    {
X
X/* No entry for the file - if the file exists, execute it as a shell
X * script
X */
X		case ENOENT:
X		    if ((res = O_for_execute (e.linep)) >= 0)
X		    {
X			S_close (res, TRUE);
X			*v = e.linep;
X			em = *--v;
X			*v = e.linep;
X		        res = Execute_program (lookup (shell, FALSE)->value,
X					       v, envp, d_flag);
X			*v = em;
X
X			if (res != -1)
X			return res;
X
X			em = "no Shell";
X		    }
X
X		    else
X			em = "not found";
X
X		    eloop = FALSE;
X		    break;
X
X		case ENOEXEC:
X		    em = "program corrupt";
X		    break;
X
X		case ENOMEM:
X		    em = "program too big";
X		    break;
X
X		case E2BIG:
X		    em = AE2big;
X		    break;
X
X		default:
X		    em = "cannot execute";
X		    eloop = FALSE;
X		    break;
X	    }
X	} while ((sp != (char *)NULL) && !eloop);
X    }
X
X    print_warn ("%s: %s\n", c, em);
X
X    if (!d_flag)
X	exit (-1);
X
X    return -1;
X}
X
X/*
X * Run the command produced by generator `f' applied to stream `arg'.
X */
X
Xint		run (argp, f)
XIO_Args		*argp;
Xint		(*f)(IO_State *);
X{
X    Word_B		*swdlist = wdlist;
X    Word_B		*siolist = iolist;
X    jmp_buf		ev, rt;
X    int			*ofail = failpt;
X    int			rv = -1;
X    Break_C		*S_RList = Return_List;	/* Save loval links	*/
X    Break_C		*S_BList = Break_List;
X    Break_C		*S_SList = SShell_List;
X    Break_C		bc;
X    int			LS_depth = Execute_stack_depth;
X    C_Op		*outtree;
X
X/* Create a new environment in which to run */
X
X    if (Create_NG_VL () == -1)
X	return -1;
X
X/* Create a new save area */
X
X    areanum++;
X
X/* Execute the command */
X
X    if (newenv (setjmp (errpt = ev)) == FALSE)
X    {
X	Return_List = (Break_C *)NULL;
X	Break_List  = (Break_C *)NULL;
X	wdlist      = (Word_B *)NULL;
X	iolist      = (Word_B *)NULL;
X
X	pushio (argp, f);
X	e.iobase = e.iop;
X	yynerrs = 0;
X
X
X	if ((setjmp (failpt = rt) == 0) &&
X	    ((outtree = yyparse ()) != (C_Op *)NULL))
X	{
X	    if (setjmp (bc.brkpt) == 0)
X	    {
X		bc.nextlev = SShell_List;
X		SShell_List = &bc;
X		rv = execute (outtree, NOPIPE, NOPIPE, 0);
X	    }
X
X	    else
X		rv = getn (lookup ("?", FALSE)->value);
X	}
X
X	quitenv ();
X    }
X
X/* Restore the environment */
X
X    Return_List = S_RList;
X    Break_List = S_BList;
X    SShell_List = S_SList;
X    wdlist = swdlist;
X    iolist = siolist;
X    failpt = ofail;
X
X    Restore_Environment (rv, LS_depth);
X
X    freearea (areanum--);
X    return rv;
X}
X
X/* Exec or spawn the program ? */
X
Xstatic int	Execute_program (path, parms, envp, d_flag)
Xchar		*path;
Xchar		**parms;
Xchar		**envp;
Xbool		d_flag;
X{
X    return setstatus ((!d_flag) ? execve (path, parms, envp)
X				: S_spawnve (path, parms, envp));
X}
X
X/* Set up to spawn a process */
X
Xstatic int	S_spawnve (path, parms, envp)
Xchar		*path;
Xchar		**parms;
Xchar		**envp;
X{
X    unsigned int	c_cur = (unsigned int)(_psp - 1);
X    unsigned int	size = 0;
X    char		*ep, *ep1;
X    int			res, serrno;
X    struct MCB_list	*mp = (struct MCB_list *)((unsigned long)c_cur << 16L);
X
X
X/* Check to see if the file exists */
X
X    strcpy (path_line, path);
X
X    if ((ep = strrchr (path_line, '/')) == (char *)NULL)
X	ep = path_line;
X
X/* If no dot in name - check for .exe and .com files */
X
X    if ((ep1 = strchr (ep, '.')) == (char *)NULL)
X    {
X	ep1 = ep + strlen (ep);
X	strcpy (ep1, ".exe");
X
X	if ((res = access (path_line, F_OK)) != 0)
X	{
X	    strcpy (ep1, ".com");
X	    res = access (path_line, F_OK);
X	}
X
X	if (res != 0)
X	    return -1;
X    }
X
X    else if ((stricmp (ep1, ".exe") != 0) && (stricmp (ep1, ".com") != 0))
X    {
X	errno = ENOEXEC;
X	return -1;
X    }
X
X    else if (access (path_line, F_OK) != 0)
X	return -1;
X
X/* Process the command line.  If no swapping, we have executed the program */
X
X    res = build_command_line (path_line, parms, envp);
X
X    if ((Swap_Mode == SWAP_OFF) || res)
X	return res;
X
X/* Find the length of the swap area */
X
X    while ((mp = (struct MCB_list *)((unsigned long)c_cur << 16L))->MCB_type
X	    == MCB_CON)
X    {
X	if ((mp->MCB_pid != _psp) && (mp->MCB_pid != 0) &&
X	    (mp->MCB_type != MCB_END))
X	{
X	    Clear_Extended_File ();
X	    print_error ("Fatal: Memory chain corrupt\n");
X	    return -1;
X	}
X
X	c_cur += (mp->MCB_len + 1);
X	size += mp->MCB_len + 1;
X    }
X
X/*
X * Convert swap size from paragraphs to 16K blocks.
X */
X
X    if (size == 0)
X	size = mp->MCB_len + 1;
X
X    SW_Blocks = (size / 0x0400) + 1;
X
X/* OK Now we've set up the FCB's, command line and opened the swap file.
X * Get some sys info for the swapper and execute my little assembler
X * function to swap us out
X */
X
X    get_sys_info ();
X
X/* Ok - 3 methods of swapping */
X
X/* If expanded memory - try that */
X
X    if (Swap_Mode & SWAP_EXPAND)
X    {
X	int	cr;
X	SW_Mode = 3;			/* Set Expanded memory swap	*/
X
X	res = SA_spawn (envp);
X	cr = EMS_Close ();		/* Close EMS			*/
X
X	if ((res != -2) && cr)		/* Report Close error ?		*/
X	{
X	    res = -2;
X	    errno = cr;
X	}
X
X	if (res == -2)
X	    EMS_error ("Expanded memory swap failed (%x)\n", errno);
X
X	else
X	{
X	    Clear_Extended_File ();
X	    return res;
X	}
X
X/* Failed - disabled */
X
X	Swap_Mode &= (~SWAP_EXPAND);
X    }
X
X    if (Swap_Mode & SWAP_EXTEND)
X    {
X	SW_Mode = 2;			/* Set Extended memory swap	*/
X
X        if ((SW_EMsize <= SW_Blocks) ||
X	    ((SW_EMstart - 0x100000L +
X	      ((long)(SW_Blocks - SW_EMsize) * 16L * 1024L)) < 0L))
X	    print_warn ("Not enough Extended memory for swap\n");
X
X	else if ((res = SA_spawn (envp)) == -2)
X	    print_warn ("Extended memory swap failed (%x)\n", errno);
X
X	else
X	{
X	    Clear_Extended_File ();
X	    return res;
X	}
X
X/* Failed - disabled */
X
X	Swap_Mode &= (~SWAP_EXTEND);
X    }
X
X/* Try the disk if available */
X
X    if (Swap_Mode & SWAP_DISK)
X    {
X	if ((SW_fp = S_open (TRUE, g_tempname (), O_SMASK, 0600)) < 0)
X	{
X	    print_error ("No Swap files\n");
X	    errno = ENOSPC;
X	    return -1;
X	}
X
X	SW_Mode = 1;			/* Set Disk file swap		*/
X
X/* Execute the program */
X
X	res = SA_spawn (envp);
X
X	Clear_Extended_File ();
X
X	if (res == -2)
X	{
X	    print_warn ("Swap file write failed\n");
X	    errno = ENOSPC;
X	    res = -1;
X	}
X
X/* Close the swap file and return the result */
X
X	serrno = errno;
X	S_close (SW_fp, TRUE);
X	errno = serrno;
X	return res;
X    }
X
X/* No swapping available - give up */
X
X    Clear_Extended_File ();
X    print_error ("All Swapping methods failed\n");
X    errno = ENOSPC;
X    return -1;
X}
X
X/* Get some system info */
X
Xstatic void	get_sys_info ()
X{
X    union REGS		or;
X    struct SREGS	sr;
X    char		*sp;
X
X/* Save the interrupt 0 address */
X
X    or.x.ax = 0x3500;
X    intdosx (&or, &or, &sr);
X
X    SW_I0_V_BX = or.x.bx;
X    SW_I0_V_ES = sr.es;
X
X/* Save the interrupt 23 address */
X
X    or.x.ax = 0x3523;
X    intdosx (&or, &or, &sr);
X
X    SW_I23_V_BX = or.x.bx;
X    SW_I23_V_ES = sr.es;
X
X/* Get max Extended memory pages, and convert to 16K blocks.  If Extended
X * memory swapping disabled, set to zero
X */
X
X    or.x.ax = 0x8800;
X    int86 (0x15, &or, &or);
X    SW_EMsize = (Swap_Mode & SWAP_EXTEND) ? or.x.ax / 16 : 0;
X
X/* Check for the Expand Memory System */
X
X    if (!(Swap_Mode & SWAP_EXPAND))
X	return;
X
X    SW_fp = -1;				/* Set EMS handler not defined	*/
X
X    or.x.ax = 0x3567;
X    intdosx (&or, &or, &sr);
X
X    sp = (char *)((unsigned long)(sr.es) << 16L | 10L);
X
X/* If not there - disable */
X
X    if (memcmp ("EMMXXXX0", sp, 8) != 0)
X    {
X	EMS_error ("Warning: EMS not available\n", 0);
X	return;
X    }
X
X    or.h.ah = 0x40;			/* Check status			*/
X    int86 (0x67, &or, &or);
X
X    if (or.h.ah != 0)
X    {
X	EMS_error (EMS_emsg, or.h.ah);
X	return;
X    }
X
X/* Check version greater than 3.2 */
X
X    or.h.ah = 0x46;
X    int86 (0x67, &or, &or);
X
X    if ((or.h.ah != 0) || (or.h.al < 0x32))
X    {
X	EMS_error (EMS_emsg, or.h.ah);
X	return;
X    }
X
X/*  get page frame address */
X
X    or.h.ah = 0x41;
X    int86 (0x67, &or, &or);
X
X    if (or.h.ah != 0)
X    {
X	EMS_error (EMS_emsg, or.h.ah);
X	return;
X    }
X
X    SW_EMSFrame = or.x.bx;		/* Save the page frame		*/
X
X/* Get the number of pages required */
X
X    or.h.ah = 0x43;
X    or.x.bx = SW_Blocks;
X    int86 (0x67, &or, &or);
X
X    if (or.h.ah != 0)
X    {
X	EMS_error (EMS_emsg, or.h.ah);
X	return;
X    }
X
X/* Save the EMS Handler */
X
X    SW_fp = or.x.dx;
X
X/* save EMS page map */
X
X    or.h.ah = 0x47;
X    or.x.dx = SW_fp;
X    int86 (0x67, &or, &or);
X
X    if (or.h.ah != 0)
X    {
X	EMS_error (EMS_emsg, or.h.ah);
X	return;
X    }
X}
X
X/* Print EMS error message */
X
Xstatic void	EMS_error (s, v)
Xchar		*s;
Xint		v;
X{
X    print_warn (s, v);
X    Swap_Mode &= ~(SWAP_EXPAND);
X    EMS_Close ();
X}
X
X
X/* If the handler is defined - close it */
X
Xstatic int	EMS_Close ()
X{
X    union REGS		or;
X    int			res = 0;
X
X    if (SW_fp == -1)
X	return 0;
X
X/* Restore EMS page */
X
X    or.h.ah = 0x48;
X    or.x.dx = SW_fp;
X    int86 (0x67, &or, &or);
X
X    if (or.h.ah != 0)
X	res = or.h.al;
X
X    or.h.ah = 0x45;
X    or.x.dx = SW_fp;
X    int86 (0x67, &or, &or);
X
X    SW_fp = -1;
X    return (res) ? res : or.h.ah;
X}
X
X/* Set up command line.  If the EXTENDED_LINE variable is set, we create
X * a temporary file, write the argument list (one entry per line) to the
X * this file and set the command line to @<filename>.  If NOSWAPPING, we
X * execute the program because I have to modify the argument line
X */
X
Xint	build_command_line (path, argv, envp)
Xchar	*path;
Xchar	**argv;
Xchar	**envp;
X{
X    char		**pl = argv;
X    char		*fname;
X    int			res, fd;
X    char		*pname;
X    FILE		*fp;
X    char		nbuffer[NAME_MAX + 2];
X    bool		found;
X    char		*ep;
X    char		*new_args[3];
X
X/* Find the start of the program name */
X
X    if ((pname = strrchr (path, '/')) == (char *)NULL)
X	pname = path;
X
X    else
X	++pname;
X
X/* Translate process name to MSDOS format */
X
X    Convert_Slashes (path);
X    strupr (path);
X
X/* Extended command line processing */
X
X    Extend_file == (char *)NULL;		/* Set no file		*/
X
X    if ((*(pl++) != (char *)NULL) &&
X	((fname = lookup ("EXTENDED_LINE", FALSE)->value) != null) &&
X	((fp = fopen (fname, "rt")) != (FILE *)NULL))
X    {
X
X/* Loop through the file look for the current program */
X
X	found = FALSE;
X
X	while (fgets (nbuffer, NAME_MAX + 1, fp) != (char *)NULL)
X	{
X	    if ((ep = strchr (nbuffer, '\n')) != (char *)NULL)
X		*ep = 0;
X
X	    if (stricmp (nbuffer, pname) == 0)
X	    {
X		found = TRUE;
X		break;
X	    }
X	}
X
X	fclose (fp);
X
X/* Check parameters don't contain a re-direction parameter */
X
X	if (found)
X	{
X	    char	**pl1 = pl;
X
X	    while (*pl1 != (char *)NULL)
X	    {
X		if (**(pl1++) == '@')
X		{
X		    found = FALSE;
X		    break;
X		}
X	    }
X	}
X
X/* If we find it - create a temporary file and write the stuff */
X
X	if ((found) &&
X	    ((fd = S_open (FALSE, Extend_file = g_tempname (), O_CMASK,
X			   0600)) >= 0))
X	{
X
X/* Copy to end of list */
X
X	    while (*pl != (char *)NULL)
X	    {
X		if (((res = strlen (*pl)) && (write (fd, *pl, res) != res)) ||
X		    (write (fd, "\n", 1) != 1))
X		{
X		    close (fd);
X		    unlink (Extend_file);
X		    Extend_file == (char *)NULL;
X		    errno = ENOSPC;
X		    return -1;
X		}
X
X		++pl;
X	    }
X
X/* Completed write OK */
X
X	    close (fd);
X
X/* Set up cmd_line[1] to contain the filename */
X
X	    memset (cmd_line, 0, CMD_LINE_MAX);
X	    cmd_line[1] = '@';
X	    strcpy (&cmd_line[2], Extend_file);
X	    cmd_line[0] = (char)(strlen (Extend_file) + 1);
X
X/* Correctly terminate cmd_line in no swap mode */
X
X	    if (Swap_Mode != SWAP_OFF)
X		cmd_line[cmd_line[0] + 1] = '\r';
X
X/* If the name in the file is in upper case - use \ for separators */
X
X	    if (isupper (*nbuffer))
X		Convert_Slashes (&cmd_line[2]);
X
X/* OK we are ready to execute */
X
X	    if (Swap_Mode == SWAP_OFF)
X	    {
X		new_args[0] = *argv;
X		new_args[1] = &cmd_line[1];
X		new_args[2] = (char *)NULL;
X		return spawnve (P_WAIT, path, new_args, envp);
X	    }
X
X	    else
X		return 0;
X	}
X    }
X
X/* Check length of Parameter list */
X
X    res = 0;
X    cmd_line[0] = 0;
X    cmd_line[1] = '\r';
X    ep = cmd_line;
X
X/* Skip the first parameter and get the length of the rest */
X
X    if (*argv != (char *)NULL)
X    {
X	while (*pl != (char *)NULL)
X	{
X	    if ((res += (strlen (*pl) + 1)) >= CMD_LINE_MAX)
X	    {
X		errno = E2BIG;
X		return -1;
X	    }
X
X	    strcat (strcat (ep, " "), *(pl++));
X	}
X
X	if (res)
X	    cmd_line[res--] = '\r';
X    }
X
X/* Terminate the line and insert the line length */
X
X    cmd_line[0] = (char)res;
X
X/* If swapping disabled - just execute it */
X
X    return (Swap_Mode == SWAP_OFF) ? spawnve (P_WAIT, path, argv, envp) : 0;
X}
X
X/* Clear Extended command line file */
X
Xstatic void	Clear_Extended_File ()
X{
X    if (Extend_file != (char *)NULL)
X	unlink (Extend_file);
X
X    Extend_file = (char *)NULL;
X}
SHAR_EOF
chmod 0644 shell/sh3.c || echo "restore of shell/sh3.c fails"
set `wc -c shell/sh3.c`;Sum=$1
if test "$Sum" != "28726"
then echo original size 28726, current size $Sum;fi
echo "x - extracting shell/sh4.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh4.c &&
X/* MS-DOS SHELL - 'word' Interpretator
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: sh4.c 1.1 90/01/25 13:41:38 MS_user Exp $
X *
X *    $Log:	sh4.c $
X * Revision 1.1  90/01/25  13:41:38  MS_user
X * Initial revision
X * 
X */
X
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <dirent.h>
X#include <string.h>
X#include <stdlib.h>
X#include <unistd.h>
X#include <ctype.h>
X#include <bios.h>
X#include <dos.h>
X#include "sh.h"
X
X/*
X * ${}, `command`, blank interpretation, quoting and file name expansion
X */
X
X#define	NSTART		16		/* default number of words to	*/
X					/* allow for initially		*/
Xstatic Word_B		*C_EList;	/* For expand functions		*/
Xstatic Word_B		*New_Elist;
Xstatic char		*spcl  = "[?*";
Xstatic char		*spcl1 = "\"'";
X
Xstatic void		globname (char *, char *);
Xstatic bool		expand (char *, Word_B **, int);
Xstatic char		dollar (int);
Xstatic bool		grave (int);
Xstatic Word_B		*Expand_globs (char *, Word_B *);
Xstatic bool		anyspcl (Word_B *);
Xstatic char		*blank (int);
Xstatic char		*generate (char *, char *, char *, char *);
Xstatic char		*unquote (char *);
Xstatic Word_B		*newword (int);
Xstatic bool		anys (char *, char *);
Xstatic char		*anys_p (char *, char *);
Xstatic void		Glob_MDrives (char *, char *);
Xstatic char		*Check_Multi_Drive (char *);
X
X/*
X * Expand all words to their full potential
X */
X
Xchar		**eval(ap, f)
Xregister char	**ap;
X{
X    Word_B	*wb = (Word_B *)NULL;
X    char	**wp = (char **)NULL;
X    char	**wf = (char **)NULL;
X    jmp_buf	ev;
X
X    if (newenv (setjmp (errpt = ev)) == FALSE)
X    {
X	while ((*ap != (char *)NULL) && isassign (*ap))
X	    expand (*(ap++), &wb, f & ~DOGLOB);
X
X	if (FL_TEST ('k'))
X	{
X	    for (wf = ap; *wf != (char *)NULL; wf++)
X	    {
X		if (isassign (*wf))
X		    expand (*wf, &wb, f & ~DOGLOB);
X	    }
X	}
X
X/* Now expand the words */
X
X	for (wb = addword ((char *)NULL, wb); *ap; ap++)
X	{
X	    if (!FL_TEST ('k') || !isassign(*ap))
X		expand (*ap, &wb, f & ~DOKEY);
X	}
X
X/* Get the word list */
X
X	wp = getwords (wb = addword ((char *)NULL, wb));
X	quitenv ();
X    }
X
X    else
X	gflg = 1;
X
X    return gflg ? (char **)NULL : wp;
X}
X
X/*
X * Make the exported environment from the exported names in the dictionary.
X * Keyword assignments will already have been done.  Convert to MSDOS
X * format if flag set and m enabled
X */
X
Xchar	**makenv ()
X{
X    register Word_B	*wb = (Word_B *)NULL;
X    register Var_List	*vp;
X    char		*cp, *sp;
X    int			len = 0;
X
X    for (vp = vlist; vp != (Var_List *)NULL; vp = vp->next)
X    {
X	if (vp->status & EXPORT)
X	{
X	    if ((len += (strlen (vp->name) + 1)) >= 0x7f00)
X		return (char **)NULL;
X
X	    wb = addword (vp->name, wb);
X
X/* If MSDOS mode, we need to copy the variable, convert / to \ and put
X * the copy in the environment list instead
X */
X
X	    if (FL_TEST ('m') && (vp->status & C_MSDOS))
X	    {
X		cp = space (strlen (sp = wb->w_words[wb->w_nword - 1]) + 1);
X		wb->w_words[wb->w_nword - 1] = cp;
X		Convert_Slashes (strcpy (cp, sp));
X	    }
X	}
X    }
X
X    return getwords (wb = addword ((char *)NULL, wb));
X}
X
Xchar		*evalstr(cp, f)
Xregister char	*cp;
Xint		f;
X{
X    Word_B	*wb = (Word_B *)NULL;
X
X    if (expand (cp, &wb, f))
X    {
X	if ((wb == (Word_B *)NULL) || (wb->w_nword == 0) ||
X	    ((cp = wb->w_words[0]) == (char *)NULL))
X	    cp = null;
X
X	DELETE (wb);
X    }
X
X    else
X	cp = (char *)NULL;
X
X    return cp;
X}
X
X/* Expand special characters and variables */
X
Xstatic bool		expand (cp, wbp, f)
Xregister char		*cp;
Xregister Word_B		**wbp;
X{
X    jmp_buf	ev;
X
X    gflg = 0;
X
X    if (cp == (char *)NULL)
X	return FALSE;
X
X/* If there are no special characters and no separators, nothing to do,
X * just save the word
X */
X
X    if (!anys (spcl2, cp) && !anys (ifs->value, cp) &&
X	((f & DOGLOB) == 0 || !anys (spcl, cp)))
X    {
X	cp = strsave (cp, areanum);
X
X	if (f & DOTRIM)
X	    unquote (cp);
X
X	*wbp = addword (cp, *wbp);
X	return TRUE;
X    }
X
X/* Set up to read the word back in */
X
X    if (newenv (setjmp (errpt = ev)) == FALSE)
X    {
X	PUSHIO (aword, cp, strchar);
X	e.iobase = e.iop;
X
X	while ((cp = blank (f)) && gflg == 0)
X	{
X	    e.linep = cp;
X	    cp = strsave (cp, areanum);
X
X/* Global expansion disabled ? */
X
X	    if (((f & DOGLOB) == 0) || FL_TEST ('f'))
X	    {
X		if (f & DOTRIM)
X		    unquote(cp);
X
X		*wbp = addword (cp, *wbp);
X	    }
X
X	    else
X		*wbp = Expand_globs (cp, *wbp);
X	}
X
X	quitenv ();
X    }
X
X    else
X	gflg = 1;
X
X    return (gflg == 0) ? TRUE : FALSE;
X}
X
X/*
X * Blank interpretation and quoting
X */
X
Xstatic char	*blank(f)
X{
X    register int	c, c1;
X    register char	*sp = e.linep;
X    int			scanequals = f & DOKEY;
X    int			foundequals = 0;
X
Xloop:
X    switch (c = subgetc ('"', foundequals))
X    {
X	case 0:
X	    if (sp == e.linep)
X		return (char *)NULL;
X
X	    *e.linep++ = 0;
X	    return sp;
X
X	default:
X	    if ((f & DOBLANK) && any ((char)c, ifs->value))
X		goto loop;
X
X	    break;
X
X	case '"':
X	case '\'':
X	    scanequals = 0;
X	    if (INSUB())
X		break;
X
X	    for (c1 = c; (c = subgetc ((char)c1, 1)) != c1;)
X	    {
X		if (c == 0)
X		    break;
X
X		if ((c == '\'') || !any ((char)c, "$`\""))
X		    c |= QUOTE;
X
X		*e.linep++ = (char)c;
X	    }
X
X	    c = 0;
X    }
X
X    unget(c);
X
X    if (!isalpha (c))
X	scanequals = 0;
X
X    while (1)
X    {
X	if (((c = subgetc ('"', foundequals)) == 0) ||
X	    (f & DOBLANK) && any ((char)c, ifs->value) ||
X	    !INSUB() && any ((char)c, spcl1))
X	{
X	    scanequals = 0;
X	    unget (c);
X
X	    if (any ((char)c, spcl1))
X		goto loop;
X
X	    break;
X	}
X
X	if (scanequals)
X	{
X	    if (c == '=')
X	    {
X		foundequals = 1;
X		scanequals  = 0;
X	    }
X
X	    else if (!isalnum (c))
X		scanequals = 0;
X	}
X
X	*e.linep++ = (char)c;
X    }
X
X    *e.linep++ = 0;
X    return sp;
X}
X
X/*
X * Get characters, substituting for ` and $
X */
X
Xint		subgetc (ec, quoted)
Xregister char	ec;
Xint		quoted;
X{
X    register char	c;
X
Xagain:
X    c = (char)Getc (ec);
X
X    if (!INSUB() && ec != '\'')
X    {
X	if (c == '`')
X	{
X	    if (grave (quoted) == 0)
X		return 0;
X
X	    e.iop->task = XGRAVE;
X	    goto again;
X	}
X
X	if (c == '$' && (c = dollar (quoted)) == 0)
X	{
X	    e.iop->task = XDOLL;
X	    goto again;
X	}
X    }
X
X    return c;
X}
X
X/*
X * Prepare to generate the string returned by ${} substitution.
X */
X
Xstatic char	dollar (quoted)
Xint		quoted;
X{
X    IO_State		*oiop;
X    char		*dolp, otask;
X    register char	*s, c, *cp;
X    Var_List		*vp;
X    bool		colon_f = FALSE;
X
X    c = (char)readc ();
X    s = e.linep;
X
X/* Bracketed or not ? */
X
X    if (c != '{')
X    {
X
X/* Get the string, while it is a alpha character */
X
X	*e.linep++ = c;
X
X	if (isalpha (c))
X	{
X	    while (((c = (char)readc ()) != 0) && isalnum (c))
X	    {
X		if (e.linep < e.eline)
X			*e.linep++ = c;
X	    }
X
X	    unget(c);
X	}
X
X	c = 0;
X    }
X
X/* Bracketed - special case */
X
X    else
X    {
X	oiop = e.iop;
X	otask = e.iop->task;
X	e.iop->task = XOTHER;
X
X	while (((c = (char)subgetc ('"', 0)) != 0) && (c != '}') && (c != NL))
X	{
X	    if (e.linep < e.eline)
X		*e.linep++ = c;
X	}
X
X	if (oiop == e.iop)
X	    e.iop->task = otask;
X
X/* Check terminate correctly */
X
X	if (c != '}')
X	{
X	    print_error ("sh: unclosed ${\n");
X	    gflg++;
X	    return c;
X	}
X    }
X
X/* Check line length */
X
X    if (e.linep >= e.eline)
X    {
X	print_error ("sh: string in ${} too long\n");
X	gflg++;
X	e.linep -= 10;
X    }
X
X    *e.linep = 0;
X
X/* Scan for =-+? in string */
X
X    if (*s)
X    {
X	for (cp = s + 1; *cp; cp++)
X	{
X
X/* Check for end character other than null (=-+?) */
X
X	    if (any (*cp, "=-+?"))
X	    {
X		c = *cp;
X
X/* Check for case of :[=-+?].  If found - set flag */
X
X		if (*(cp - 1) == ':')
X		{
X		    colon_f = TRUE;
X		    *(cp - 1) = 0;
X		}
X
X		*(cp++) = 0;
X		break;
X	    }
X	}
X    }
X
X/* Check for * and @ processing */
X
X    if (s[1] == 0 && (*s == '*' || *s == '@'))
X    {
X	if (dolc > 1)
X	{
X	    e.linep = s;
X	    PUSHIO (awordlist, dolv + 1, dol_char);
X	    e.iop->dflag = (char)(!quoted ? DSA_NULL
X					  : ((*s == '*') ? DSA_STAR : DSA_AMP));
X	    return 0;
X	}
X
X/* trap the nasty ${=} */
X
X	else
X	{
X	    s[0] = '1';
X	    s[1] = 0;
X	}
X    }
X
X/* Find the current value
X *
X * $~xxx variables are used by the Shell internally and cannot be accessed
X * by the user.
X */
X
X    if (*s == '~')
X	dolp = null;
X
X    else if ((dolp = (vp = lookup (s, FALSE))->value) == null)
X    {
X	switch (c)
X	{
X	    case '=':
X		if (isdigit (*s))
X		{
X		    print_error ("sh: cannot use ${...=...} with $n\n");
X		    gflg++;
X		    break;
X		}
X
X		setval ((vp = lookup (s, TRUE)), cp);
X		dolp = vp->value;
X		break;
X
X	    case '-':
X		dolp = strsave (cp, areanum);
X		break;
X
X	    case '?':
X		if (*cp == 0)
X		    cp = "parameter null or not set";
X
X		print_error ("%s: %s\n", s, cp);
X
X		gflg++;
X		break;
X	}
X    }
X
X    else if (c == '+')
X	dolp = strsave (cp, areanum);
X
X/* Check for unset values */
X
X    if (FL_TEST ('u') && dolp == null)
X    {
X	print_error ("sh: unset variable %s\n", s);
X	gflg++;
X    }
X
X    e.linep = s;
X    PUSHIO (aword, dolp, quoted ? qstrchar : strchar);
X    return 0;
X}
X
X/*
X * Run the command in `...` and read its output.
X */
X
Xstatic bool	grave (quoted)
Xint		quoted;
X{
X    char		*cp, *sp;
X    int			localpipe, rv;
X    jmp_buf		ev, rt;
X    C_Op		*outtree;
X    Break_C		bc;
X
X/* Save area */
X
X    long		s_flags = flags;
X    Word_B		*s_wdlist = wdlist;
X    Word_B		*s_iolist = iolist;
X    Break_C		*S_RList = Return_List;	/* Save loval links	*/
X    Break_C		*S_BList = Break_List;
X    Break_C		*S_SList = SShell_List;
X    int			*s_fail = failpt;
X    int			s_execflg = execflg;
X    int			Local_depth;
X
X/* Check there is an ending grave */
X
X    if ((cp = strchr (e.iop->argp->aword, '`')) == (char *)NULL)
X    {
X	print_error ("sh: no closing `\n");
X	return FALSE;
X    }
X
X/* Create the pipe to read the output from the command string */
X
X    if ((localpipe = openpipe ()) < 0)
X	return FALSE;
X
X/* Terminate string and initialise save area */
X
X    *cp = 0;
X
X/* Create a new environment */
X
X    S_dup2 (localpipe, 1);
X
X    FL_CLEAR ('e');
X    FL_CLEAR ('v');
X    FL_CLEAR ('n');
X
X    sp = strsave (e.iop->argp->aword, areanum++);
X    unquote (sp);
X
X/* Set up new environment */
X
X    Local_depth = Execute_stack_depth++;
X    rv = Create_NG_VL ();
X
X    if ((rv != -1) && (newenv (setjmp (errpt = ev)) == FALSE))
X    {
X	Return_List = (Break_C *)NULL;
X	Break_List  = (Break_C *)NULL;
X	wdlist	    = (Word_B *)NULL;
X	wdlist	    = (Word_B *)NULL;
X	iolist	    = (Word_B *)NULL;
X
X	PUSHIO (aword, sp, nlchar);
X	e.cline = space (LINE_MAX);
X	e.eline = e.cline + LINE_MAX - 5;
X	e.linep = e.cline;
X	e.iobase = e.iop;
X
X/* Clear interrupt, error, multiline, parse and execute flags.  */
X
X	SW_intr = 0;
X	yynerrs = 0;
X	multiline = 0;
X	inparse = 0;
X	execflg = 1;
X
X/* Parse the line and execute it */
X
X	if ((setjmp (failpt = rt) == 0) &&
X	    ((outtree = yyparse ()) != (C_Op *)NULL))
X	{
X	    if (setjmp (bc.brkpt) == 0)
X	    {
X		bc.nextlev = SShell_List;
X		SShell_List = &bc;
X		execute (outtree, NOPIPE, NOPIPE, 0);
X	    }
X	}
X
X	quitenv ();
X    }
X
X/* Fail - close pipe and delete it */
X
X    else
X    {
X	S_Delete (localpipe);
X	S_close (localpipe, TRUE);
X    }
X
X/* Restore environment */
X
X    Restore_Environment (0, Local_depth);
X
X/* Free old space */
X
X    freehere (areanum);
X    freearea (areanum--);	/* free old space */
X
X/* Ok - completed processing - restore environment and read the pipe */
X
X    execflg	= s_execflg;
X    flags	= s_flags;
X    wdlist	= s_wdlist;
X    iolist	= s_iolist;
X    failpt	= s_fail;
X    Return_List = S_RList;
X    Break_List	= S_BList;
X    SShell_List = S_SList;
X
X/* Move pipe to start so we can read it */
X
X    *(cp++) = '`';
X    lseek (localpipe, 0L, SEEK_SET);
X    e.iop->argp->aword = cp;
X    PUSHIO (afile, remap (localpipe), quoted ? qgravechar: gravechar);
X    return TRUE;
X}
X
X/*
X * Remove Quotes from a string
X */
X
Xstatic char	*unquote (as)
Xregister char	*as;
X{
X    register char	*s;
X
X    if ((s = as) != (char *)NULL)
X    {
X	while (*s)
X	    *(s++) &= ~QUOTE;
X    }
X
X    return as;
X}
X
X/*
X * Expand *, [] and ?
X */
X
Xstatic Word_B	*Expand_globs (cp, wb)
Xchar		*cp;
XWord_B		*wb;
X{
X    register int	i = 0;
X    register char	*pp;
X
X/* Ignore null strings */
X
X    if (cp == (char *)NULL)
X	return wb;
X
X/* Any special characters */
X
X    for (pp = cp; *pp; pp++)
X    {
X	if (any (*pp, spcl))
X	    i++;
X
X	else if (!any (*pp & ~QUOTE, spcl))
X	    *pp &= ~QUOTE;
X    }
X
X/* No - just add the word to the selected block */
X
X    if (i == 0)
X	return addword (unquote (cp), wb);
X
X/* OK - we have to expand the word whilst any words in cl have special
X * characters in them
X */
X
X    for (C_EList = addword (strsave (cp, areanum), (Word_B *)NULL);
X	 anyspcl (C_EList); C_EList = New_Elist)
X    {
X
X/* Get a new block for this pass of the expansion */
X
X	New_Elist = newword (C_EList->w_nword * 2);
X
X/* For each word, expand it */
X
X	for (i = 0; i < C_EList->w_nword; i++)
X	{
X	    if ((pp = anys_p (C_EList->w_words[i], spcl)) != (char *)NULL)
X		Glob_MDrives (C_EList->w_words[i], pp);
X
X	    else
X		New_Elist = addword (strsave (C_EList->w_words[i], areanum),
X				     New_Elist);
X	}
X
X/* The current list is now the previous list, so delete it */
X
X	for (i = 0; i < C_EList->w_nword; i++)
X	    DELETE (C_EList->w_words[i]);
X
X	DELETE (C_EList);
X    }
X
X    for (i = 0; i < C_EList->w_nword; i++)
X	unquote (C_EList->w_words[i]);
X
X    qsort (C_EList->w_words, C_EList->w_nword, sizeof (char *), sort_compare);
X
X/* Did we find any files matching the specification.  Yes - add them to
X * the block
X */
X
X    if (C_EList->w_nword)
X    {
X	for (i = 0; i < C_EList->w_nword; i++)
X	    wb = addword (C_EList->w_words[i], wb);
X
X	DELETE (C_EList);
X	return wb;
X    }
X
X/* No - add the original word */
X
X    else
X	return addword (unquote (cp), wb);
X}
X
X/*
X * Read a directory for matches against the specified name
X */
X
Xstatic void	globname (we, pp)
Xchar		*we;			/* Start			*/
Xregister char	*pp;			/* First special character	*/
X{
X    register char	*np, *cp;
X    char		*name, *gp, *dp;
X    DIR			*dn;
X    struct dirent	*d_ce;
X    char		dname[NAME_MAX + 1];
X    struct stat		dbuf;
X
X/* Find the previous directory separator */
X
X    for (np = we; np != pp; pp--)
X    {
X	if (pp[-1] == '/')
X	    break;
X    }
X
X/* If we don't find it, check for a drive */
X
X    if ((np == pp) && (strlen (we) > 2) && (we[1] == ':'))
X	pp += 2;
X
X/* Save copy of directory name */
X
X    for (dp = cp = space ((int)(pp - np) + 3); np < pp;)
X	*cp++ = *np++;
X
X    *cp++ = '.';
X    *cp = '\0';
X
X/* Save copy of pattern for this directory.  NP is left pointing to the
X * rest of the string for any subdirectories
X */
X
X    for (gp = cp = space (strlen (pp) + 1); *np && *np != '/';)
X	*cp++ = *np++;
X
X    *cp = '\0';
X
X/* Open the directory */
X
X    if ((dn = opendir (dp)) == (DIR *)NULL)
X    {
X	DELETE (dp);
X	DELETE (gp);
X	return;
X    }
X
X/* Scan for matches */
X
X    while ((d_ce = readdir (dn)) != (struct dirent *)NULL)
X    {
X	if ((*(strcpy (dname, d_ce->d_name)) == '.') && (*gp != '.'))
X	    continue;
X
X	for (cp = dname; *cp; cp++)
X	{
X	    if (any (*cp, spcl))
X		*cp |= QUOTE;
X	}
X
X/* Check for a match */
X
X	if (gmatch (dname, gp, TRUE))
X	{
X
X/* If there are no special characters in the new full name, the file must
X * exist
X */
X
X	    name = generate (we, pp, dname, np);
X
X	    if (*np && !anys (np, spcl))
X	    {
X		if (stat (name, &dbuf))
X		{
X		    DELETE (name);
X		    continue;
X		}
X	    }
X
X/* Ok save the name */
X
X	    New_Elist = addword (name, New_Elist);
X	}
X    }
X
X    closedir (dn);
X    DELETE (dp);
X    DELETE (gp);
X}
X
X/*
X * generate a pathname as below.  start..end1 / middle end.  The slashes come
X * for free
X */
X
Xstatic char	*generate (start1, end1, middle, end)
Xchar		*start1;
Xregister char	*end1;
Xchar		*middle, *end;
X{
X    register char	*op;
X    int			clen = (int)(end1 - start1);
X
X    op = space (clen + strlen (middle) + strlen (end) + 2);
X
X    strncpy (op, start1, clen);
X    strcat (strcpy (&op[clen], middle), end);
X    return op;
X}
X
X/*
X * Scan a Word Block for special characters
X */
X
Xstatic bool	anyspcl (wb)
Xregister Word_B	*wb;
X{
X    register int	i;
X    register char	**wd = wb->w_words;
X
X    for (i = 0; i < wb->w_nword; i++)
X    {
X	if (anys (spcl, *wd++))
X	    return TRUE;
X    }
X
X    return FALSE;
X}
X
X/*
X * Create a new Word Block
X */
X
Xstatic Word_B	*newword (nw)
Xregister int	nw;
X{
X    register Word_B	*wb;
X
X    wb = (Word_B *) space (sizeof (Word_B) + nw * sizeof (char *));
X    wb->w_bsize = nw;
X    wb->w_nword = 0;
X
X    return wb;
X}
X
X/*
X * Add a new word to a Word Block or list
X */
X
XWord_B		*addword (wd, wb)
Xchar		*wd;
Xregister Word_B	*wb;
X{
X    register Word_B	*wb2;
X    register int	nw;
X
X    if (wb == (Word_B *)NULL)
X	wb = newword (NSTART);
X
X/* Do we require more space ? */
X
X    if ((nw = wb->w_nword) >= wb->w_bsize)
X    {
X	wb2 = newword (nw * 2);
X	memcpy ((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *));
X	wb2->w_nword = nw;
X	DELETE (wb);
X	wb = wb2;
X    }
X
X/* Add to the list */
X
X    wb->w_words[wb->w_nword++] = wd;
X    return wb;
X}
X
X/*
X * Convert a word block structure into a array of strings
X */
X
Xchar		**getwords(wb)
Xregister Word_B	*wb;
X{
X    register char	**wd;
X    register nb;
X
X/* If the word block is empty or does not exist, return no list */
X
X    if (wb == (Word_B **)NULL)
X	return (char *)NULL;
X
X    if (wb->w_nword == 0)
X    {
X	DELETE (wb);
X	return (char *)NULL;
X    }
X
X/* Get some space for the array and set it up */
X
X    wd = (char **)space (nb = sizeof (char *) * wb->w_nword);
X
X    memcpy ((char *)wd, (char *)wb->w_words, nb);
X    DELETE (wb);	/* perhaps should done by caller */
X    return wd;
X}
X
X/*
X * Is any character from s1 in s2?  Return a boolean.
X */
X
Xstatic bool	anys (s1, s2)
Xregister char	*s1, *s2;
X{
X    while (*s1)
X    {
X	if (any (*(s1++), s2))
X	    return TRUE;
X    }
X
X    return FALSE;
X}
X
X/*
X * Is any character from s1 in s2? Yes - return a pointer to that
X * character.
X */
X
Xstatic char	*anys_p (s1, s2)
Xregister char	*s1, *s2;
X{
X    while (*s1)
X    {
X	if (any (*(s1++), s2))
X	    return --s1;
X    }
X
X    return (char *)NULL;
X}
X
X/*
X * Expansion - check for multiple drive request
X *
X * If there is a multi-drive expansion (*:, ?: or []:), we have to check
X * out each existing drive and then expand.  So we check for a multi-drive
X * condition and then for each existing drive, we check that pattern
X * against the drive and then expand the rest of the pattern.
X *
X * Otherwise, we just expand the pattern.
X */
X
Xstatic void	Glob_MDrives (pattern, start)
Xchar		*pattern;
Xchar		*start;
X{
X    unsigned int	c_drive;	/* Current drive		*/
X    unsigned int	m_drive;	/* Max drive			*/
X    unsigned int	s_drive;	/* Selected drive		*/
X    unsigned int	x_drive, y_drive;	/* Dummies		*/
X    char		*multi;		/* Multi-drive flag		*/
X    static char		*t_drive = "x";
X    char		*new_pattern;
X
X/* Search all drives ? */
X
X    if ((multi = Check_Multi_Drive (pattern)) != (char *)NULL)
X    {
X	_dos_getdrive (&c_drive);	/* Get number of drives		*/
X	_dos_setdrive (c_drive, &m_drive);
X	new_pattern = space (strlen (multi) + 2);
X
X	strcpy (new_pattern + 1, multi);
X	*multi = 0;
X
X	for (s_drive = 1; s_drive <= m_drive; ++s_drive)
X	{
X	    _dos_setdrive (s_drive, &x_drive);
X	    _dos_getdrive (&y_drive);
X	    _dos_setdrive (c_drive, &x_drive);
X
X/* Check to see if the second diskette drive is really there */
X
X	    if (((_bios_equiplist () & 0x00c0) == 0x0000) && (s_drive == 2))
X		continue;
X
X/* If the drive exists and is in our list - process it */
X
X	    *t_drive = (char)(s_drive + 'a' - 1);
X
X	    if ((y_drive == s_drive) && gmatch (t_drive, pattern, TRUE))
X	    {
X		*new_pattern = *t_drive;
X		globname (new_pattern, &new_pattern[2]);
X	    }
X	}
X
X/* Restore and delete space */
X
X	*multi = ':';
X	DELETE (new_pattern);
X    }
X
X/* No drive specifier - just check it out */
X
X    else
X	globname (pattern, start);
X}
X
X/*
X * Check for multi_drive prefix - *:, ?: or []:
X *
X * Return NULL or the address of the colon character
X */
X
Xstatic char	*Check_Multi_Drive (pattern)
Xchar		*pattern;
X{
X    if (strlen (pattern) < 3)
X	return (char *)NULL;
X
X    if (((*pattern == '*') || (*pattern == '?')) && (pattern[1] == ':'))
X	return pattern + 1;
X
X    if (*pattern != '[')
X	return (char *)NULL;
X
X    while (*pattern && (*pattern != ']'))
X    {
X	if ((*pattern == '\\') && (*(pattern + 1)))
X	    ++pattern;
X
X	++pattern;
X    }
X
X    return (*pattern && (*(pattern + 1) == ':')) ? pattern + 1 : (char *)NULL;
X}
SHAR_EOF
chmod 0644 shell/sh4.c || echo "restore of shell/sh4.c fails"
set `wc -c shell/sh4.c`;Sum=$1
if test "$Sum" != "20630"
then echo original size 20630, current size $Sum;fi
echo "x - extracting shell/sh5.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh5.c &&
X/* MS-DOS SHELL - Main I/O Functions
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: sh5.c 1.1 90/01/25 13:41:50 MS_user Exp $
X *
X *    $Log:	sh5.c $
X * Revision 1.1  90/01/25  13:41:50  MS_user
X * Initial revision
X * 
X */
X
X#include <sys/types.h>
X#include <stdio.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <stdlib.h>
X#include <string.h>
X#include <fcntl.h>
X#include <io.h>
X#include <limits.h>
X#include <unistd.h>
X#include "sh.h"
X
X/*
X * shell IO
X */
X
Xstatic IO_Buf		sharedbuf = {AFID_NOBUF};
Xstatic IO_Buf		mainbuf = {AFID_NOBUF};
Xstatic unsigned int	bufid = AFID_ID;	/* buffer id counter */
X					/* list of hear docs while parsing */
Xstatic Here_D	*inhere = (Here_D *)NULL;
X					/* list of active here documents */
Xstatic Here_D	*acthere = (Here_D *)NULL;
X
Xstatic int	dol1_char (IO_State *);
Xstatic void	readhere (char **, char *, int);
Xstatic int	herechar (IO_State *);
X
Xint		Getc (ec)
Xregister int	ec;
X{
X    register int	c;
X
X    if (e.linep > e.eline)
X    {
X	while (((c = readc ()) != NL) && c)
X	    ;
X
X	print_error ("sh: input line too long\n");
X	gflg++;
X	return c;
X    }
X
X    c = readc();
X    if ((ec != '\'') && (ec != '`') && (e.iop->task != XGRAVE))
X    {
X	if (c == '\\')
X	{
X	    if (((c = readc ()) == NL) && (ec != '\"'))
X		return Getc (ec);
X
X	    c |= QUOTE;
X	}
X    }
X
X    return c;
X}
X
Xvoid	unget (c)
Xint	c;
X{
X    if (e.iop >= e.iobase)
X	e.iop->peekc = c;
X}
X
Xint	eofc ()
X{
X    return (e.iop < e.iobase) || ((e.iop->peekc == 0) && (e.iop->prev == 0));
X}
X
X/* Read the next character */
X
Xint	readc ()
X{
X    register int	c;
X    char		s_dflag = e.iop->dflag;
X
X/* The dflag is transfered from the higher level to the lower level at end
X * of input at the higher level.  This is part of the implementation of
X * $* and $@ processing.
X */
X
X    for (; e.iop >= e.iobase; e.iop--)
X    {
X
X/* Set up the current dflag */
X
X	e.iop->dflag = s_dflag;
X
X/* If there is an unget character, use it */
X
X	if ((c = e.iop->peekc) != '\0')
X	{
X	    e.iop->peekc = 0;
X	    return c;
X	}
X
X/* Some special processing for multi-line commands */
X
X	else
X	{
X	    if (e.iop->prev != 0)
X	    {
X
X/* Get the next character from the IO function */
X
X		if ((c = (*e.iop->iofn)(e.iop)) != '\0')
X		{
X
X/* End of current level, but continue at this level as another read
X * function has been put on the stack
X */
X
X		    if (c == -1)
X		    {
X			e.iop++;
X			continue;
X		    }
X
X/* If we are at the bottom - echo the character */
X
X		    if ((e.iop == iostack) && (FL_TEST ('v')))
X			S_putc ((char)c);
X
X/* Return the current character */
X
X		    return (e.iop->prev = (char)c);
X		}
X
X		else if (e.iop->task == XIO && e.iop->prev != NL)
X		{
X		    e.iop->prev = 0;
X
X		    if ((e.iop == iostack) && (FL_TEST ('v')))
X			S_putc (NL);
X
X		    return NL;
X		}
X
X		else
X		    s_dflag = e.iop->dflag;
X	    }
X
X	    if (e.iop->task == XIO)
X	    {
X		if (multiline)
X		    return e.iop->prev = 0;
X
X		if (talking && e.iop == iostack + 1)
X		    put_prompt (ps1->value);
X	    }
X	}
X    }
X
X    if (e.iop >= iostack)
X	return 0;
X
X    leave();
X    /* NOTREACHED */
X}
X
X/* Add an Input channel to the input stack */
X
Xvoid		pushio (argp, fn)
XIO_Args		*argp;
Xint		(*fn)(IO_State *);
X{
X    if (++e.iop >= &iostack[NPUSH])
X    {
X	e.iop--;
X	print_error ("sh: Shell input nested too deeply\n");
X	gflg++;
X	return;
X    }
X
X    e.iop->iofn = fn;
X
X    if (argp->afid != AFID_NOBUF)
X	e.iop->argp = argp;
X
X    else
X    {
X	e.iop->argp  = ioargstack + (e.iop - iostack);
X	*e.iop->argp = *argp;
X	e.iop->argp->afbuf = e.iop == &iostack[0] ? &mainbuf : &sharedbuf;
X
X	if ((isatty (e.iop->argp->afile) == 0) &&
X	    ((e.iop == &iostack[0]) ||
X	     (lseek (e.iop->argp->afile, 0L, 1) != -1L)))
X	{
X	    if (++bufid == AFID_NOBUF)
X		bufid = AFID_ID;
X
X	    e.iop->argp->afid  = bufid;
X	}
X    }
X
X    e.iop->prev  = ~NL;
X    e.iop->peekc = 0;
X    e.iop->xchar = 0;
X    e.iop->nlcount = 0;
X
X    if ((fn == filechar) || (fn == linechar))
X	e.iop->task = XIO;
X
X    else if ((fn == gravechar) || (fn == qgravechar))
X	e.iop->task = XGRAVE;
X
X    else
X	e.iop->task = XOTHER;
X}
X
X/*
X * Input generating functions
X */
X
X/*
X * Produce the characters of a string, then a newline, then EOF.
X */
Xint			nlchar (iop)
Xregister IO_State	*iop;
X{
X    register int	c;
X
X    if (iop->argp->aword == (char *)NULL)
X	return 0;
X
X    if ((c = *iop->argp->aword++) == 0)
X    {
X	iop->argp->aword = (char *)NULL;
X	return NL;
X    }
X
X    return c;
X}
X
X/*
X * Given a list of words, produce the characters
X * in them, with a space after each word.
X */
X
Xint			wdchar (iop)
Xregister IO_State	*iop;
X{
X    register char	c;
X    register char	**wl;
X
X    if ((wl = iop->argp->awordlist) == (char **)NULL)
X	return 0;
X
X    if (*wl != (char *)NULL)
X    {
X	if ((c = *(*wl)++) != 0)
X	    return (c & 0177);
X
X	iop->argp->awordlist++;
X	return SP;
X    }
X
X    iop->argp->awordlist = (char **)NULL;
X    return NL;
X}
X
X/*
X * Return the characters of a list of words, producing a space between them.
X */
X
Xint			dol_char (iop)
XIO_State		*iop;
X{
X    register char	*wp;
X    char		cflag;
X
X    if ((wp = *(iop->argp->awordlist)++) != (char *)NULL)
X    {
X	if (*iop->argp->awordlist == (char *)NULL)
X	    iop->dflag |= DSA_END;
X
X	cflag = iop->dflag;
X	PUSHIO (aword, wp, dol1_char);
X	e.iop->dflag = cflag;
X	return -1;
X    }
X
X    return 0;
X}
X
X/* Return next character from the word with a space at the end */
X
Xstatic int		dol1_char (iop)
XIO_State		*iop;
X{
X    register int c;
X
X    if ((iop->dflag & DSA_MODE) == DSA_AMP)
X    {
X	if (!(iop->dflag & DSA_START))
X	    iop->dflag |= DSA_START;
X
X/* Has the previous word ended */
X
X	else if (iop->dflag & DSA_START1)
X	{
X	    iop->dflag &= ~DSA_START1;
X	    return '"';
X	}
X    }
X
X    if (iop->argp->aword == (char *)NULL)
X	return 0;
X
X    if ((c = *iop->argp->aword) == '\0')
X    {
X	if ((iop->dflag & DSA_MODE) != DSA_AMP)
X	{
X	    iop->argp->aword = (char *)NULL;
X	    return (iop->dflag & DSA_END) ? 0 : SP;
X	}
X
X	if (!(iop->dflag & DSA_END1))
X	{
X	    iop->dflag |= DSA_END1;
X	    return '"';
X	}
X
X	iop->argp->aword = (char *)NULL;
X	iop->dflag &= ~DSA_END1;
X	iop->dflag |= DSA_START1;
X	return (iop->dflag & DSA_END) ? 0 : SP;
X    }
X
X    iop->argp->aword++;
X    if ((iop->dflag != DSA_NULL) && any ((char)c, ifs->value))
X	c |= QUOTE;
X
X    return c;
X}
X
X/*
X * Produce the characters from a single word (string).
X */
X
Xint		strchar (iop)
XIO_State	*iop;
X{
X    register int	c;
X
X    return ((iop->argp->aword == (char *)NULL) ||
X	    ((c = *(iop->argp->aword++)) == 0)) ? 0 : c;
X}
X
X/*
X * Produce quoted characters from a single word (string).
X */
X
Xint		qstrchar (iop)
XIO_State	*iop;
X{
X    register int	c;
X
X    return ((iop->argp->aword == (char *)NULL) ||
X	    ((c = *(iop->argp->aword++)) == 0)) ? 0 : (c | QUOTE);
X}
X
X/*
X * Return the characters from a file.
X */
X
Xint		filechar (iop)
XIO_State	*iop;
X{
X    register IO_Args	*ap = iop->argp;
X    register int	i;
X    char		c;
X    IO_Buf		*bp = ap->afbuf;
X
X    if (ap->afid != AFID_NOBUF)
X    {
X	if ((i = (ap->afid != bp->id)) || (bp->bufp == bp->ebufp))
X	{
X	    if (i)
X		lseek (ap->afile, ap->afpos, SEEK_SET);
X
X	    if ((i = read (ap->afile, bp->buf, sizeof (bp->buf))) <= 0)
X	    {
X		if (ap->afile > STDERR_FILENO)
X		    S_close (ap->afile, TRUE);
X
X		return 0;
X	    }
X
X	    bp->id = ap->afid;
X	    bp->ebufp = (bp->bufp  = bp->buf) + i;
X	}
X
X	ap->afpos++;
X
X	return *bp->bufp++ & 0177;
X    }
X
X/* If this is the terminal, there is special input processing */
X
X    else if ((ap->afile == 0) && isatty (ap->afile))
X        return Get_stdin (ap);
X
X    if ((i = read (ap->afile, &c, sizeof(c))) == sizeof (c))
X	return (int)c & 0177;
X
X    if (ap->afile > STDERR_FILENO)
X	S_close (ap->afile, TRUE);
X
X    return 0;
X}
X
X/*
X * Return the characters from a here temp file.
X */
X
Xstatic int		herechar (iop)
Xregister IO_State	*iop;
X{
X    char			c;
X
X    if (read (iop->argp->afile, &c, sizeof(c)) != sizeof(c))
X    {
X	S_close (iop->argp->afile, TRUE);
X	c = 0;
X    }
X
X    return c;
X}
X
X/*
X * Return the characters produced by a process (`...`).
X * Quote them if required, and remove any trailing newline characters.
X */
X
Xint		gravechar (iop)
XIO_State	*iop;
X{
X    register int c;
X
X    if ((c = qgravechar (iop) & ~QUOTE) == NL)
X	c = SP;
X
X    return c;
X}
X
X/*
X * Process input from a `...` string
X */
X
Xint		qgravechar (iop)
XIO_State	*iop;
X{
X    register int	c;
X
X    if (iop->xchar)
X    {
X	if (iop->nlcount)
X	{
X	    iop->nlcount--;
X	    return (NL | QUOTE);
X	}
X
X	c = iop->xchar;
X	iop->xchar = 0;
X    }
X
X    else if ((c = filechar (iop)) == NL)
X    {
X	iop->nlcount = 1;
X
X	while ((c = filechar (iop)) == NL)
X	    iop->nlcount++;
X
X	iop->xchar = (char)c;
X
X	if (c == 0)
X	    return(c);
X
X	iop->nlcount--;
X	c = NL;
X    }
X
X    return (c != 0) ? (c | QUOTE): 0;
X}
X
X/*
X * Return a single command (usually the first line) from a file.
X */
X
Xint		linechar (iop)
XIO_State	*iop;
X{
X    register int	c;
X
X    if ((c = filechar (iop)) == NL)
X    {
X	if (!multiline)
X	{
X	    if (iop->argp->afile > STDERR_FILENO)
X		S_close (iop->argp->afile, TRUE);
X
X	    iop->argp->afile = -1;	/* illegal value */
X	}
X    }
X
X    return c;
X}
X
Xvoid	closeall ()
X{
X    register int	u;
X
X    for (u = NUFILE; u < NOFILE;)
X	S_close (u++, TRUE);
X}
X
X/*
X * remap fd into Shell's fd space
X */
X
Xint		remap (fd)
Xregister int	fd;
X{
X    register int	i;
X    register int	n_io = 0;
X    int			map[NOFILE];
X    int			o_fd = fd;
X
X    if (fd < e.iofd)
X    {
X	do
X	{
X	    map[n_io++] = fd;
X	    fd = dup (fd);
X
X	} while ((fd >= 0) && (fd < e.iofd));
X
X	for (i = 0; i < n_io; i++)
X	    close (map[i]);
X
X	S_Remap (o_fd, fd);
X	S_close (o_fd, TRUE);
X
X	if (fd < 0)
X	    print_error ("sh: too many files open\n");
X    }
X
X    return fd;
X}
X
X/*
X * here documents
X */
X
Xvoid		markhere (s, iop)
Xregister char	*s;
XIO_Actions 	*iop;
X{
X    register Here_D	*h, *lh;
X
X    if ((h = (Here_D *) space(sizeof(Here_D))) == (Here_D *)NULL)
X	return;
X
X    if ((h->h_tag = evalstr (s, DOSUB)) == (char *)NULL)
X	return;
X
X    h->h_iop     = iop;
X    iop->io_name = (char *)NULL;
X    h->h_next    = (Here_D *)NULL;
X
X    if (inhere == (Here_D *)NULL)
X	inhere = h;
X
X    else
X    {
X	for (lh = inhere; lh != (Here_D *)NULL; lh = lh->h_next)
X	{
X	    if (lh->h_next == (Here_D *)NULL)
X	    {
X		lh->h_next = h;
X		break;
X	    }
X	}
X    }
X
X    iop->io_flag |= IOHERE|IOXHERE;
X
X    for (s = h->h_tag; *s; s++)
X    {
X	if (*s & QUOTE)
X	{
X	    iop->io_flag &= ~ IOXHERE;
X	    *s &= ~ QUOTE;
X	}
X    }
X
X    h->h_dosub = iop->io_flag & IOXHERE;
X}
X
Xvoid	gethere ()
X{
X    register Here_D	*h, *hp;
X
X/* Scan here files first leaving inhere list in place */
X
X    for (hp = h = inhere; h != (Here_D *)NULL; hp = h, h = h->h_next)
X	readhere (&h->h_iop->io_name, h->h_tag, h->h_dosub ? 0 : '\'');
X
X/* Make inhere list active - keep list intact for scraphere */
X
X    if (hp != (Here_D *)NULL)
X    {
X	hp->h_next = acthere;
X	acthere    = inhere;
X	inhere     = (Here_D *)NULL;
X    }
X}
X
Xstatic void	readhere (name, s, ec)
Xchar		**name;
Xregister char	*s;
X{
X    int			tf;
X    register int	c;
X    jmp_buf		ev;
X    char		*line;
X    char		*next;
X
X    *name = strsave (g_tempname (), areanum);
X
X    if ((tf = S_open (FALSE, *name, O_CMASK | O_NOINHERIT, 0600)) < 0)
X	return;
X
X    if (newenv (setjmp (errpt = ev)) == TRUE)
X	S_Delete (tf);
X
X    else
X    {
X	line = space (LINE_MAX + 1);
X	pushio (e.iop->argp, e.iop->iofn);
X	e.iobase = e.iop;
X
X	while (1)
X	{
X	    if (talking && e.iop <= iostack)
X		put_prompt (ps2->value);
X
X	    next = line;
X	    while ((c = Getc (ec)) != NL && c)
X	    {
X		if (ec == '\'')
X		    c &= ~ QUOTE;
X
X		if (next >= &line[LINE_MAX])
X		{
X		    c = 0;
X		    break;
X		}
X
X		*next++ = (char)c;
X	    }
X
X	    *next = 0;
X	    if (strcmp (s, line) == 0 || c == 0)
X		break;
X
X	    *next++ = NL;
X	    write (tf, line, (int)(next-line));
X	}
X
X	if (c == 0)
X	    print_error ("here document `%s' unclosed\n", s);
X
X	quitenv ();
X    }
X
X    S_close (tf, TRUE);
X}
X
X/*
X * open here temp file.
X * If unquoted here, expand here temp file into second temp file.
X */
X
Xint		herein (hname, xdoll)
Xchar		*hname;
Xint		xdoll;
X{
X    register int	hf, tf;
X
X    if (hname == (char *)NULL)
X	return -1;
X
X    if ((hf = S_open (FALSE, hname, O_RDONLY)) < 0)
X	return -1;
X
X    if (xdoll)
X    {
X	char		c;
X	char		*tname = g_tempname();
X	jmp_buf		ev;
X
X	if ((tf = S_open (FALSE, tname, O_CMASK | O_NOINHERIT, 0600)) < 0)
X	    return -1;
X
X	if (newenv (setjmp (errpt = ev)) == FALSE)
X	{
X	    PUSHIO (afile, hf, herechar);
X	    e.iobase = e.iop;
X
X	    while ((c = (char)subgetc(0, 0)) != 0)
X	    {
X		c &= ~ QUOTE;
X		write (tf, &c, sizeof c);
X	    }
X
X	    quitenv ();
X	}
X
X	else
X	    S_Delete (tf);
X
X	S_close (tf, TRUE);
X	return S_open (TRUE, tname, O_RDONLY);
X    }
X
X    else
X	return hf;
X}
X
Xvoid	scraphere()
X{
X    register Here_D	*h;
X
X    for (h = inhere; h != (Here_D *)NULL; h = h->h_next)
X    {
X	if ((h->h_iop != (IO_Actions *)NULL) &&
X	    (h->h_iop->io_name != (char *)NULL))
X	    unlink (h->h_iop->io_name);
X    }
X
X    inhere = (Here_D *)NULL;
X}
X
X/* unlink here temp files before a freearea (area) */
X
Xvoid	freehere (area)
Xint	area;
X{
X    register Here_D	*h;
X    register Here_D	*hl = (Here_D *)NULL;
X
X    for (h = acthere; h != (Here_D *)NULL; hl = h, h = h->h_next)
X    {
X	if (getarea ((char *)h) >= area)
X	{
X	    if (h->h_iop->io_name != (char *)NULL)
X		unlink (h->h_iop->io_name);
X
X	    if (hl == (Here_D *)NULL)
X		acthere = h->h_next;
X
X	    else
X		hl->h_next = h->h_next;
X	}
X    }
X}
X
Xchar	*g_tempname ()
X{
X    static char	tmpfile[FFNAME_MAX];
X    char	*tmpdir;	/* Points to directory prefix of pipe	*/
X    static int	temp_count = 0;
X
X/* Find out where we should put temporary files */
X
X    if ((tmpdir = lookup ("TMPDIR", FALSE)->value) == null)
X	tmpdir = lookup ("TMP", FALSE)->value;
X
X/* Get a unique temporary file name */
X
X    while (1)
X    {
X	sprintf (tmpfile, "%s/sht%.5u.tmp", tmpdir, temp_count++);
X
X	if (access (tmpfile, F_OK) != 0)
X	    break;
X    }
X
X    return tmpfile;
X}
SHAR_EOF
chmod 0644 shell/sh5.c || echo "restore of shell/sh5.c fails"
set `wc -c shell/sh5.c`;Sum=$1
if test "$Sum" != "14249"
then echo original size 14249, current size $Sum;fi
echo "x - extracting shell/sh6.c (Text)"
sed 's/^X//' << 'SHAR_EOF' > shell/sh6.c &&
X/* MS-DOS SHELL - Data Declarations
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: sh6.c 1.1 90/01/25 13:42:04 MS_user Exp $
X *
X *    $Log:	sh6.c $
X * Revision 1.1  90/01/25  13:42:04  MS_user
X * Initial revision
X * 
X */
X
X#include <sys/types.h>
X#include <stddef.h>
X#include <signal.h>
X#include <errno.h>
X#include <setjmp.h>
X#include <stdlib.h>
X#include <limits.h>
X#include <unistd.h>
X#include "sh.h"
X
Xchar		*Copy_Right1 = "MS-DOS SH Version 1.4\341 (DOS %d.%d)\n";
Xchar		*Copy_Right2 = "Copyright (c) Data Logic Ltd and Charles Forsyth 1990\n";
Xchar		**dolv;		/* Parameter array			*/
Xint		dolc;		/* Number of entries in parameter array	*/
Xint		exstat;		/* Exit status				*/
Xchar		gflg;
Xint		fn_area_number = -1;	/* Next function area number	*/
Xint		talking;	/* interactive (talking-type wireless)	*/
Xint		execflg;	/* Exec mode				*/
Xint		multiline;	/* \n changed to ;			*/
Xint		Current_Event = 0;	/* Current history event	*/
Xint		*failpt;	/* Current fail point jump address	*/
Xint		*errpt;		/* Current error point jump address	*/
X				/* Swap mode				*/
Xint		Swap_Mode = SWAP_EXPAND | SWAP_DISK;
XBreak_C		*Break_List;	/* Break list for FOR/WHILE		*/
XBreak_C		*Return_List;	/* Return list for RETURN		*/
XBreak_C		*SShell_List;	/* SubShell list for EXIT		*/
Xbool		level0 = FALSE;	/* Level Zero flag			*/
Xbool		r_flag = FALSE;	/* Restricted shell			*/
X				/* History processing enabled flag	*/
Xbool		History_Enabled = FALSE;
XFun_Ops		*fun_list = (Fun_Ops *)NULL;	/* Function list	*/
XSave_IO		*SSave_IO;	/* Save IO array			*/
Xint		NSave_IO_E = 0;	/* Number of entries in Save IO array	*/
Xint		MSave_IO_E = 0;	/* Max Number of entries in SSave_IO	*/
XS_SubShell	*SubShells;	/* Save Vars array			*/
Xint		NSubShells = 0;	/* Number of entries in SubShells	*/
Xint		MSubShells = 0;	/* Max Number of entries in SubShells	*/
X
XWord_B		*wdlist;	/* Current Word List			*/
XWord_B		*iolist;	/* Current IO List			*/
Xlong		ourtrap = 0L;	/* Signal detected			*/
Xint		trapset;	/* Trap pending				*/
Xint		yynerrs;	/* yacc errors detected			*/
Xint		Execute_stack_depth;	/* execute function recursion	*/
X					/* depth			*/
XVar_List	*vlist = (Var_List *)NULL;	/* dictionary		*/
SHAR_EOF
echo "End of part 3"
echo "File shell/sh6.c is continued in part 4"
echo "4" > s2_seq_.tmp
exit 0

-- 
Regards,

Ian Stewartson
Data Logic Ltd.




More information about the Comp.sources.misc mailing list