Blame | Last modification | View Log | RSS feed
/** MS-DOS SHELL - Symantic Parser** MS-DOS SHELL - Copyright (c) 1990,4 Data Logic Limited and Charles Forsyth** This code is based on (in part) the shell program written by Charles* Forsyth and the subsequence modifications made by Simon J. Gerraty (for* his Public Domain Korn Shell) and is subject to the following copyright* restrictions:** 1. Redistribution and use in source and binary forms are permitted* provided that the above copyright notice is duplicated in the* source form and the copyright notice in file sh6.c is displayed* on entry to the program.** 2. The sources (or parts thereof) or objects generated from the sources* (or parts of sources) cannot be sold under any circumstances.*** $Header: /cvsroot/device/DEVL/UTILS/SH/SH2.C,v 1.1 2002/08/02 06:49:32 adamy Exp $** $Log: SH2.C,v $* Revision 1.1 2002/08/02 06:49:32 adamy* imported (reference only)** Revision 1.1 2001/07/20 05:55:42 ayoung* WIN32 support** Revision 1.1.1.1 1999/12/02 01:11:12 gordonh* UTIL** Revision 2.12 1994/08/25 20:49:11 istewart* MS Shell 2.3 Release** Revision 2.11 1994/02/01 10:25:20 istewart* Release 2.3 Beta 2, including first NT port** Revision 2.10 1994/01/11 17:55:25 istewart* Release 2.3 Beta 0 patches** Revision 2.9 1993/08/25 16:03:57 istewart* Beta 225 - see Notes file** Revision 2.8 1993/07/02 10:21:35 istewart* 224 Beta fixes** Revision 2.7 1993/06/14 11:00:12 istewart* More changes for 223 beta** Revision 2.6 1993/06/02 09:52:35 istewart* Beta 223 Updates - see Notes file** Revision 2.5 1993/02/16 16:03:15 istewart* Beta 2.22 Release** Revision 2.4 1993/01/26 18:35:09 istewart* Release 2.2 beta 0** Revision 2.3 1992/11/06 10:03:44 istewart* 214 Beta test updates** Revision 2.2 1992/09/03 18:54:45 istewart* Beta 213 Updates** Revision 2.1 1992/07/10 10:52:48 istewart* 211 Beta updates** Revision 2.0 1992/04/13 17:39:09 Ian_Stewartson* MS-Shell 2.0 Baseline release**/#include <sys/types.h>#include <sys/stat.h>#include <stdio.h>#include <stddef.h>#include <signal.h>#include <errno.h>#include <setjmp.h>#include <string.h>#include <ctype.h>#include <unistd.h>#include <limits.h>#include <dirent.h>#include "sh.h"static C_Op * F_LOCAL ScanPipeSyntax (int);static C_Op * F_LOCAL ScanAndOrSyntax (void);static C_Op * F_LOCAL CommandList (void);static IO_Actions * F_LOCAL SynchroniseIOList (int);static void F_LOCAL CheckNextTokenIS (int, int);static void F_LOCAL SyntaxError (char *);static C_Op * F_LOCAL ScanNestedSyntax (int, int);static C_Op * F_LOCAL ScanSimpleCommand (int);static C_Op * F_LOCAL GetDoDoneCommandList (void);static C_Op * F_LOCAL ThenPartList (void);static C_Op * F_LOCAL ElsePartList (void);static C_Op * F_LOCAL CaseList (void);static C_Op * F_LOCAL CaseListEntries (void);static char ** F_LOCAL GetINWordList (void);static C_Op * F_LOCAL SetupTreeNode (int, C_Op *, C_Op *, char **);static C_Op * F_LOCAL CreateTreeNode (void);static C_Op * F_LOCAL yyparse (void);static char * F_LOCAL LookUpToken (int);static int F_LOCAL GetNextToken (int);static int F_LOCAL LookAtNextToken (int);static char * F_LOCAL MemoryDup (char *, size_t);static char *LIT_2ManyRedir = "Too many redirections";static char *LIT_Expecting = "%s - Expecting '%s', found '%s'";/** Special strings for (( and ))*/static char LIT_ODP [] = {WORD_CHAR,CHAR_OPEN_PARATHENSIS,WORD_CHAR,CHAR_OPEN_PARATHENSIS,0};static char LIT_CDP [] = {WORD_CHAR,CHAR_CLOSE_PARATHENSIS,WORD_CHAR,CHAR_CLOSE_PARATHENSIS,0};/* Special [[ */static char LIT_ODB [] = {WORD_CHAR,CHAR_OPEN_BRACKETS,WORD_CHAR,CHAR_OPEN_BRACKETS,0};/** Other statics*/static bool reject; /* GetNextToken(cf) gets symbol *//* again */static int symbol; /* yylex value */#define NEWNODE ((C_Op *)CreateTreeNode ())#define REJECT (reject = TRUE)#define ACCEPT (reject = FALSE)/** Get the next token from input*/static int F_LOCAL GetNextToken (int cf){if (reject)ACCEPT;else{symbol = ScanNextToken (cf);ACCEPT;}return symbol;}/** Look at the next token from input*/static int F_LOCAL LookAtNextToken (int cf){if (!reject){symbol = ScanNextToken (cf);REJECT;}return symbol;}/** Parse the current input stack*/static C_Op * F_LOCAL yyparse (void){C_Op *t; /* yyparse output */ACCEPT;yynerrs = 0;/* Check for EOF */if ((LookAtNextToken (ALLOW_KEYWORD | ALLOW_ALIAS)) == 0){(t = NEWNODE)->type = TEOF;DPRINT (1, ("yyparse: Create TEOF"));}else{t = CommandList ();CheckNextTokenIS (CHAR_NEW_LINE, 0);}return t;}/** Check a pipeline*/static C_Op * F_LOCAL ScanPipeSyntax (int LexicalControlFlags){C_Op *t, *p, *tl = NULL;if ((t = ScanSimpleCommand (LexicalControlFlags)) != (C_Op *)NULL){while (GetNextToken (0) == CHAR_PIPE){if ((p = ScanSimpleCommand (ALLOW_CONTINUATION)) == NULL)SyntaxError ("no commands found following pipe");if (tl == NULL){tl = SetupTreeNode (TPIPE, t, p, NOWORDS);t = tl;}else{tl->right = SetupTreeNode (TPIPE, tl->right, p, NOWORDS);tl = tl->right;}}REJECT;}return t;}static C_Op * F_LOCAL ScanAndOrSyntax (void){C_Op *t, *p;int c;t = ScanPipeSyntax (0);if (t != NULL){while (((c = GetNextToken (0)) == PARSE_LOGICAL_AND) ||(c == PARSE_LOGICAL_OR)){if ((p = ScanPipeSyntax (ALLOW_CONTINUATION)) == NULL)SyntaxError ("no commands found following || or &&");t = SetupTreeNode ((c == PARSE_LOGICAL_AND) ? TAND : TOR, t, p,NOWORDS);}REJECT;}return t;}static C_Op *F_LOCAL CommandList (void){C_Op *t, *p, *tl = NULL;int c;if ((t = ScanAndOrSyntax ()) != NULL){while (((c = GetNextToken (0)) == CHAR_SEPARATOR) ||(c == CHAR_ASYNC) || (c == PARSE_COPROCESS) ||((AllowMultipleLines ||(source->type == SSTRING) ||(source->type == SALIAS)) && (c == CHAR_NEW_LINE))){if ((c == CHAR_ASYNC) || (c == PARSE_COPROCESS)){c = (c == CHAR_ASYNC) ? TASYNC : TCOPROCESS;if (tl)tl->right = SetupTreeNode (c, tl->right, NOBLOCK, NOWORDS);elset = SetupTreeNode (c, t, NOBLOCK, NOWORDS);}if ((p = ScanAndOrSyntax ()) == NULL)return t;if (tl == NULL){tl = SetupTreeNode (TLIST, t, p, NOWORDS);t = tl;}else{tl->right = SetupTreeNode (TLIST, tl->right, p, NOWORDS);tl = tl->right;}}REJECT;}return t;}/** Handle IO re-direction*/static IO_Actions * F_LOCAL SynchroniseIOList (int LexicalControlFlags){IO_Actions *iop;if (LookAtNextToken (LexicalControlFlags) != PARSE_REDIR)return (IO_Actions *)NULL;ACCEPT;iop = yylval.iop;CheckNextTokenIS (PARSE_WORD, 0);iop->io_name = yylval.cp;if ((iop->io_flag & IOTYPE) == IOHERE){if (*CurrentLexIdentifier != 0) /* unquoted */iop->io_flag |= IOEVAL;SaveHereDocumentInfo (iop);}return iop;}static void F_LOCAL CheckNextTokenIS (int c, int LexicalControlFlags){int got;if ((got = GetNextToken (LexicalControlFlags)) != c){CompilingError ();ShellErrorMessage (LIT_Expecting, LIT_SyntaxError, LookUpToken (c),LookUpToken (got));}}/** Handle Nested thingys - ( and {*/static C_Op * F_LOCAL ScanNestedSyntax (int type, int mark){C_Op *t;AllowMultipleLines++;t = CommandList ();CheckNextTokenIS (mark, ALLOW_KEYWORD);AllowMultipleLines--;return SetupTreeNode (type, t, NOBLOCK, NOWORDS);}/** Handle a single command and its bits and pieces - IO redirection,* arguments and variable assignments*/static C_Op * F_LOCAL ScanSimpleCommand (int LexicalControlFlags){C_Op *t;int c;IO_Actions *iop;Word_B *Arguments = (Word_B *)NULL;Word_B *Variables = (Word_B *)NULL;Word_B *IOactions = (Word_B *)NULL;/* Allocate space for IO actions structures */if (AllowMultipleLines)LexicalControlFlags = ALLOW_CONTINUATION;LexicalControlFlags |= ALLOW_KEYWORD | ALLOW_ALIAS;while ((iop = SynchroniseIOList (LexicalControlFlags)) != NULL){if (WordBlockSize (IOactions) >= NUFILE){CompilingError ();ShellErrorMessage (LIT_2ManyRedir);}IOactions = AddWordToBlock ((char *)iop, IOactions);LexicalControlFlags &=~ ALLOW_CONTINUATION;}switch (c = GetNextToken (LexicalControlFlags)){case 0:CompilingError ();ShellErrorMessage ("unexpected EOF");return NULL;case CHAR_SEPARATOR:REJECT;(t = NEWNODE)->type = TCOM;DPRINT (1, ("ScanSimpleCommand: Create TCOM"));break;default:REJECT;if (WordBlockSize (IOactions) == 0)return (C_Op *)NULL; /* empty line */(t = NEWNODE)->type = TCOM;DPRINT (1, ("ScanSimpleCommand: Create TCOM"));break;case PARSE_WORD:case PARSE_MDPAREN:REJECT;(t = NEWNODE)->type = TCOM;DPRINT (1, ("ScanSimpleCommand: Create TCOM"));if (c == PARSE_MDPAREN){ACCEPT;Arguments = AddWordToBlock (MemoryDup (LIT_ODP, 5), Arguments);CheckNextTokenIS (PARSE_WORD, MATHS_EXPRESSION);Arguments = AddWordToBlock (yylval.cp, Arguments);Arguments = AddWordToBlock (MemoryDup (LIT_CDP, 5), Arguments);}while (1){switch (LookAtNextToken (0)){case PARSE_REDIR:if (WordBlockSize (IOactions) >= NUFILE){CompilingError ();ShellErrorMessage (LIT_2ManyRedir);}IOactions = AddWordToBlock ((char *)SynchroniseIOList (0),IOactions);break;/** Word - check to see if this should be an argument or a variable,* depending on what we've seen, the state of the k flag and an* assignment in the word*/case PARSE_WORD:ACCEPT;if (((WordBlockSize (Arguments) == 0) ||FL_TEST (FLAG_ALL_KEYWORDS)) &&(strchr (CurrentLexIdentifier + 1, CHAR_ASSIGN) !=(char *)NULL))Variables = AddWordToBlock (yylval.cp, Variables);elseArguments = AddWordToBlock (yylval.cp, Arguments);break;case PARSE_MPAREN:ACCEPT;if (WordBlockSize (Arguments) != 1)SyntaxError ("Too many function names");if (*CurrentLexIdentifier == 0)SyntaxError ("Bad function name");(t = NEWNODE)->type = TFUNC;DPRINT (1, ("ScanSimpleCommand: Create TFUNC"));t->str = StringCopy (CurrentLexIdentifier);CheckNextTokenIS (CHAR_OPEN_BRACES,ALLOW_CONTINUATION | ALLOW_KEYWORD);t->left = ScanNestedSyntax (TBRACE, CHAR_CLOSE_BRACES);return t;default:goto Leave;}}Leave:break;case CHAR_OPEN_PARATHENSIS:t = ScanNestedSyntax (TPAREN, CHAR_CLOSE_PARATHENSIS);break;case CHAR_OPEN_BRACES:t = ScanNestedSyntax (TBRACE, CHAR_CLOSE_BRACES);break;/** Format for: [[ ..... ]]*/case PARSE_TEST:(t = NEWNODE)->type = TCOM;DPRINT (1, ("ScanSimpleCommand: Create TCOM"));Arguments = AddWordToBlock (MemoryDup (LIT_ODB, 5), Arguments);while (GetNextToken (TEST_EXPRESSION) == PARSE_WORD){Arguments = AddWordToBlock (yylval.cp, Arguments);if (strcmp (CurrentLexIdentifier , "]]") == 0)break;}break;/** Format for: select word in list do .... done* select word do .... done* for word in list do .... done* for word do .... done*/case PARSE_FOR:case PARSE_SELECT:(t = NEWNODE)->type = (c == PARSE_FOR) ? TFOR : TSELECT;DPRINT (1, ("ScanSimpleCommand: Create TFOR/TSELECT"));CheckNextTokenIS (PARSE_WORD, 0);t->str = StringCopy (CurrentLexIdentifier);AllowMultipleLines++;t->vars = GetINWordList ();t->left = GetDoDoneCommandList ();AllowMultipleLines--;break;/** Format for: while command do ... done* until command do ... done*/case PARSE_WHILE:case PARSE_UNTIL:AllowMultipleLines++;(t = NEWNODE)->type = (c == PARSE_WHILE) ? TWHILE : TUNTIL;DPRINT (1, ("ScanSimpleCommand: Create TWHILE/TUNTIL"));t->left = CommandList ();t->right = GetDoDoneCommandList ();AllowMultipleLines--;break;/** Format for: case name in .... esac*/case PARSE_CASE:(t = NEWNODE)->type = TCASE;DPRINT (1, ("ScanSimpleCommand: Create TCASE"));CheckNextTokenIS (PARSE_WORD, 0);t->str = yylval.cp;AllowMultipleLines++;CheckNextTokenIS (PARSE_IN, ALLOW_KEYWORD | ALLOW_CONTINUATION);t->left = CaseList ();CheckNextTokenIS (PARSE_ESAC, ALLOW_KEYWORD);AllowMultipleLines--;break;/** Format for: if command then command fi* if command then command else command fi* if command then command elif command then ... else ... fi*/case PARSE_IF:AllowMultipleLines++;(t = NEWNODE)->type = TIF;DPRINT (1, ("ScanSimpleCommand: Create TIF"));t->left = CommandList ();t->right = ThenPartList ();CheckNextTokenIS (PARSE_FI, ALLOW_KEYWORD);AllowMultipleLines--;break;/** Format for: time command*/case PARSE_TIME:t = ScanPipeSyntax (ALLOW_CONTINUATION);t = SetupTreeNode (TTIME, t, NOBLOCK, NOWORDS);break;/** Format for: function name { .... }*/case PARSE_FUNCTION:(t = NEWNODE)->type = TFUNC;DPRINT (1, ("ScanSimpleCommand: Create TFUNC"));CheckNextTokenIS (PARSE_WORD, 0);t->str = StringCopy (CurrentLexIdentifier);CheckNextTokenIS (CHAR_OPEN_BRACES,(ALLOW_CONTINUATION | ALLOW_KEYWORD));t->left = ScanNestedSyntax (TBRACE, CHAR_CLOSE_BRACES);break;}/* Get any remaining IOactions */while ((iop = SynchroniseIOList (ALLOW_KEYWORD)) != NULL){if (WordBlockSize (IOactions) >= NUFILE){CompilingError ();ShellErrorMessage (LIT_2ManyRedir);}IOactions = AddWordToBlock ((char *)iop, IOactions);}/* Save the IOactions */if (WordBlockSize (IOactions) == 0)t->ioact = (IO_Actions **)NULL;elset->ioact = (IO_Actions **) GetWordList (AddWordToBlock (NOWORD,IOactions));/* If TCOM, save the arguments and variable assignments */if (t->type == TCOM){t->args = GetWordList (AddWordToBlock (NOWORD, Arguments));t->vars = GetWordList (AddWordToBlock (NOWORD, Variables));}/* Handle re-direction on other pipelines */else if ((t->type != TPAREN) && (t->ioact != (IO_Actions **)NULL)){C_Op *t1 = t;(t = NEWNODE)->type = TPAREN;DPRINT (1, ("ScanSimpleCommand: Create TPAREN"));t->left = t1;t->right = NOBLOCK;t->args = NOWORDS;t->vars = NOWORDS;t->ioact = t1->ioact;t1->ioact = (IO_Actions **)NULL;}/** We should probably release IOactions, Arguments and Variables if they* are not used. However, I don't think its necessary. The release memory* level should do it.*/return t;}/** Processing for the do grouping - do ... done*/static C_Op * F_LOCAL GetDoDoneCommandList (void){int c;C_Op *list;if ((c = GetNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_DO){CompilingError ();ShellErrorMessage (LIT_Expecting, LIT_SyntaxError, "do",LookUpToken (c));}list = CommandList ();CheckNextTokenIS (PARSE_DONE, ALLOW_KEYWORD);return list;}/** Handle the then part of an if statement*/static C_Op * F_LOCAL ThenPartList (void){C_Op *t;if (GetNextToken (0) != PARSE_THEN){REJECT;return (C_Op *)NULL;}(t = NEWNODE)->type = 0;DPRINT (1, ("ThenPartList: Create dummy"));if ((t->left = CommandList ()) == (C_Op *)NULL)SyntaxError ("no command found after then");t->right = ElsePartList ();return t;}/** Handle the else part of an if statement*/static C_Op * F_LOCAL ElsePartList (void){C_Op *t;switch (GetNextToken (0)){case PARSE_ELSE:if ((t = CommandList ()) == (C_Op *)NULL)SyntaxError ("no commands associated with else");return t;case PARSE_ELIF:(t = NEWNODE)->type = TELIF;DPRINT (1, ("ElsePartList: Create TELIF"));t->left = CommandList ();t->right = ThenPartList ();return t;default:REJECT;return (C_Op *)NULL;}}/** Process the CASE statment*/static C_Op * F_LOCAL CaseList (void){C_Op *t = (C_Op *)NULL;C_Op *tl = (C_Op *)NULL;while ((LookAtNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_ESAC){C_Op *tc = CaseListEntries ();if (tl == (C_Op *)NULL){t = tc;(tl = tc)->right = (C_Op *)NULL;}else{tl->right = tc;tl = tc;}}return t;}/** Process an individual case entry: pattern) commands;;*/static C_Op * F_LOCAL CaseListEntries (void){C_Op *t;int LexicalControlFlags = ALLOW_CONTINUATION | ALLOW_KEYWORD;Word_B *Patterns = (Word_B *)NULL;(t = NEWNODE)->type = TPAT;DPRINT (1, ("CaseListEntries: Create TPAT"));if (GetNextToken (LexicalControlFlags) != CHAR_OPEN_PARATHENSIS)REJECT;elseLexicalControlFlags = 0;do{CheckNextTokenIS (PARSE_WORD, LexicalControlFlags);Patterns = AddWordToBlock (yylval.cp, Patterns);LexicalControlFlags = 0;} while (GetNextToken (0) == CHAR_PIPE);REJECT;/** Terminate the list of patterns*/t->vars = GetWordList (AddWordToBlock (NOWORD, Patterns));/** Check for the terminating ), and get the command list*/CheckNextTokenIS (CHAR_CLOSE_PARATHENSIS, 0);t->left = CommandList ();if ((LookAtNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_ESAC)CheckNextTokenIS (PARSE_BREAK, ALLOW_CONTINUATION | ALLOW_KEYWORD);return (t);}/** Handle the in words.... part of a for or select statement. Get the* words and build a list.*/static char ** F_LOCAL GetINWordList (void){int c;Word_B *Parameters = (Word_B *)NULL;/** Check to see if the next symbol is "in". If not there are no words* following*/if ((c = GetNextToken (ALLOW_CONTINUATION | ALLOW_KEYWORD)) != PARSE_IN){REJECT;return NOWORDS;}/* Get the list */while ((c = GetNextToken (0)) == PARSE_WORD)Parameters = AddWordToBlock (yylval.cp, Parameters);if ((c != CHAR_NEW_LINE) && (c != CHAR_SEPARATOR)){CompilingError ();ShellErrorMessage (LIT_Expecting, LIT_SyntaxError, "newline' or ';",LookUpToken (c));}/* Are there any words found? */if (Parameters == (Word_B *)NULL)return NOWORDS;return GetWordList (AddWordToBlock (NOWORD, Parameters));}/** supporting functions*/static C_Op * F_LOCAL SetupTreeNode (int type, C_Op *t1, C_Op *t2, char **wp){C_Op *t;(t = NEWNODE)->type = type;DPRINT (1, ("SetupTreeNode: Create %d", type));t->left = t1;t->right = t2;t->vars = wp;return t;}/** Get and compile the next command from the user/file etc*/C_Op *BuildParseTree (Source *s){C_Op *t; /* yyparse output */yynerrs = 0;AllowMultipleLines = 0;source = s;t = yyparse ();if (s->type == STTY || s->type == SFILE)s->str = null; /* line is not preserved */return yynerrs ? (C_Op *)NULL : t;}/** Get a new tree leaf structure*/static C_Op * F_LOCAL CreateTreeNode (void){C_Op *t;if ((t = (C_Op *)AllocateMemoryCell (sizeof (C_Op))) == (C_Op *)NULL)ShellErrorMessage ("command line too complicated");return t;}/** List of keywords*/static struct res {char *r_name;int r_val;} restab[] = {{ "for", PARSE_FOR}, { "case", PARSE_CASE},{ "esac", PARSE_ESAC}, { "while", PARSE_WHILE},{ "do", PARSE_DO}, { LIT_done, PARSE_DONE},{ "if", PARSE_IF}, { "in", PARSE_IN},{ "then", PARSE_THEN}, { "else", PARSE_ELSE},{ "elif", PARSE_ELIF}, { "until", PARSE_UNTIL},{ "fi", PARSE_FI}, { "select", PARSE_SELECT},{ "time", PARSE_TIME}, { "function", PARSE_FUNCTION},{ LIT_Test, PARSE_TEST},{ "{", CHAR_OPEN_BRACES}, { "}", CHAR_CLOSE_BRACES},{ (char *)NULL, 0},/* Additional definitions */{ "word", PARSE_WORD}, { "&&", PARSE_LOGICAL_AND},{ "||", PARSE_LOGICAL_OR}, { "redirection",PARSE_REDIR },{ "(..)", PARSE_MPAREN}, { "((...))", PARSE_MDPAREN},{ "|&", PARSE_COPROCESS}, { "newline",'\n'},{ (char *)NULL, 0}};int LookUpSymbol (char *n){struct res *rp = restab;while ((rp->r_name != (char *)NULL) && strcmp (rp->r_name, n))rp++;return rp->r_val;}static char * F_LOCAL LookUpToken (int n){struct res *rp = restab;int first = TRUE;while (TRUE){if ((rp->r_name == (char *)NULL) && !first){char *cp = GetAllocatedSpace (4);if (cp == (char *)NULL)return (char *)NULL;sprintf (cp, "%c", n);return cp;}else if (rp->r_name == (char *)NULL)first = FALSE;else if (rp->r_val == n)return rp->r_name;rp++;}}/** Syntax error*/static void F_LOCAL SyntaxError (char *emsg){CompilingError ();ShellErrorMessage ("%s - %s", LIT_SyntaxError, emsg);}/** Duplicate a memory string*/static char * F_LOCAL MemoryDup (char *string, size_t length){char *t;if ((t = AllocateMemoryCell (length)) == (char *)NULL)ShellErrorMessage ("Out of memory");return memcpy (t, string, length);}