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