Blame | Last modification | View Log | RSS feed
/** MS-DOS SHELL - 'word' Interpretator** 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/SH4.C,v 1.1 2002/08/02 06:49:32 adamy Exp $** $Log: SH4.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.2 2000/09/27 08:33:35 adamy* Added EXTENDED_LINE cache and use __systeml_mode during DOS builds.** Revision 1.1.1.1 1999/12/02 01:11:12 gordonh* UTIL** Revision 2.13 1994/08/25 20:49:11 istewart* MS Shell 2.3 Release** Revision 2.12 1994/02/01 10:25:20 istewart* Release 2.3 Beta 2, including first NT port** Revision 2.11 1994/01/11 17:55:25 istewart* Release 2.3 Beta 0 patches** Revision 2.10 1993/08/25 16:03:57 istewart* Beta 225 - see Notes file** Revision 2.9 1993/07/02 10:21:35 istewart* 224 Beta fixes** Revision 2.8 1993/06/14 11:00:12 istewart* More changes for 223 beta** Revision 2.7 1993/06/02 09:52:35 istewart* Beta 223 Updates - see Notes file** Revision 2.6 1993/02/16 16:03:15 istewart* Beta 2.22 Release** Revision 2.5 1993/01/26 18:35:09 istewart* Release 2.2 beta 0** Revision 2.4 1992/12/14 10:54:56 istewart* BETA 215 Fixes and 2.1 Release** 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 <limits.h> /* String library functions */#include <signal.h>#include <errno.h>#include <setjmp.h>#include <dirent.h>#include <string.h>#include <stdlib.h>#include <unistd.h>#include <ctype.h>#include <fcntl.h>#include "sh.h"/** string expansion** first pass: quoting, IFS separation, ${} and $() substitution.* second pass: filename expansion (*?[]~).*//** expansion generator state*/typedef struct Expand {/* int type; */ /* see ExpandAWord() */char *str; /* string */union {char **strv; /* string[] */FILE *file; /* file */} u; /* source */bool split; /* split "$@" */} Expand;#define XBASE 0 /* scanning original */#define XSUB 1 /* expanding ${} string */#define XARGSEP 2 /* ifs0 between "$@" */#define XARG 3 /* expanding $*, $@ */#define XCOM 4 /* expanding $() *//** Quote processing*/#define QUOTE_NONE 0 /* None */#define QUOTE_INSIDE 1 /* Inside quotes */#define QUOTE_TEMP 2 /* Mark a temporary quote *//** for nested substitution: ${var:=$var2}*/typedef struct SubType {short type; /* [=+-?%#] action after expanded word */short base; /* begin position of expanded word */char *name; /* name for ${var=word} */int index; /* index ${var[index]=word} */} SubType;static void F_LOCAL ExpandAWord (char *, Word_B **, int);static int F_LOCAL VariableSubstitute (Expand *, char *, int, int);static int F_LOCAL CommandSubstitute (Expand *, char *);static char * F_LOCAL TrimSubstitute (SubType *, char *);static int F_LOCAL MathsSubstitute (Expand *, char *);static void F_LOCAL ExpandGlobCharacters (char *, Word_B **);static void F_LOCAL GlobAWord (char *, char *, char *, Word_B **, bool);static char * F_LOCAL RemoveMagicMarkers (unsigned char *);static char * F_LOCAL TildeSubstitution (unsigned char *);static unsigned char * F_LOCAL CheckForMultipleDrives (unsigned char *);static bool F_LOCAL ProcessCommandTree (C_Op *, int);static char * F_LOCAL WordScan (char *, int);static int F_LOCAL HandleArrayValue (char *, char **, int);static void F_LOCAL CheckForUnset (char *, int);static void F_LOCAL AlternationExpand (char *, Word_B **, int);static int F_LOCAL AlternationScan (char **, char **, char, int);static void BuildVariableEntryList (const void *, VISIT, int);static int F_LOCAL GetNumberofFloppyDrives (void);static char *PNullNSet = "%s: parameter null or not set";static char *GVAV_Name; /* Name for building a *//* list of Variable *//* Array values */static Word_B *GVAV_WordList; /* Word block for list *//** compile and expand word*/char *substitute (char *cp, int ExpandMode){struct source *sold = source;char *res;source = pushs (SWSTR);source->str = (char *) cp;if (ScanNextToken (ONEWORD) != PARSE_WORD)PrintErrorMessage ("eval: substitute error");res = ExpandAString (yylval.cp, ExpandMode);source = sold;return res;}/** expand arg-list*/char **ExpandWordList (char **ap, int ExpandMode, ExeMode *PMode){Word_B *w = (Word_B *)NULL;bool FoundProgram = FALSE;int i;int InitialCount;char *CurrentAp;if ((ap == NOWORDS) || (*ap == NOWORD))return ap;/* Expand the arguments */while (*ap != NOWORD){InitialCount = WordBlockSize (w);ExpandAWord (CurrentAp = *(ap++), &w, ExpandMode);/** Get the program mode for expansion of words and globs. Update the* current mode to reflect it.*/if ((!FoundProgram) &&(PMode != (ExeMode *)NULL) && WordBlockSize (w)){CheckProgramMode (w->w_words[0], PMode);FoundProgram = TRUE; /* One shot config check */if (PMode->Flags & EP_NOEXPAND)ExpandMode &= ~EXPAND_GLOBBING;if (PMode->Flags & EP_NOWORDS)ExpandMode &= ~EXPAND_SPLITIFS;if (PMode->Flags & EP_CONVERT)ExpandMode |= EXPAND_CONVERT;}/* Convert UNIX directories to DOS and - to / ?, except for first* argument*/if (ExpandMode & EXPAND_CONVERT){for (i = max (InitialCount, 1); i < WordBlockSize (w); i++){PATH_TO_DOS (w->w_words[i]);/* Convert - to /, if the string is not quoted */if ((*(w->w_words[i]) == CHAR_SWITCH) &&((*CurrentAp != WORD_OQUOTE) ||(*CurrentAp != WORD_QTCHAR)))*(w->w_words[i]) = '/';}}}/* Return the word list */return GetWordList (AddWordToBlock (NOWORD, w));}/** expand string*/char *ExpandAString (char *cp, int ExpandMode){Word_B *w = (Word_B *)NULL;ExpandAWord (cp, &w, ExpandMode);return (WordBlockSize (w) == 0) ? null : w->w_words[0];}/** expand string - return only one component* used from iosetup to expand redirection files*/char *ExpandOneStringFirstComponent (char *cp, int ExpandMode){Word_B *w = (Word_B *)NULL;ExpandAWord (cp, &w, ExpandMode);switch (WordBlockSize (w)){case 0:return null;case 1:return w->w_words[0];}return ExpandAString (cp, ExpandMode & ~EXPAND_GLOBBING);}/** Expand a word or two!*/static void F_LOCAL ExpandAWord (char *OriginalWord, /* input word */Word_B **WordList, /* output word list*/int ExpandMode) /* Expand Flags */{int c;int type = XBASE; /* expansion type */int QuoteStatus = 0; /* quoted */int quotestack[11]; /* Keep this bigger than the *//* subtype stack */int *qst = quotestack + 11;XString ds; /* Expandable destination string*/unsigned char *dp; /* Pointer into Destination S. */char *sp; /* source */int fdo; /* second pass flags; have word */int word;int combase;int ArrayIndex;Expand x; /* expansion variables */SubType subtype [10]; /* substitution type stack */SubType *st = subtype + 10;int newlines; /* For trailing newlines in COMSUB */int trimming = 0; /* flag if expanding ${var#pat} *//* or ${var%pat} */char ifs0 = *GetVariableAsString (IFS, FALSE);if (OriginalWord == NULL)PrintErrorMessage ("eval: expanding a NULL");if (FL_TEST (FLAG_DISABLE_GLOB))ExpandMode &= (~EXPAND_GLOBBING);/** Look for '{' in the input word*/if ((ShellGlobalFlags & FLAGS_ALTERNATION) &&(!(ExpandMode & EXPAND_NOALTS)) &&(ExpandMode & EXPAND_GLOBBING) &&((sp = strchr (OriginalWord, CHAR_OPEN_BRACES)) != (char *)NULL) &&(sp[-1] == WORD_CHAR) &&(!(sp[1] == WORD_CHAR && sp[2] == CHAR_CLOSE_BRACES))){AlternationExpand (OriginalWord, WordList, ExpandMode);return;}ExpandMode &= ~EXPAND_NOALTS;/* Initialise */dp = (unsigned char *)XCreate (&ds, 128); /* destination string */type = XBASE;sp = OriginalWord;fdo = 0;word = !(ExpandMode & EXPAND_SPLITIFS);/* The Main loop!! */while (1){XCheck (&ds, (unsigned char **)(&dp));switch (type){case XBASE: /* original prefixed string */switch ((c = *(sp++))){case WORD_EOS: /* End - Hurray */c = 0;break;case WORD_CHAR: /* Normal char */c = *(sp++);break;case WORD_QCHAR: /* Escaped char */case WORD_QTCHAR:QuoteStatus |= QUOTE_TEMP;/* temporary quote */c = *(sp++);break;case WORD_OQUOTE: /* Start quoted */case WORD_ODQUOTE:word = 1;QuoteStatus = QUOTE_INSIDE;continue;case WORD_CQUOTE: /* End quoted */case WORD_CDQUOTE:QuoteStatus = QUOTE_NONE;continue;case WORD_COMSUB: /* $(....) */type = CommandSubstitute (&x, sp);sp = strchr (sp, 0) + 1;combase = XCurrentOffset (ds, dp);newlines = 0;continue;case WORD_OMATHS: /* $((....)) */type = MathsSubstitute (&x, sp);sp = strchr (sp, 0) + 1;continue;case WORD_OSUBST: /* ${var{:}[=+-?]word} */OriginalWord = sp; /* variable */sp = strchr (sp, 0) + 1; /* skip variable *//* Check for Array Variable */ArrayIndex = 0;if (*sp == WORD_OARRAY)ArrayIndex = HandleArrayValue (OriginalWord, &sp,ExpandMode);c = (*sp == WORD_CSUBST) ? 0 : *(sp++);/* Check for match option */if (((c & 0x7f) == CHAR_MATCH_START) ||((c & 0x7f) == CHAR_MATCH_END)){CheckForUnset (OriginalWord, ArrayIndex);trimming++;type = XBASE;*--qst = QuoteStatus;QuoteStatus = QUOTE_NONE;}elsetype = VariableSubstitute (&x, OriginalWord,c, ArrayIndex);/* expand? */if (type == XBASE){if (st == subtype)ShellErrorMessage ("ridiculous ${} nesting");--st;st->type = c;st->base = XCurrentOffset (ds, dp);st->name = OriginalWord;st->index = ArrayIndex;}elsesp = WordScan (sp, WORD_CSUBST); /* skip word */continue;case WORD_CSUBST: /* only get here if expanding word */*dp = 0;if (ExpandMode & EXPAND_GLOBBING)ExpandMode &= (~EXPAND_PATTERN);/** Check that full functionality is here!*/switch (st->type & 0x7f){case CHAR_MATCH_START:case CHAR_MATCH_END:*dp = 0;dp = XResetOffset (ds, st->base);QuoteStatus = *(qst++);x.str = TrimSubstitute (st, (char *)dp);type = XSUB;trimming--;continue;case CHAR_ASSIGN:SetVariableArrayFromString(st->name, st->index,(char *)XResetOffset (ds, st->base));break;case '?':if (dp == XResetOffset (ds, st->base))ShellErrorMessage (PNullNSet, OriginalWord);elseShellErrorMessage ("%s",XResetOffset (ds,st->base));}st++;type = XBASE;continue;}break;case XSUB:if ((c = *(x.str++)) == 0){type = XBASE;continue;}break;case XARGSEP:type = XARG;QuoteStatus = QUOTE_INSIDE;case XARG:if ((c = *(x.str++)) == 0){if ((x.str = *(x.u.strv++)) == NULL){type = XBASE;continue;}else if (QuoteStatus && x.split){type = XARGSEP; /* terminate word for "$@" */QuoteStatus = QUOTE_NONE;}c = ifs0;}break;case XCOM:if (newlines) /* Spit out saved nl's */{c = CHAR_NEW_LINE;--newlines;}else{while ((c = getc (x.u.file)) == CHAR_NEW_LINE)newlines++; /* Save newlines */if (newlines && (c != EOF)){ungetc (c, x.u.file);c = CHAR_NEW_LINE;--newlines;}}if (c == EOF){OriginalWord = (char *)XResetOffset (ds, combase);newlines = 0;S_fclose (x.u.file, TRUE);type = XBASE;continue;}break;}/* check for end of word or IFS separation */if ((c == 0) || (!QuoteStatus && (ExpandMode & EXPAND_SPLITIFS) &&IS_IFS (c))){if (word){*(dp++) = 0;OriginalWord = XClose (&ds, dp);if (fdo & EXPAND_TILDE)OriginalWord =TildeSubstitution ((unsigned char *)OriginalWord);if (fdo & EXPAND_GLOBBING)ExpandGlobCharacters (OriginalWord, WordList);else*WordList = AddWordToBlock (OriginalWord, *WordList);/* Re-set */fdo = 0;word = 0;if (c != 0)dp = (unsigned char *)XCreate (&ds, 128);}if (c == 0)return;}/** Mark any special second pass chars*/else{if (!QuoteStatus){switch (c){case CHAR_MATCH_ALL:case CHAR_MATCH_ANY:case CHAR_OPEN_BRACKETS:if ((ExpandMode & (EXPAND_PATTERN | EXPAND_GLOBBING)) ||trimming){fdo |= (ExpandMode & EXPAND_GLOBBING);*dp++ = CHAR_MAGIC;}break;/** Check for [^...*/case CHAR_NOT:if (((ExpandMode & (EXPAND_PATTERN | EXPAND_GLOBBING))|| trimming) &&((dp[-1] == CHAR_OPEN_BRACKETS) &&(dp[-2] == CHAR_MAGIC)))*dp++ = CHAR_MAGIC;break;case CHAR_TILDE:if (((ExpandMode & EXPAND_TILDE) &&(dp == XStart (ds))) ||(!(ExpandMode & EXPAND_SPLITIFS) &&(dp[-1] == '=' || dp[-1] == ':'))){fdo |= EXPAND_TILDE;*dp++ = CHAR_MAGIC;}break;}}elseQuoteStatus &= ~QUOTE_TEMP; /* undo temporary */word = 1;*dp++ = (char)c; /* save output char */}}}/** Prepare to generate the string returned by ${} substitution.*/static int F_LOCAL VariableSubstitute (Expand *xp,char *name,int stype,int Index){int c;int type;/* Handle ${#*|@}* ${#name[*]}* ${#name[value]}** String length or argc*/if ((*name == '#') && ((c = name[1]) != 0)){if ((c == '*') || (c == '@'))c = ParameterCount - 1;else if (Index < 0)c = CountVariableArraySize (name + 1);elsec = strlen (GetVariableArrayAsString (name + 1, Index, FALSE));xp->str = StringCopy (IntegerToString (c));return XSUB;}c = *name;/* Handle ${*|@}* ${*|@[*|@]}** Use Parameter list*/if (c == '*' || c == '@'){if (ParameterCount == 0){xp->str = null;type = XSUB;}else{xp->u.strv = ParameterArray + 1 + ((Index >= 0) ? Index : 0);xp->str = *(xp->u.strv++);xp->split = C2bool (c == '@'); /* $@ */type = XARG;}}/* ${name[*|@]} */else if (Index < 0){/* Build list of values */if (isdigit (*name)){for (c = 0; isdigit (*name) && (c < 1000); name++)c = c * 10 + *name - '0';xp->u.strv = (c <= ParameterCount) ? ParameterArray + c: NOWORDS;}else{GVAV_Name = name;GVAV_WordList = (Word_B *)NULL;twalk (VariableTree, BuildVariableEntryList);xp->u.strv = WordBlockSize (GVAV_WordList)? GetWordList (AddWordToBlock (NOWORD,GVAV_WordList)): NOWORDS;}/* Set up list. Check to see if there any any entries */if (xp->u.strv == NOWORDS){xp->str = null;type = XSUB;}else{xp->str = *(xp->u.strv++);xp->split = C2bool (Index == -2); /* ${name[@]} */type = XARG;}}/* ${name[num]} */else{xp->str = GetVariableArrayAsString (name, Index, TRUE);type = XSUB;}c = stype & 0x7F;/* test the compiler's code generator */if ((c == CHAR_MATCH_END) || (c == CHAR_MATCH_START) ||(((stype & CHAR_MAGIC) ? (*xp->str == 0): (xp->str == null))? (c == '=') || (c == '-') || (c == '?'): (c == '+')))type = XBASE; /* expand word instead of variable value *//* Check for unset value */if ((type != XBASE) && FL_TEST (FLAG_UNSET_ERROR) &&(xp->str == null) && (c != '+'))ShellErrorMessage ("unset variable %s", name);return type;}/** Run the command in $(...) and read its output.*/static int F_LOCAL CommandSubstitute (Expand *xp, char *cp){Source *s;C_Op *t;FILE *fi;jmp_buf ReturnPoint;int localpipe;if ((localpipe = OpenAPipe ()) < 0)return XBASE;/* Create a new environment */CreateNewEnvironment ();MemoryAreaLevel++;if (SetErrorPoint (ReturnPoint)){QuitCurrentEnvironment ();ReleaseMemoryArea (MemoryAreaLevel--); /* free old space */ClearExtendedLineFile ();S_close (localpipe, TRUE);return XBASE;}/* Create line buffer */e.line = GetAllocatedSpace (LINE_MAX);/* Parse the command */s = pushs (SSTRING);s->str = cp;/* Check for $(<file) */if (((t = BuildParseTree (s)) != (C_Op *)NULL) &&(t->type == TCOM) && (*t->args == NOWORD) &&(*t->vars == NOWORD) && (t->ioact != (IO_Actions **)NULL)){IO_Actions *io = *t->ioact;char *name;/* We don't need the pipe - so get rid of it */S_close (localpipe, TRUE);/* OK - terminate the created environment */QuitCurrentEnvironment ();if ((io->io_flag & IOTYPE) != IOREAD)ShellErrorMessage ("funny $() command");if ((localpipe = S_open (FALSE, name = ExpandAString (io->io_name,EXPAND_TILDE),O_RMASK)) < 0)ShellErrorMessage ("cannot open %s", name);}/* Execute the command */else{if (!ProcessCommandTree (t, localpipe))longjmp (ReturnPoint, 1);QuitCurrentEnvironment ();}/* Open the IO Stream */if ((fi = ReOpenFile (ReMapIOHandler (localpipe),sOpenReadMode)) == (FILE *)NULL)ShellErrorMessage ("cannot open $() input");/** Free old memory area*/ReleaseMemoryArea (MemoryAreaLevel--);xp->u.file = fi;return XCOM;}/** perform #pattern and %pattern substitution in ${}*/static char * F_LOCAL TrimSubstitute (SubType *st, char *pat){int mode = GM_SHORTEST;char *pos;char *tsp;char *str = GetVariableArrayAsString (st->name, st->index,TRUE);/** Switch on the match type*/switch (st->type & 0xff){case CHAR_MATCH_START | CHAR_MAGIC:/* longest match at begin */mode = GM_LONGEST;case CHAR_MATCH_START: /* shortest at begin */if (GeneralPatternMatch (str, (unsigned char *)pat, FALSE, &pos, mode))return pos;break;case CHAR_MATCH_END | CHAR_MAGIC:/* longest match at end */mode = GM_LONGEST;case CHAR_MATCH_END: /* shortest match at end */if (SuffixPatternMatch (str, pat, &pos, mode)){tsp = StringCopy (str);tsp[pos - str] = 0;return tsp;}break;}return str; /* no match, return string */}/** glob* Name derived from V6's /etc/glob, the program that expanded filenames.*/static void F_LOCAL ExpandGlobCharacters (char *Pattern, Word_B **WordList){char path [FFNAME_MAX];int oldsize = WordBlockSize (*WordList);int newsize;#if (OS_TYPE != OS_UNIX)char *NewPattern; /* Search file name */int CurrentDrive; /* Current drive */int MaxDrives; /* Max drive */int SelectedDrive; /* Selected drive */int y_drive; /* Dummies */unsigned char *DriveCharacter; /* Multi-drive flag */char SDriveString[2];char *EndPattern;/* Search all drives ? */if ((DriveCharacter = CheckForMultipleDrives (Pattern))!= (unsigned char *)NULL){CurrentDrive = GetCurrentDrive ();MaxDrives = SetCurrentDrive (CurrentDrive);SDriveString[1] = 0;EndPattern = WordScan (Pattern, 0);NewPattern = GetAllocatedSpace ((EndPattern - Pattern) + 1);/* Scan the available drives */for (SelectedDrive = 1; SelectedDrive <= MaxDrives; ++SelectedDrive){if (SetCurrentDrive (SelectedDrive) != -1){y_drive = GetCurrentDrive ();SetCurrentDrive (CurrentDrive);}elsey_drive = -1;/* Check to see if the second diskette drive is really there */if ((GetNumberofFloppyDrives () < 2) && (SelectedDrive == 2))continue;/* If the drive exists and is in our list - process it */*DriveCharacter = 0;*SDriveString = GetDriveLetter (SelectedDrive);strlwr (Pattern);if ((y_drive == SelectedDrive) &&GeneralPatternMatch (SDriveString, Pattern, TRUE, (char **)NULL,GM_ALL)){*DriveCharacter = CHAR_DRIVE;*NewPattern = *SDriveString;memcpy (NewPattern + 1, DriveCharacter,((unsigned char *)EndPattern - DriveCharacter) + 1);GlobAWord (path, path, NewPattern, WordList, FALSE);}*DriveCharacter = CHAR_DRIVE;}ReleaseMemoryCell (NewPattern);}/** No special processing for drives*/elseGlobAWord (path, path, Pattern, WordList, FALSE);#else/* UNIX has not drives. Goodie! */GlobAWord (path, path, Pattern, WordList, FALSE);#endif/** Sort or something*/if ((newsize = WordBlockSize (*WordList)) == oldsize)*WordList = AddWordToBlock (RemoveMagicMarkers ((unsigned char *)Pattern),*WordList);elseqsort (&(*WordList)->w_words[oldsize], (size_t)(newsize - oldsize),sizeof (char *), SortCompare);}/** Recursive bit*/static void F_LOCAL GlobAWord (char *ds, /* dest path */char *dp, /* dest end */char *sp, /* source path */Word_B **WordList,/* output list */bool check) /* check dest existence */{char *EndFileName; /* next source component */char EndChar;char *CFileName;char *tdp;DIR *dirp;struct dirent *d;bool IgnoreCase = TRUE;/* End of source path ? */if (sp == (char *)NULL){if (check && (!S_access (ds, F_OK)))return;*WordList = AddWordToBlock (StringCopy (ds), *WordList);return;}if ((dp > ds) && (!IsDriveCharacter (*(dp - 1))))*dp++ = CHAR_UNIX_DIRECTORY;while (IsPathCharacter (*sp))*(dp++) = *(sp++);/* Find end of current file name */if ((EndFileName = FindPathCharacter (sp)) != (char *)NULL){*(EndFileName++) = 0;EndChar = CHAR_UNIX_DIRECTORY;}#if (OS_TYPE != OS_UNIX)if ((tdp = strchr (sp, CHAR_DRIVE)) != (char *)NULL){if (EndFileName != (char *)NULL)*(--EndFileName) = CHAR_UNIX_DIRECTORY;EndFileName = tdp;*(EndFileName++) = 0;EndChar = CHAR_DRIVE;}#endif*dp = 0;/* contains no pattern? */if (strchr (sp, CHAR_MAGIC) == NULL){tdp = dp;CFileName = sp;while ((*(tdp++) = *(CFileName++)) != 0)continue;if (IsDriveCharacter (EndChar)){*(tdp - 1) = CHAR_DRIVE;*tdp = 0;}else--tdp;GlobAWord (ds, tdp, EndFileName, WordList, check);}else{/* Check for drive letter and append a . to get the current directory */if ((strlen (ds) == 2) && IsDriveCharacter (*(ds + 1))){*(ds + 2) = CHAR_PERIOD;*(ds + 3) = 0;}/* Scan the directory */if ((dirp = opendir ((*ds == 0) ? CurrentDirLiteral: ds)) != (DIR *)NULL){if ((IsHPFSFileSystem ((*ds == 0) ? CurrentDirLiteral : ds)) &&(!(ShellGlobalFlags & FLAGS_NOCASE)))IgnoreCase = FALSE;while ((d = readdir (dirp)) != (struct dirent *)NULL){CFileName = d->d_name;/** Ignore . * ..*/if ((*CFileName == CHAR_PERIOD) &&((*(CFileName + 1) == 0) ||((*(CFileName + 1) == CHAR_PERIOD) &&(*(CFileName + 2) == 0))))continue;/** Ignore . files unless match starts with a dot.*/if ((*CFileName == CHAR_PERIOD && *sp != CHAR_PERIOD) ||!GeneralPatternMatch (CFileName, (unsigned char *)sp,IgnoreCase, (char **)NULL, GM_ALL))continue;tdp = dp;while ((*tdp++ = *CFileName++) != 0)continue;--tdp;GlobAWord (ds, tdp, EndFileName, WordList,C2bool (EndFileName != NULL));}closedir (dirp);}}if (EndFileName != NULL)*(--EndFileName) = EndChar;}/** remove MAGIC from string*/static char * F_LOCAL RemoveMagicMarkers (unsigned char *Word){unsigned char *dp, *sp;for (dp = sp = Word; *sp != 0; sp++){if (*sp != CHAR_MAGIC)*dp++ = *sp;}*dp = 0;return (char *)Word;}/** tilde expansion** based on a version by Arnold Robbins** Think this needs Expanable strings!!*/static char * F_LOCAL TildeSubstitution (unsigned char *acp){unsigned c;unsigned char path[FFNAME_MAX];unsigned char *cp = acp;unsigned char *wp = path;unsigned char *dp;while (TRUE){while (TRUE){if ((c = *cp++) == 0){*wp = 0;ReleaseMemoryCell ((void *)acp);return StringCopy ((char *)path);}else if ((c == CHAR_MAGIC) && (*cp == CHAR_TILDE))break;else*wp++ = (char)c;}dp = NULL; /* no output substitution *//** ~ or ~/*/if ((cp[1] == 0) || IsPathCharacter (cp[1]) || (IsDriveCharacter (cp[1]))){dp = (unsigned char *)GetVariableAsString (HomeVariableName, FALSE);cp += 1;}else if ((cp[1] == '+') && (IsPathCharacter (cp[2]) ||IsDriveCharacter (cp[2]) || (cp[2] == 0))){dp = (unsigned char *)GetVariableAsString (PWDVariable, FALSE);cp += 2;}else if ((cp[1] == '-') && (IsPathCharacter (cp[2]) ||IsDriveCharacter (cp[2]) || (cp[2] == 0))){dp = (unsigned char *)GetVariableAsString (OldPWDVariable, FALSE);cp += 2;}/* substitute */if (dp != NULL){while (*dp != 0)*wp++ = *dp++;/* Remove double //'s on directories */if (IsPathCharacter (*(wp - 1)) && IsPathCharacter (*cp))cp++;}}}/** Sort Compare*/extern int _CDECL SortCompare (const void *a1, const void *a2){return strcmp (*((char **)a1), *((char **)a2));}/** Return the position of Prefix StopWord in the quoted string*/static char * F_LOCAL WordScan (char *QString, int StopWord){int VarSubNest = 0;while (TRUE){switch (*(QString++)){case WORD_EOS:return QString;case WORD_CHAR:case WORD_QCHAR:case WORD_QTCHAR:QString++;break;case WORD_OQUOTE:case WORD_ODQUOTE:case WORD_CQUOTE:case WORD_CDQUOTE:break;case WORD_OARRAY:VarSubNest++;break;case WORD_CARRAY:if ((StopWord == WORD_CARRAY) && (VarSubNest == 0))return QString;VarSubNest--;break;case WORD_OSUBST:VarSubNest++;while (*(QString++) != 0)continue;if (*QString != WORD_CSUBST)QString++;break;case WORD_CSUBST:if ((StopWord == WORD_CSUBST) && (VarSubNest == 0))return QString;VarSubNest--;break;case WORD_COMSUB:case WORD_OMATHS:while (*(QString++) != 0)continue;break;}}}/** Maths substitute - convert $((....)) to a number.*/static int F_LOCAL MathsSubstitute (Expand *xp, char *sp){char DecimalString[12];char *esp;esp = substitute (sp, 0);sprintf (DecimalString, "%lu", EvaluateMathsExpression (esp));xp->str = StringCopy (DecimalString);return XSUB;}/** Check for multi_drive prefix*/#if (OS_TYPE != OS_UNIX)static unsigned char * F_LOCAL CheckForMultipleDrives (unsigned char *prefix){if ((*(prefix++) != CHAR_MAGIC) || (!IS_WildCard (*prefix)))return (unsigned char *)NULL;if (*prefix != CHAR_OPEN_BRACKETS)return *(prefix + 1) == CHAR_DRIVE ? prefix + 1 : (unsigned char *)NULL;while (*prefix && (*prefix != CHAR_CLOSE_BRACKETS)){if ((*prefix == CHAR_MATCH_RANGE) && (*(prefix + 1)))++prefix;++prefix;}return (*prefix && (*(prefix + 1) == CHAR_DRIVE))? prefix + 1 : (unsigned char *)NULL;}#endif/** A command tree is to be expanded for stdin*/static bool F_LOCAL ProcessCommandTree (C_Op *outtree, int localpipe){long s_flags = flags;Break_C *S_RList = Return_List; /* Save loval links */Break_C *S_BList = Break_List;Break_C *S_SList = SShell_List;bool s_ProcessingEXECCommand = ProcessingEXECCommand;int Local_depth = Execute_stack_depth++;jmp_buf ReturnPoint;int ReturnValue;FunctionList *s_CurrentFunction = CurrentFunction;Break_C bc;/* Create the pipe to read the output from the command string */S_dup2 (localpipe, 1);FL_CLEAR (FLAG_EXIT_ON_ERROR);FL_CLEAR (FLAG_ECHO_INPUT);FL_CLEAR (FLAG_NO_EXECUTE);/* Set up new environment */ReturnValue = CreateGlobalVariableList (FLAGS_NONE);if ((ReturnValue != -1) && (!SetErrorPoint (ReturnPoint))){Return_List = (Break_C *)NULL;Break_List = (Break_C *)NULL;/* Clear execute flags. */ProcessingEXECCommand = TRUE;CurrentFunction = (FunctionList *)NULL;/* Parse the line and execute it */if (setjmp (bc.CurrentReturnPoint) == 0){bc.NextExitLevel = SShell_List;SShell_List = &bc;ReturnValue = ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);}/* Parse error */elseReturnValue = -1;/* Clean up any files around we nolonger need */ClearExtendedLineFile ();}elseReturnValue = -1;/* Restore environment */RestoreEnvironment (ReturnValue, Local_depth);/* Free old space */FreeAllHereDocuments (MemoryAreaLevel);/* Ok - completed processing - restore environment and read the pipe */ProcessingEXECCommand = s_ProcessingEXECCommand;flags = s_flags;Return_List = S_RList;Break_List = S_BList;SShell_List = S_SList;CurrentFunction = s_CurrentFunction;/* Move pipe to start so we can read it */lseek (localpipe, 0L, SEEK_SET);return C2bool (ReturnValue != -1);}/* (pc@hillside.co.uk)* I have decided to `fudge' alternations by picking up the compiled command* tree and working with it recursively to generate the set of arguments.* This has the advantage of making a single discrete change to the code** This routine calls itself recursively** a) Scan forward looking for { building the output string if none found* then call expand - and exit* b) When { found, scan forward finding the end }* c) Add first alternate to output string* d) Scan for the end of the string copying into output* e) Call routine with new string** Major complication is quoting*/static void F_LOCAL AlternationExpand (char *cp, /* input word */Word_B **WordList,/* output words */int ExpandMode)/* DO* flags */{char *srcp = cp;char *left; /* destination string of left hand side */char *leftend; /* end of left hand side */char *alt; /* start of alterate section */char *altend; /* end of alternate section */char *ap; /* working pointer */char *right; /* right hand side */char *rp; /* used to copy right-hand side */size_t maxlen; /* max string length */maxlen = WordScan (cp, 0) - cp;left = GetAllocatedSpace (maxlen);leftend = left;if (AlternationScan (&srcp, &leftend, CHAR_OPEN_BRACES, 0) == 0){ExpandAWord (cp, WordList, ExpandMode & EXPAND_NOALTS);ReleaseMemoryCell (left);return;}/* We have a alternation section */alt = GetAllocatedSpace (maxlen);altend = alt;srcp += 2;if (AlternationScan (&srcp, &altend, CHAR_CLOSE_BRACES, 1) == 0){ReleaseMemoryCell (left);ReleaseMemoryCell (alt);PrintErrorMessage ("Mis-matched {}.");}*(altend++) = WORD_CHAR;*(altend++) = ',';*altend = WORD_EOS;/* finally we may have a right-hand side */right = srcp + 2;/* glue the bits together making a new string */for (srcp = alt; *srcp != WORD_EOS;){ap = leftend;if (AlternationScan (&srcp, &ap, ',', -1) == 0){ReleaseMemoryCell (left);ReleaseMemoryCell (alt);PrintErrorMessage ("Missing comma.");}srcp += 2;rp = right;AlternationScan (&rp, &ap, WORD_EOS, 0);AlternationExpand (left, WordList, ExpandMode);}ReleaseMemoryCell (left);ReleaseMemoryCell (alt);return;}/** Scan the tree*/static int F_LOCAL AlternationScan (char **cpp,/* source pointer */char **dpp,/* destination pointer */char endc, /* last character look for */int bal){char *cp, *dp;bool QuoteStatus = FALSE;int balance = 0;bool UseBalance = FALSE;int VarSubNest = 0;if (bal){UseBalance = TRUE;balance = (bal < 1) ? 0 : 1;}cp = *cpp;dp = *dpp;while (*cp != WORD_EOS){switch (*cp){case WORD_CHAR:if (QuoteStatus){if (cp[1] == CHAR_CLOSE_BRACKETS)QuoteStatus = FALSE;}else if (!QuoteStatus){if (cp[1] == CHAR_OPEN_BRACKETS)QuoteStatus = TRUE;else{if (UseBalance){if (cp[1] == CHAR_OPEN_BRACES)balance++;if (cp[1] == CHAR_CLOSE_BRACES)balance--;}if ((cp[1] == endc) && (balance == 0)){*dp = WORD_EOS;*dpp = dp;*cpp = cp;return 1;}}}case WORD_QCHAR:case WORD_QTCHAR:*(dp++) = *(cp++);*(dp++) = *(cp++);break;case WORD_OQUOTE:case WORD_ODQUOTE:QuoteStatus = TRUE;*(dp++) = *(cp++);break;case WORD_CQUOTE:case WORD_CDQUOTE:QuoteStatus = FALSE;*(dp++) = *(cp++);break;case WORD_OARRAY:VarSubNest++;*(dp++) = *(cp++);break;case WORD_OSUBST:VarSubNest++;while ((*(dp++) = *(cp++)))continue;if (*cp != WORD_CSUBST)*(dp++) = *(cp++);break;case WORD_CSUBST:case WORD_CARRAY:*(dp++) = *(cp++);VarSubNest--;break;case WORD_COMSUB:case WORD_OMATHS:while ((*(dp++) = *(cp++)))continue;break;}}*dp = WORD_EOS;*cpp = cp;*dpp = dp;return 0;}/** Handle Array Value between WORD_OARRAY & WORD_CARRAY* Return the array index (-1 == [*]).* (-2 == [@]).*/static int F_LOCAL HandleArrayValue (char *name,char **InputString,int ExpandMode){/* Start after the Open Array */char *End = WordScan ((*InputString) + 1, WORD_CARRAY);size_t Length = (End - *InputString) - 1;char *ExpWord;Word_B *WordList = (Word_B *)NULL;long value;/* Build a copy of the substring to expand */ExpWord = memcpy (GetAllocatedSpace (Length), *InputString + 1, Length);ExpWord[Length - 1] = WORD_EOS;ExpandMode &= ~(EXPAND_GLOBBING | EXPAND_CONVERT | EXPAND_NOALTS);ExpandAWord (ExpWord, &WordList, ExpandMode);/* Check for valid value */if (WordBlockSize (WordList) != 1)ShellErrorMessage (LIT_BadArray, "too many words");/** There are a couple of special cases:** ${#name[*]}* ${name[*]}* ${name[@]}*/if (!strcmp (WordList->w_words[0], "*"))value = -1L;else if (!strcmp (WordList->w_words[0], "@") &&((*name != '#') || (*(name + 1) == 0)))value = -2L;/** Otherwise, get the array value*/else{value = EvaluateMathsExpression (WordList->w_words[0]);if ((value < 0) || (value > INT_MAX))ShellErrorMessage (LIT_ArrayRange, name);}*InputString = End;return (int)value;}/** Check for Unset Variable*/static void F_LOCAL CheckForUnset (char *name, int Index){if ((FL_TEST (FLAG_UNSET_ERROR)) &&(GetVariableArrayAsString (name, Index, FALSE) == null))ShellErrorMessage ("%s: unset variable", name);}/** TWALK - Build list of the values of an Environment Variable*/static void BuildVariableEntryList (const void *key, VISIT visit, int level){VariableList *vp = (*(VariableList **)key);if (((visit == postorder) || (visit == leaf)) &&(strcmp (GVAV_Name, vp->name) == 0))GVAV_WordList = AddWordToBlock (GetVariableArrayAsString (vp->name,vp->index,TRUE),GVAV_WordList);}/** Return the number of floppy disks*/#if (OS_TYPE == OS_OS2)static int F_LOCAL GetNumberofFloppyDrives (void){BYTE nflop = 1;# if (OS_SIZE == OS_16)DosDevConfig (&nflop, DEVINFO_FLOPPY, 0);# elseDosDevConfig (&nflop, DEVINFO_FLOPPY);# endifreturn nflop;}#endif/* DOS Version */#if (OS_TYPE == OS_DOS)static int F_LOCAL GetNumberofFloppyDrives (void){# if defined (__TURBOC__)return ((biosequip () & 0x00c0) >> 6) + 1;# elif defined (__EMX__)union REGS r;SystemInterrupt (0x11, &r, &r);return ((r.x.REG_AX & 0x00c0) >> 6) + 1;# elsereturn ((_bios_equiplist () & 0x00c0) >> 6) + 1;# endif}#endif/* NT Version */#if (OS_TYPE == OS_NT)static int F_LOCAL GetNumberofFloppyDrives (void){char szNewDrive[4];DWORD dwLogicalDrives = GetLogicalDrives();int LastTest = 0;int i;strcpy (szNewDrive, "x:\\");/* Look at each drive until we find a non-floppy which exists */for (i = 0; i < 25; i++){if (dwLogicalDrives & (1L << i)){szNewDrive[0] = i + 'A';if (GetDriveType (szNewDrive) != DRIVE_REMOVABLE)break;LastTest = i + 1;}}return LastTest;}#endif