/* * 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 #include #include #include /* String library functions */ #include #include #include #include #include #include #include #include #include #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; } else type = 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; } else sp = 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); else ShellErrorMessage ("%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; } } else QuoteStatus &= ~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); else c = 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 $(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); } else y_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 */ else GlobAWord (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); else qsort (&(*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 */ else ReturnValue = -1; /* Clean up any files around we nolonger need */ ClearExtendedLineFile (); } else ReturnValue = -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); # else DosDevConfig (&nflop, DEVINFO_FLOPPY); # endif return 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; # else return ((_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