/*++ Copyright (c) 1988-1999 Microsoft Corporation Module Name: cmd.c Abstract: Top-level driver for CMD --*/ #include "cmd.h" // // Used in rebuilding command lines for display // #define NSPC 0 // Don't use space #define YSPC 1 // Do use space extern CPINFO CurrentCPInfo; extern UINT CurrentCP; extern ULONG LastMsgNo; // // Jump buffers used to return to main loop after some error condition // jmp_buf MainEnv; // SigHand() uses to return to main jmp_buf CmdJBuf1; // Both of these buffers are used by jmp_buf CmdJBuf2; // various parts of Command for error // // rioCur points to a linked list of rio structures dynamically // allocated when redirection is performed. Note that memory is automatically // freed when Dispatch completed work. rioCur points to last entry. // struct rio *rioCur = NULL; // // Retrun code for last external program // int LastRetCode; // // Constants used in parsing // extern TCHAR PathChar; extern TCHAR SwitChar; extern TCHAR Delimiters[]; extern TCHAR Delim2[]; extern TCHAR Delim4[]; extern TCHAR Delim5[]; extern TCHAR ForStr[]; extern TCHAR ForLoopStr[]; extern TCHAR ForDirTooStr[]; extern TCHAR ForParseStr[]; extern TCHAR ForRecurseStr[]; // // Current Drive:Directory. Set in ChDir // It it is change temp. SaveDir used used to old original // extern TCHAR CurDrvDir[]; // // Name of NULL device. Used to output to nothing // extern TCHAR DevNul[]; // // Number of elements in Data stack // extern ULONG DCount; // // Environment string to locate command shell. // extern TCHAR ComSpecStr[]; // // DOS error code // extern unsigned DosErr; // // Alternative path (DDPATH) to search // extern TCHAR AppendStr[]; // // flag if control-c was seen // extern BOOL CtrlCSeen; extern BOOLEAN fPrintCtrlC; extern PTCHAR pszTitleCur; extern BOOLEAN fTitleChanged; // // Prototypes // PTCHAR GetEnvVar(); PTCHAR EatWS(); int UnParse(struct node *, PTCHAR); int UnBuild(struct node *, PTCHAR); void UnDuRd(struct node *, PTCHAR); void SPutC(PTCHAR, PTCHAR,int ); PTCHAR argstr1(); int DelayedEnvVarSub(struct cmdnode *, struct savtype *, BOOLEAN); int DESubWork(BOOLEAN, TCHAR **, TCHAR **); VOID GetCmdPolicy(INT * iDisabled); #define CMD_POLICY_NORMAL 0 #define CMD_POLICY_DISABLE_SCRIPTS 1 #define CMD_POLICY_ALLOW_SCRIPTS 2 // // Used to set and reset ctlcseen flag // VOID SetCtrlC(); VOID ResetCtrlC(); // // to monitor stack usage // extern BOOLEAN flChkStack; extern PVOID FixedPtrOnStack; typedef struct { PVOID Base; PVOID GuardPage; PVOID Bottom; PVOID ApprxSP; } STACK_USE; extern STACK_USE GlStackUsage; extern int ChkStack (PVOID pFixed, STACK_USE *pStackUse); int __cdecl main() /*++ Routine Description: Main entry point for command interpreter Arguments: Command line: /P - Permanent Command. Set permanent CMD flag. /C - Single command. Build a command line out of the rest of the args and pass it back to Init. /K - Same as /C but also set SingleBatchInvocation flag. /Q - No echo Return Value: Return: 0 - If success 1 - Parsing Error 0xFF - Could not init n - Return code from command --*/ { CHAR VarOnStack; struct node *pnodeCmdTree; // // When in multi-cmd mode tells parser where to get input from. // int InputType; // // Pointer to initial command lines // PTCHAR InitialCmds[ 3 ]; int i, iDisabled; BOOL bInit; // // flag used when a setjmp returns while processing /K // error and move to next line. // unsigned fIgnore = FALSE; unsigned ReturnCode, rc; // // Since we operate in a multilingual environment, we must set up the // system/user/thread locales correctly BEFORE ever issuing a message // #if !defined( WIN95_CMD ) CmdSetThreadUILanguage(0); #endif __try { // // Check policy to see if cmd is disabled // GetCmdPolicy (&iDisabled); // // flChkStack is turned ON initially here and it stays ON while // I believe the information returned by ChkStack() is correct. // // It is turned OFF the first time I don't believe that info and // therefore I don't want to make any decisions changing the CMD's // behavior. // // It will stay OFF until CMD terminates so we will never check // stack usage again. // // I implemented one method to prevent CMD.EXE from the stack overflow: // Have count and limit of recursion in batch file processing and check // stack every time we exceed the limit of recursion until we reach 90% // of stack usage. // If (stack usage >= 90% of 1 MByte) then terminate batch file // unconditionally and handle such termination properly (freeing memory // and stack and saving CMD.EXE) // // It is also possible to implement SEH but then we won't know about // CMD problems. // flChkStack = 1; FixedPtrOnStack = (VOID *) &VarOnStack; // to be used in ChkStack() if ( ChkStack (FixedPtrOnStack, &GlStackUsage) == FAILURE ) { flChkStack = 0; } // // Initialize the DBCS lead byte table based on the current locale. // InitializeDbcsLeadCharTable( ); // // Set base APIs to operate in OEM mode // #ifndef UNICODE SetFileApisToOEM(); #endif /* Unicode */ // // Init returns TRUE if there are any commands to run before // entering the main loop (e.g. /C or /K and/or AutoRun from registry) // memset( &InitialCmds, 0, sizeof( InitialCmds ) ); ReturnCode = 0; bInit = Init( InitialCmds ); if (CMD_POLICY_DISABLE_SCRIPTS == iDisabled) { PutStdOut( MSG_DISABLED_BY_POLICY, NOARGS ) ; ePause(0); CMDexit( 0xFF ); } if ( bInit ) { if (setjmp(MainEnv)) { // // If processing /K and setjmp'd out of init. then ignore // fIgnore = TRUE; if ( SingleCommandInvocation ) ReturnCode = 0xFF; } if ( !fIgnore ) { // // Loop over any initial commands read from registry of from /C or /K // for (i=0; i<3; i++) if (InitialCmds[i] != NULL) { DEBUG((MNGRP, MNLVL, "MAIN: Single command mode on `%ws'", InitialCmds[i])); if ((pnodeCmdTree = Parser(READSTRING, (INT_PTR)InitialCmds[i], DCount)) == (struct node *) PARSERROR) CMDexit(MAINERROR); if (pnodeCmdTree == (struct node *) EOF) CMDexit(SUCCESS); DEBUG((MNGRP, MNLVL, "MAIN: Single command parsed successfully.")); rc = Dispatch(RIO_MAIN, pnodeCmdTree); if (rc != 0) ReturnCode = rc; } // // Make sure we have the correct console modes. // ResetConsoleMode(); // // Get current CodePage Info. We need this to decide whether // or not to use half-width characters. // GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo); // // Maybe console output code page was changed by CHCP or MODE, // so need to reset LanguageID to correspond to code page. // #if !defined( WIN95_CMD ) CmdSetThreadUILanguage(0); #endif } // // All done if /C specified. // if ( SingleCommandInvocation ) CMDexit( ReturnCode ); SingleBatchInvocation = FALSE; // Allow ASync exec of GUI apps now } // // Through init and single command processing. reset our Setjmp location // to here for error processing. // if (ReturnCode = setjmp(MainEnv)) { // // fix later to have a generalized abort // for now assume this is a real abort from // eof on stdin redirected. if (ReturnCode == EXIT_EOF) { CMDexit(SUCCESS); } } // // Exit now if the interactive command prompt // if (CMD_POLICY_ALLOW_SCRIPTS == iDisabled) { PutStdOut( MSG_DISABLED_BY_POLICY, NOARGS ) ; ePause(0); CMDexit( 0xFF ); } // // Check if our I/O has been redirected. This is used to tell // where we should read input from. // InputType = (FileIsDevice(STDIN)) ? READSTDIN : READFILE; DEBUG((MNGRP,MNLVL,"MAIN: Multi command mode, InputType = %d", InputType)); // // If we are reading from a file, make sure the input mode is binary. // CRLF translations mess up the lexer because FillBuf() wants to // seek around in the file. // if(InputType == READFILE) { _setmode(STDIN,_O_BINARY); } // // Loop till out of input or error parsing. // while (TRUE) { DEBUG((MNGRP, MNLVL, "MAIN: Calling Parser.")); GotoFlag = FALSE; ResetCtrlC(); if ((pnodeCmdTree = Parser(InputType, STDIN, FS_FREEALL)) == (struct node *) PARSERROR) { DEBUG((MNGRP, MNLVL, "MAIN: Parse failed.")); } else if (pnodeCmdTree == (struct node *) EOF) CMDexit(SUCCESS); else { ResetCtrlC(); DEBUG((MNGRP, MNLVL, "MAIN: Parsed OK, DISPATCHing.")); // // Get current CodePage Info. We need this to decide whether // or not to use half-width characters. // GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo); // // Maybe console output code page was changed by console property sheet // so need to reset LanguageID to correspond to code page. // #if !defined( WIN95_CMD ) CmdSetThreadUILanguage(0); #endif Dispatch(RIO_MAIN, pnodeCmdTree); // // Make sure we have the correct console modes. // ResetConsoleMode(); // // Get current CodePage Info. We need this to decide whether // or not to use half-width characters. // GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo); // // Maybe console output code page was changed by CHCP or MODE, // so need to reset LanguageID to correspond to code page. // #if !defined( WIN95_CMD ) CmdSetThreadUILanguage(0); #endif DEBUG((MNGRP, MNLVL, "MAIN: Dispatch returned.")); } } CMDexit(SUCCESS); ReturnCode = SUCCESS; } __except(EXCEPTION_EXECUTE_HANDLER) { ReturnCode = -1; } return ReturnCode; } FARPROC WINAPI CmdDelayHook( UINT dliNotify, PDelayLoadInfo pdli ) { if (ReportDelayLoadErrors) { if (dliNotify == dliFailLoadLib) { PutStdErr( MSG_CANNOT_LOAD_LIB, ONEARG, pdli->szDll ); PutStdErr( pdli->dwLastError, NOARGS ); } else if (dliNotify == dliFailGetProc) { if (pdli->dlp.fImportByName) { PutStdErr( MSG_CANNOT_FIND_FUNC_NAME, TWOARGS, pdli->szDll, pdli->dlp.szProcName ); } else { PutStdErr( MSG_CANNOT_FIND_FUNC_ORDINAL, TWOARGS, pdli->szDll, pdli->dlp.dwOrdinal ); } PutStdErr( pdli->dwLastError, NOARGS ); } } return 0; } // // Override the standard definition of __pfnDliNotifyHook that's part of // DELAYHLP.LIB // PfnDliHook __pfnDliFailureHook = CmdDelayHook; int Dispatch( IN int RioType, IN struct node *pnodeCmdTree ) /*++ Routine Description: Set up any I/O redirection for the current node. Find out who is supposed to process this node and call the routine to do it. Reset stdin/stdout if necessary. Dispatch() must now be called with all args present since the RioType is needed to properly identify the redirection list element. Dispatch() determines the need for redirection by examination of the RioType and command node and calls SetRedir only if necessary. Also, in like manner, Dispatch() only calls ResetRedir if redirection was actually performed. The conditional that determines whether newline will be issued following commands (prior to prompt), had to be altered so that the execution of piped commands did not each issue a newline. The pre- prompt newline for pipe series is now issued by ePipe(). Arguments: RioType - tells SetRedir the routine responsible for redirection pnodeCmdTree - the root of the parse tree to be executed Return Value: The return code from the command/function that was executed or FAILURE if redirection error. --*/ { int comretcode; // Retcode of the cmnd executed struct cmdnode *pcmdnode; // pointer to current command node PTCHAR pbCmdBuf; // Buffer used in building command struct savtype save; DEBUG((MNGRP, DPLVL, "DISP: pnodeCmdTree = 0x%04x, RioType = %d", pnodeCmdTree, RioType)); // // If we don't have a parse tree or // we have a goto label or // we have a comment line // then don't execute anything and return. // if (!pnodeCmdTree || GotoFlag || pnodeCmdTree->type == REMTYP) { return(SUCCESS); } comretcode = DISPERROR; DEBUG((MNGRP, DPLVL, "DISP: type = 0x%02x", pnodeCmdTree->type)); // // Copy node ptr pnodeCmdTree to new node ptr pcmdnode // If command is to be detached or pipelined (but not a pipe) // If command is Batch file or Internal or Multi-statement command // "Unparse" tree into string approximating original commandline // Build new command node (pcmdnode) to spawn a child Command.com // Make the string ("/C" prepended) the argument of the new node // Perform redirection on node c // If node pcmdnode is to be detatched // Exec async/discard // else // Exec async/keep but don't wait for retcode (pipelined) // else // If this is a CMDTYP, PARTYP or SILTYP node and there is explicit redirection // // Perform redirection on this node // If operator node or a special type (FOR, IF, DET or REM) // Call routine identified by GetFuncPtr() to execute it // Else call FindFixAndRun() to execute the CMDTYP node. // If redirection was performed // Reset redirection // pcmdnode = (struct cmdnode *)pnodeCmdTree; if (fDelayedExpansion) { memset(&save, 0, sizeof(save)); if (DelayedEnvVarSub(pcmdnode, &save, FALSE)) { goto dispatchExit; } } // // If we are called from ePipe and PIPE command then we need // to rebuild the command in ascii form (UnParse) and fork // off another cmd.exe to execute it. // if ((RioType == RIO_PIPE && pcmdnode->type != PIPTYP)) { // // pbCmdbuf is used as tmp in FindCmd and SFE // if (!(pbCmdBuf = mkstr( MAXTOKLEN * sizeof( TCHAR )))) { goto dispatchExit; } // // If current node to execute is not a command or // could not find it as an internal command or // it was found as a batch file then // Do the unparse // if (pcmdnode->type != CMDTYP || FindCmd(CMDHIGH, pcmdnode->cmdline, pbCmdBuf) != -1 || SearchForExecutable(pcmdnode, pbCmdBuf) == SFE_ISBAT) { DEBUG((MNGRP, DPLVL, "DISP: Now UnParsing")); // // if pcmdnode an intrnl cmd then pbCmdBuf holds it's switches // if pcmdnode was a batch file then pbCmdBuf holds location // if (UnParse((struct node *)pcmdnode, pbCmdBuf)) { goto dispatchExit; } DEBUG((MNGRP, DPLVL, "DISP: UnParsed cmd = %ws", pbCmdBuf)); // // Build a command node with unparsed command // Will be exec'd later after redirection is applied // pcmdnode = (struct cmdnode *)mknode(); if (pcmdnode == NULL) { goto dispatchExit; } pcmdnode->type = CMDTYP; pcmdnode->cmdline = GetEnvVar(ComSpecStr); pcmdnode->argptr = pbCmdBuf; } // // Setup I/O redirection // if (SetRedir((struct node *)pcmdnode, RioType)) { goto dispatchExit; } DEBUG((MNGRP, DPLVL, "DISP:Calling ECWork on piped cmd")); pbCmdBuf[1] = SwitChar; pbCmdBuf[2] = TEXT('S'); comretcode = ECWork(pcmdnode, AI_KEEP, CW_W_NO); DEBUG((MNGRP, DPLVL, "DISP: ECWork returned %d", comretcode)); } else { // // We are here if command was not PIPE // // If it was a command node or a paren or a silent operator and // we have redirection then set redirection. // if ((pnodeCmdTree->type == CMDTYP || pnodeCmdTree->type == PARTYP || pnodeCmdTree->type == SILTYP || pnodeCmdTree->type == HELPTYP) && pnodeCmdTree->rio) { // // Set redirection on node. // if (SetRedir(pnodeCmdTree, RioType)) { goto dispatchExit; } } // // If it is an internal command then find it and execute // otherwise locate file load and execute // if (pnodeCmdTree->type != CMDTYP) { comretcode = (*GetFuncPtr(pnodeCmdTree->type))((struct cmdnode *)pnodeCmdTree); } else { comretcode = FindFixAndRun((struct cmdnode *)pnodeCmdTree); } } // else // // Reset and redirection that was previously setup // pcmdnode is always current node. // if ((rioCur) && (rioCur->rnod == (struct node *)pcmdnode)) { ResetRedir(); } dispatchExit: if (fDelayedExpansion) { DelayedEnvVarSub(pcmdnode, &save, TRUE); } DEBUG((MNGRP, DPLVL, "DISP: returning %d", comretcode)); return(comretcode); } int SetRedir( IN struct node *pnodeCmdTree, IN int RioType ) /*++ Routine Description: Perform the redirection required by the current node Only individual commands and parenthesised statement groups can have explicit I/O redirection. All nodes, however, can tolerate redirection of an implicit nature. Arguments: pNode - pointer node containing redirection information RioType - indicator of source of redirection request Return Value: SUCCESS if the redirection was successfully set up. FAILURE if the redirection was NOT successfully set up. --*/ { struct rio *prio; int i; CRTHANDLE OpenStatus; BOOLEAN fInputRedirected = FALSE; // // Temps. Used to hold all of the relocation information for a // command. // struct relem *prelemT; struct relem *prelemT2; TCHAR rgchFileName[MAX_PATH]; const TCHAR *p; DEBUG((MNGRP, RIOLVL, "SETRD:RioType = %d.",RioType)); prelemT = pnodeCmdTree->rio; // // Loop through redirections removing ":" from device names // and determining if input has been redirected // while (prelemT) { mystrcpy(prelemT->fname, StripQuotes(prelemT->fname) ); // // skip any redirection that already has been done // if (prelemT->svhndl) { prelemT = prelemT->nxt; continue; } // // check for and remove any COLON that might be in a device name // if ((i = mystrlen(prelemT->fname)-1) > 1 && *(prelemT->fname+i) == COLON) *(prelemT->fname+i) = NULLC; // // If input redirection specified then set flag for later use // if (prelemT->rdhndl == STDIN) { fInputRedirected = TRUE; } prelemT = prelemT->nxt; } DEBUG((MNGRP, RIOLVL, "SETRD: fInputRedirected = %d",fInputRedirected)); // // Allocate, activate and initialize the rio list element. // We must skip this if called from AddRedir (test for RIO_REPROCESS) // if (RioType != RIO_REPROCESS) { if (!(prio=(struct rio *)mkstr(sizeof(struct rio)))) { PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS); return ( FAILURE ); } prio->back = rioCur; rioCur = prio; prio->rnod = pnodeCmdTree; prio->type = RioType; DEBUG((MNGRP, RIOLVL, "SETRD: rio element built.")); } else { prio = rioCur; } // // Once the list has been set up for standard and special cases // the actual handle redirection is performed. // // loop thru the list performing all redirection and error recovery. // prelemT = pnodeCmdTree->rio; while (prelemT) { // // Skip any already done. // if (prelemT->svhndl) { prelemT = prelemT->nxt; continue; } DEBUG((MNGRP, RIOLVL, "SETRD: Old osf handle = %x", CRTTONT(prelemT->rdhndl))); // // Make sure read handle is open and valid before saving it. // if (CRTTONT(prelemT->rdhndl) == INVALID_HANDLE_VALUE) { prelemT->svhndl = BADHANDLE; } else if (FileIsDevice(prelemT->rdhndl) || FileIsPipe(prelemT->rdhndl) || SetFilePointer(CRTTONT(prelemT->rdhndl), 0L, NULL, FILE_CURRENT) != -1) { DEBUG((MNGRP, RIOLVL, "SETRD: duping %d", prelemT->rdhndl)); if ((prelemT->svhndl = Cdup(prelemT->rdhndl)) == BADHANDLE) { DEBUG((MNGRP, RIOLVL, "SETRD: Cdup error=%d, errno=%d", GetLastError(), errno)); PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (unsigned long)prelemT->rdhndl)); prelemT->svhndl = 0; ResetRedir(); return(FAILURE); } DEBUG((MNGRP, RIOLVL, "SETRD: closing %d", prelemT->rdhndl)); Cclose(prelemT->rdhndl); DEBUG((MNGRP,RIOLVL,"SETRD: save handle = %d", prelemT->svhndl)); DEBUG((MNGRP,RIOLVL,"SETRD: --->osf handle = %x", CRTTONT(prelemT->svhndl))); } else { DEBUG((MNGRP, RIOLVL, "SETRD: FileIsOpen ret'd FALSE")); PutStdErr(MSG_RDR_HNDL_OPEN, ONEARG, argstr1(TEXT("%d"), (unsigned long)prelemT->rdhndl)); prelemT->svhndl = 0; ResetRedir(); return(FAILURE); } // // Is file name the command seperator character '&' // if (*prelemT->fname == CSOP) { DEBUG((MNGRP,RIOLVL,"SETRD: Handle substitution, %ws %d", prelemT->fname, prelemT->rdhndl)); *(prelemT->fname+2) = NULLC; if (Cdup2(*(prelemT->fname+1) - TEXT('0'), prelemT->rdhndl) == BADHANDLE) { DEBUG((MNGRP, RIOLVL, "SETRD: Cdup2 error=%d, errno=%d", GetLastError(), errno)); ResetRedir(); PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (ULONG)prelemT->rdhndl)); return(FAILURE); } DEBUG((MNGRP,RIOLVL,"SETRD: %c forced to %d",*(prelemT->fname+1), (ULONG)prelemT->rdhndl)); } else { // // redirecting input from a file. Check to see if file // exists and can be opened for input. // if (prelemT->rdop == INOP) { DEBUG((MNGRP,RIOLVL,"SETRD: File in = %ws",prelemT->fname)); // // Try to open file localy first // if ((OpenStatus = Copen(prelemT->fname, O_RDONLY|O_BINARY)) == BADHANDLE) { // // Now try the DPATH (data path) // p = MyGetEnvVarPtr(AppendStr); if ( p != NULL && SearchPath( p, prelemT->fname, NULL, MAX_PATH, rgchFileName, NULL ) != 0 ) { OpenStatus = Copen(rgchFileName, O_RDONLY|O_BINARY); } } } else { // // We are not redirecting input so must be output // DEBUG((MNGRP,RIOLVL,"SETRD: File out = %ws",prelemT->fname)); // // Make sure sure we can open the file for output // OpenStatus = Copen(prelemT->fname, prelemT->flag ? OP_APPEN : OP_TRUNC); } // // If the handle to be redirected was not the lowest numbered, // unopened handle when open was called, the current handle must // be forced to it, the handle returned by open must be closed. // if (OpenStatus != BADHANDLE && OpenStatus != prelemT->rdhndl) { DEBUG((MNGRP,RIOLVL,"SETRD: Handles don't match...")); DEBUG((MNGRP,RIOLVL,"SETRD: ...forcing %d to %d", i, (ULONG)prelemT->rdhndl)); if (Cdup2(OpenStatus, prelemT->rdhndl) == BADHANDLE) { DEBUG((MNGRP, RIOLVL, "SETRD: Cdup2 error=%d, errno=%d", GetLastError(), errno)); Cclose(OpenStatus); ResetRedir(); PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (ULONG)prelemT->rdhndl)); return(FAILURE); } else { Cclose(OpenStatus); OpenStatus = prelemT->rdhndl; } } // // Copen error processing must be delayed to here to allow the // above Cdup2 to occur if necessary. Otherwise, the call to // ResetRedir in the error handler would attempt to close the // wrong handle and leave a bogus handle open. // if (OpenStatus == BADHANDLE) { DEBUG((MNGRP,RIOLVL,"SETRD: Bad Open, DosErr = %d",DosErr)); ResetRedir(); PrtErr(DosErr); return(FAILURE); } DEBUG((MNGRP, RIOLVL, "SETRD: new handle = %d", OpenStatus)); DEBUG((MNGRP,RIOLVL,"SETRD: --->osf handle = %x", CRTTONT(OpenStatus))); // // Keep highest numbered handle // prio->stdio = OpenStatus; } // else prelemT = prelemT->nxt; } // while return(SUCCESS); } AddRedir( IN struct cmdnode *pcmdnodeOriginal, IN struct cmdnode *pcmdnodeNew ) /*++ Routine Description: Add redirection from a new node to an existing one. Walk the redirection list of the old node for each element in the new. Duplicates are removed from the old and replaced by the new, while unique new ones are added to the end. When the two lists are merged, reprocess the redirection. Arguments: pcmdnodeOriginal - original node to be added to pcmdnodeNew - new node to merge. Return Value: SUCCESS if the redirection was successfully merged. FAILURE otherwise. --*/ { struct relem *prelemOriginal; struct relem *prelemNew; struct relem *prelemEnd; // Ptr to end of original list // // Flag to set Stack Minimum // BOOLEAN fSetStackMin = FALSE; PTCHAR oldname; /* Sanity check */ struct rio *rn; /* Possible rio element */ // // Won't be here unless pcmdnodeNew-reio exists // prelemNew = pcmdnodeNew->rio; // If there was no redirection associated with the original node, we must // also create a rio element so that the redirection can be reset at // command completion or receipt of signal. We have to create it here // rather than in SetRedir in order to include it on the data stack when // we set a new level. if (!(prelemEnd = prelemOriginal = pcmdnodeOriginal->rio)) { DEBUG((MNGRP, RIOLVL, "ADDRD: No old redirection.")); // // New list becomes original // pcmdnodeOriginal->rio = prelemNew; if (!(rn=(struct rio *)mkstr(sizeof(struct rio)))) { PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS); return(FAILURE); } // // Create dummy redirection node. // rn->back = rioCur; rioCur = rn; rn->rnod = (struct node *)pcmdnodeOriginal; rn->type = RIO_BATLOOP; DEBUG((MNGRP, RIOLVL, "ADDRD: rio element built.")); fSetStackMin = TRUE; /* Must save current datacount */ prelemNew = NULL; /* Skip the while loops */ } else { // // Find the end of the orignal list // while (prelemEnd->nxt) { prelemEnd = prelemEnd->nxt; } } // // If prelemNew is non-null, we've two lists which we integrate by // eliminating any duplicate entries and adding any unique entries in // the new list to the end of the original. Note that if unique entries // exist, we must save the current data count to avoid losing their // malloc'd data when we go on to SetBat(). // // // For each new redirection, look at the original // while (prelemNew) { while(prelemOriginal) { // // Do we have a duplicate // if (prelemNew->rdhndl != prelemOriginal->rdhndl) { prelemOriginal = prelemOriginal->nxt; continue; } else { if (prelemOriginal->svhndl && (prelemOriginal->svhndl != BADHANDLE)) { // // put an assert here // Cdup2(prelemOriginal->svhndl, prelemOriginal->rdhndl); Cclose(prelemOriginal->svhndl); } else { if (prelemOriginal->svhndl == BADHANDLE) { Cclose(prelemOriginal->rdhndl); } } prelemOriginal->svhndl = 0; /* ...and replace it */ prelemOriginal->flag = prelemNew->flag; prelemOriginal->rdop = prelemNew->rdop; oldname = prelemOriginal->fname; prelemOriginal->fname = resize( prelemOriginal->fname, (mystrlen( prelemNew->fname ) + 1) * sizeof( TCHAR )); mystrcpy(prelemOriginal->fname, prelemNew->fname); if (prelemOriginal->fname != oldname) { fSetStackMin = TRUE; } pcmdnodeNew->rio = prelemNew->nxt; break; } } // // If no old entry remove from new and add to original // update the end pointer, zero next pointer and preserve datacount // if (prelemNew == pcmdnodeNew->rio) { pcmdnodeNew->rio = prelemNew->nxt; prelemEnd->nxt = prelemNew; prelemEnd = prelemEnd->nxt; prelemEnd->nxt = NULL; fSetStackMin = TRUE; } prelemNew = pcmdnodeNew->rio; prelemOriginal = pcmdnodeOriginal->rio; } // // All duplicates are eliminated. Now save the data count and call // SetRedir to reprocess the redirection list for any unimplimented // redirection (io->svhndl == 0). // if (fSetStackMin) { if (CurrentBatchFile->stacksize < (CurrentBatchFile->stackmin = DCount)) { CurrentBatchFile->stacksize = DCount; } } return(SetRedir((struct node *)pcmdnodeOriginal, RIO_REPROCESS)); } void ResetRedir() /*++ Routine Description: Reset the redirection identified by the last rio list element as pointed to by rioCur. When finished, remove the rio element from the list. Arguments: Return Value: --*/ { struct rio *prio = rioCur; struct relem *prelemT; CRTHANDLE handleT; DEBUG((MNGRP, RIOLVL, "RESETR: Entered.")); prelemT = prio->rnod->rio; while (prelemT) { if (prelemT->svhndl && (prelemT->svhndl != BADHANDLE)) { DEBUG((MNGRP,RIOLVL,"RESETR: Resetting %d",(ULONG)prelemT->rdhndl)); DEBUG((MNGRP,RIOLVL,"RESETR: From save %d",(ULONG)prelemT->svhndl)); handleT = Cdup2(prelemT->svhndl, prelemT->rdhndl); Cclose(prelemT->svhndl); DEBUG((MNGRP,RIOLVL,"RESETR: Dup2 retcode = %d", handleT)); } else { if (prelemT->svhndl == BADHANDLE) { DEBUG((MNGRP,RIOLVL,"RESETR: Closing %d",(ULONG)prelemT->rdhndl)); Cclose(prelemT->rdhndl); } } prelemT->svhndl = 0; prelemT = prelemT->nxt; } // // Kill list element // rioCur = prio->back; DEBUG((MNGRP, RIOLVL, "RESETR: List element destroyed.")); } int FindFixAndRun ( IN struct cmdnode *pcmdnode ) /*++ Routine Description: If the command name is in the form d: or, just change drives. Otherwise, search for the nodes command name in the jump table. If it is found, check the arguments for bad drivespecs or unneeded switches and call the function which executes the command. Otherwise, assume it is an external command and call ExtCom. Arguments: pcmdnode - the node of the command to be executed Return Value: SUCCESS or FAILURE if changing drives. Otherwise, whatever is returned by the function which is called to execute the command. --*/ { PTCHAR pszTokStr; USHORT DriveNum; ULONG JmpTblIdx; TCHAR cname[MAX_PATH]; TCHAR cflags; int (*funcptr)(struct cmdnode *); unsigned cbTokStr; PTCHAR pszTitle; ULONG rc; // // I haven't found where in CMD we end up with NULL pointer here // (all failing mallocs cause CMD to exit) // however I saw one strange stress failure. // So lets not cause AV and just return FAILURE if NULL. // if (pcmdnode->cmdline == NULL) return(FAILURE); // // Validate any drive letter // if (*(pcmdnode->cmdline+1) == COLON) { if (!IsValidDrv(*pcmdnode->cmdline)) { PutStdErr(ERROR_INVALID_DRIVE, NOARGS); return(FAILURE); } else { // // Make sure it isn't locked either // if ( IsDriveLocked(*pcmdnode->cmdline)) { PutStdErr( GetLastError() , NOARGS); return(FAILURE); } } // // Pull out drive letter and convert to drive number // DriveNum = (USHORT)(_totupper(*pcmdnode->cmdline) - SILOP); // // If this is just a change in drive do it here // if (mystrlen(pcmdnode->cmdline) == 2) { // // ChangeDrive set CurDrvDir in addition to changing the drive ChangeDrive(DriveNum); DEBUG((MNGRP,DPLVL,"FFAR: Drv chng to %ws", CurDrvDir)); return(SUCCESS); } // // Note that if the cmdline contains a drivespec, no attempt is made at // internal command matching whatsoever. // return(ExtCom(pcmdnode)); } // // The sequence below works as follows: // - A match between the previously-parsed first non-delimiter character // group in the cmdline and the command table is attempted. A match // sets JmpTblIdx to the command index; no match sets JmpTblIdx to -1. // - FixCom is then called, and using the value of 'i', it detects cases // of internal commands only (i == -1) which have no standard delimiter // (whitespace or "=;,") between them and their arguments such as the // "cd\foo". Note that a file foo.exe in subdirectory "cd" cannot be // executed except through full path or drive specification. FixCom // actually fixes up the cmdline and argptr fields of the node. // - The command is then executed using ExtCom (i == -1) or the internal // function indicated by the index // // Added second clause to detect REM commands which were parsed incorrectly // as CMDTYP due to semi-delimiter characters appended. If REM, we know // its OK, so just return success. If any other of the special types, // FOR, DET, EXT, etc., allow to continue and fail in ExtCom since they // weren'tparsed correctly and will bomb. // JmpTblIdx = FindAndFix( pcmdnode, (PTCHAR )&cflags ); DEBUG((MNGRP, DPLVL, "FFAR: After FixCom pcmdnode->cmdline = '%ws'", pcmdnode->cmdline)); // // Check if it was not an internal command, if so then exec it // if (JmpTblIdx == -1) { DEBUG((MNGRP, DPLVL, "FFAR: Calling ExtCom on %ws", pcmdnode->cmdline)); return(ExtCom(pcmdnode)); } // // CMD was found in table. If function field is NULL as in the // case of REM, this is a dummy entry and must return SUCCESS. // if ((funcptr = GetFuncPtr(JmpTblIdx)) == NULL) { DEBUG((MNGRP, DPLVL, "FFAR: Found internal with NULL entry")); DEBUG((MNGRP, DPLVL, " Returning SUCESS")); return(SUCCESS); } // // If the command is supposed to have the drivespecs on its args // validated before the command is executed, do it. If the command // is not allowed toto contain switches and it has one, complain. // // // Set up extra delimiter for seperating out switches // cname[0] = SwitChar; cname[1] = NULLC; pszTokStr = TokStr(pcmdnode->argptr, cname, TS_SDTOKENS); // this hack to allow environment variables to contain /? if (JmpTblIdx != SETTYP || !pszTokStr || (_tcsncmp(pszTokStr,TEXT("/\0?"),4) == 0)) { // this is to exclude START command if (JmpTblIdx != STRTTYP) { if (CheckHelpSwitch(JmpTblIdx, pszTokStr) ) { return( FAILURE ); } } } DEBUG((MNGRP, DPLVL, "FFAR: Internal command, about to validate args")); for (;(pszTokStr != NULL) && *pszTokStr; pszTokStr += mystrlen(pszTokStr)+1) { cbTokStr = mystrlen(pszTokStr); mystrcpy( pszTokStr, StripQuotes( pszTokStr ) ); DEBUG((MNGRP, DPLVL, "FFAR: Checking args; arg = %ws", pszTokStr)); if ((cflags & CHECKDRIVES) && *(pszTokStr+1) == COLON) { if (!IsValidDrv(*pszTokStr)) { PutStdErr(ERROR_INVALID_DRIVE, NOARGS); return(LastRetCode = FAILURE); } else { // // If not the copy command (A->B B->A swaps) // then check if drive is locked // if drive locked then // display error return code message // terminate this command's processing // if (JmpTblIdx != CPYTYP) { if ( IsDriveLocked(*pszTokStr)) { PutStdErr( GetLastError() , NOARGS); return(LastRetCode = FAILURE); } } } } if ((cflags & NOSWITCHES) && (pszTokStr != NULL) && *pszTokStr == SwitChar) { PutStdErr(MSG_BAD_SYNTAX, NOARGS); return(LastRetCode = FAILURE); } } DEBUG((MNGRP, DPLVL, "FFAR: calling function, cmd = `%ws'", pcmdnode->cmdline)); // // Call internal routine to execute the command // if ((pszTitle = GetTitle(pcmdnode)) != NULL) { SetConTitle(pszTitle); } rc = (*funcptr)(pcmdnode); ResetConTitle(pszTitleCur); return(rc); } int FindAndFix ( IN struct cmdnode *pcmdnode, IN PTCHAR pbCmdFlags ) /*++ Routine Description: This routine separates the command and its following switch character if there is no space between the command and the switch character. This routine is used for both left side and right side of PIPE. Arguments: pcmdnode - pointer to node the contains command to locate pbCmdFlags - Return Value: --*/ { TCHAR chCur; // current character we are looking at TCHAR rgchCmdStr[MAX_PATH]; PTCHAR pszArgT; // Temp. used to build a new arguemt string ULONG JmpTableIdx; // index into jump table of function pointers ULONG iCmdStr; // index into command string LONG iDelim5CmdStr; // index into command string ULONG cbCmdStr; // length of command string DWORD dwFileAttr; BOOLEAN fQuoteFound, fQuoteFound2; BOOLEAN fDone; fQuoteFound = FALSE; fQuoteFound2 = FALSE; iDelim5CmdStr = -1; // // Extract only commnand from the command string (pcmdnode->cmdline) // for (iCmdStr = 0; iCmdStr < MAX_PATH-1; iCmdStr++) { chCur = *(pcmdnode->cmdline + iCmdStr); // // If we found a quote invert the current quote state // for both first quote (fQuoteFound) and end quote (fQuoteFound2) // if ( chCur == QUOTE ) { fQuoteFound = (BOOLEAN)!fQuoteFound; fQuoteFound2 = (BOOLEAN)!fQuoteFound; } // // If we have a character and // have found either a begin or end quote or cur char is not delimiter // and cur char is not a special (+[] etc.) delimiter // if ((chCur) && ( fQuoteFound || fQuoteFound2 || !mystrchr(Delim4,chCur))) { if (iDelim5CmdStr == -1 && mystrchr(Delim5,chCur)) { // // If extensions not enabled, then path characters terminate // the scan if (!fEnableExtensions) break; iDelim5CmdStr = iCmdStr; } rgchCmdStr[iCmdStr] = chCur; fQuoteFound2 = FALSE; } else { break; } } if (iCmdStr == 0) { return -1; } // // Null terminate command name. If a path delimiter was found somewhere // in the command name, then see if the whole command name is the name of // an existing file. If so, then that is the command, which will launch // the file through its association // rgchCmdStr[iCmdStr] = NULLC; if (iDelim5CmdStr != -1 && ((dwFileAttr = GetFileAttributes(rgchCmdStr)) == -1 || (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY) ) ) { iCmdStr = iDelim5CmdStr; rgchCmdStr[iCmdStr] = NULLC; } // // See if command is in jump table (is an internal command) // If it is not found amoung the normal internal command // check amoung the special parse type if it was a comment // if ((JmpTableIdx = FindCmd(CMDHIGH, rgchCmdStr, pbCmdFlags)) == -1) { if (FindCmd(CMDMAX, rgchCmdStr, pbCmdFlags) == REMTYP) { return(REMTYP); } } else if (JmpTableIdx == GOTYP) pcmdnode->flag = CMDNODE_FLAG_GOTO; fQuoteFound = FALSE; fQuoteFound2 = FALSE; // // If the command is not found, check the length of command string // for the case of DBCS. Count the characters that are not white space // remaining in command if ( JmpTableIdx == -1 ) { iCmdStr = 0; fDone = FALSE; while ( !fDone ) { chCur = *(pcmdnode->cmdline+iCmdStr); if ( chCur && chCur == QUOTE ) { fQuoteFound = (BOOLEAN)!fQuoteFound; fQuoteFound2 = (BOOLEAN)!fQuoteFound; } if ( chCur && ( fQuoteFound || fQuoteFound2 || !_istspace(chCur) && !mystrchr(Delimiters, chCur) && !(chCur == SwitChar))) { iCmdStr++; fQuoteFound2 = FALSE; } else { fDone = TRUE; } } } // // If cmdstr contains more than command, strip of extra part // and put it in front of the existing command argument pcmdnode-argptr // // if (iCmdStr != (cbCmdStr = mystrlen(pcmdnode->cmdline))) { int ArgLen; ArgLen = mystrlen(pcmdnode->argptr); ArgLen += cbCmdStr; if (!(pszArgT = mkstr(ArgLen*sizeof(TCHAR)))) { PutStdErr(MSG_NO_MEMORY, NOARGS); Abort(); } // // create argument string and copy the 'extra' part of command // it. // mystrcpy(pszArgT, pcmdnode->cmdline+iCmdStr); // // If we have a argument pointer stuff in the front // if (pcmdnode->argptr) { mystrcat(pszArgT, pcmdnode->argptr); } pcmdnode->argptr = pszArgT; *(pcmdnode->cmdline+iCmdStr) = NULLC; } return(JmpTableIdx); } int UnParse( IN struct node *pnode, IN PTCHAR pbCmdBuf ) /*++ Routine Description: Do setup and call UnBuild to deparse a node tree. Arguments: pnode - pointer to root of parse tree to UnParse pbCmdBuf - Uses Global pointer CBuf and assumes a string of MAXTOKLEN+1 bytes has already been allocated to it (as done by Dispatch). Return Value: --*/ { int rc; DEBUG((MNGRP, DPLVL, "UNPRS: Entered")); if (!pnode) { DEBUG((MNGRP, DPLVL, "UNPRS: Found NULL node")); return(FAILURE); } // // Leave space in front of command for a /s // Setup command buffer for a single command execution // mystrcpy( pbCmdBuf, TEXT( " /D /c\"" )); // // Setup to handle an exception during detach. if (setjmp(CmdJBuf2)) { DEBUG((MNGRP, DPLVL, "UNPRS: Longjmp return occurred!")); return(FAILURE); } // // DisAssemble the current command // rc = (UnBuild(pnode, pbCmdBuf)); mystrcat( pbCmdBuf, TEXT("\"") ); return( rc ); } UnBuild( IN struct node *pnode, IN PTCHAR pbCmdBuf ) /*++ Routine Description: Recursively take apart a parse tree of nodes, building a string of their components. Arguments: pnode - root of parse tree to UnBuild pbCmdBuf - Where to put UnBuilt command Return Value: --*/ { // // Different kinds of nodes to Unbuild // struct cmdnode *pcmdnode; struct fornode *pfornode; struct ifnode *pifnode; PTCHAR op; DEBUG((MNGRP, DPLVL, "UNBLD: Entered")); switch (pnode->type) { case LFTYP: case CSTYP: case ORTYP: case ANDTYP: case PIPTYP: case PARTYP: case SILTYP: DEBUG((MNGRP, DPLVL, "UNBLD: Found OPERATOR")); UnDuRd(pnode, pbCmdBuf); switch (pnode->type) { case LFTYP: case CSTYP: op = CSSTR; break; case ORTYP: op = ORSTR; break; case ANDTYP: op = ANDSTR; break; case PIPTYP: op = PIPSTR; break; case PARTYP: SPutC(pbCmdBuf, LEFTPSTR,YSPC); op = RPSTR; break; case SILTYP: SPutC(pbCmdBuf, SILSTR,YSPC); op = SPCSTR; break; } // // Recurse down undoing the left hand side // UnBuild(pnode->lhs, pbCmdBuf); // // Now that left side there copy in operator and do right side // SPutC(pbCmdBuf, op,YSPC); if (pnode->type != PARTYP && pnode->type != SILTYP) UnBuild(pnode->rhs, pbCmdBuf); break; case FORTYP: DEBUG((MNGRP, DPLVL, "UNBLD: Found FORTYP")); pfornode = (struct fornode *) pnode; // // Put in the FOR keyword, arguements and list // SPutC( pbCmdBuf, ForStr,YSPC); if (fEnableExtensions) { if (pfornode->flag & FOR_LOOP) { SPutC( pbCmdBuf, ForLoopStr,YSPC); } else if (pfornode->flag & FOR_MATCH_DIRONLY) { SPutC( pbCmdBuf, ForDirTooStr,YSPC); } else if (pfornode->flag & FOR_MATCH_PARSE) { SPutC( pbCmdBuf, ForParseStr,YSPC); if (pfornode->parseOpts) SPutC( pbCmdBuf, pfornode->parseOpts,YSPC); } else if (pfornode->flag & FOR_MATCH_RECURSE) { SPutC( pbCmdBuf, ForRecurseStr,YSPC); if (pfornode->recurseDir) SPutC( pbCmdBuf, pfornode->recurseDir,YSPC); } } SPutC( pbCmdBuf, pfornode->cmdline+_tcslen(ForStr),YSPC); SPutC( pbCmdBuf, LEFTPSTR,YSPC); SPutC( pbCmdBuf, pfornode->arglist,NSPC); SPutC( pbCmdBuf, RPSTR,NSPC); SPutC( pbCmdBuf, pfornode->cmdline+DOPOS,YSPC); // // Now get the for body // UnBuild(pfornode->body, pbCmdBuf); break; case IFTYP: DEBUG((MNGRP, DPLVL, "UNBLD: Found IFTYP")); // // put ine IF keyword pifnode = (struct ifnode *) pnode; SPutC( pbCmdBuf, pifnode->cmdline,YSPC); op = NULL; if (pifnode->cond->type != NOTTYP) { if (pifnode->cond->flag == CMDNODE_FLAG_IF_IGNCASE) op = TEXT("/I"); } else if (((struct cmdnode *)(pifnode->cond->argptr))->flag == CMDNODE_FLAG_IF_IGNCASE) op = TEXT("/I"); if (op) SPutC( pbCmdBuf, op,YSPC); // // Get the condition part of the statement // UnBuild((struct node *)pifnode->cond, pbCmdBuf); // // Unbuild the body of the IF // UnBuild(pifnode->ifbody, pbCmdBuf); if (pifnode->elsebody) { SPutC( pbCmdBuf, pifnode->elseline,YSPC); UnBuild(pifnode->elsebody, pbCmdBuf); } break; case NOTTYP: DEBUG((MNGRP, DPLVL, "UNBLD: Found NOTTYP")); pcmdnode = (struct cmdnode *) pnode; SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC); UnBuild((struct node *)pcmdnode->argptr, pbCmdBuf); break; case REMTYP: case CMDTYP: case ERRTYP: case EXSTYP: case DEFTYP: case CMDVERTYP: DEBUG((MNGRP, DPLVL, "UNBLD: Found CMDTYP")); pcmdnode = (struct cmdnode *) pnode; SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC); if (pcmdnode->argptr) SPutC( pbCmdBuf, pcmdnode->argptr,NSPC); UnDuRd((struct node *)pcmdnode, pbCmdBuf); break; case CMPTYP: case STRTYP: pcmdnode = (struct cmdnode *) pnode; op = TEXT("== "); // // If extensions are enabled, handle displaying the // new forms of comparison operators. // if (fEnableExtensions) { if (pcmdnode->cmdarg == CMDNODE_ARG_IF_EQU) op = TEXT("EQU "); else if (pcmdnode->cmdarg == CMDNODE_ARG_IF_NEQ) op = TEXT("NEQ "); else if (pcmdnode->cmdarg == CMDNODE_ARG_IF_LSS) op = TEXT("LSS "); else if (pcmdnode->cmdarg == CMDNODE_ARG_IF_LEQ) op = TEXT("LEQ "); else if (pcmdnode->cmdarg == CMDNODE_ARG_IF_GTR) op = TEXT("GTR "); else if (pcmdnode->cmdarg == CMDNODE_ARG_IF_GEQ) op = TEXT("GEQ "); } SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC); SPutC( pbCmdBuf, op, NSPC); if (pcmdnode->argptr) SPutC( pbCmdBuf, pcmdnode->argptr,NSPC); UnDuRd((struct node *)pcmdnode, pbCmdBuf); break; case HELPTYP: DEBUG((MNGRP, DPLVL, "UNBLD: Found HELPTYP")); if (LastMsgNo == MSG_HELP_FOR) { SPutC( pbCmdBuf, TEXT("FOR /?"), YSPC); } else if (LastMsgNo == MSG_HELP_IF) { SPutC( pbCmdBuf, TEXT("IF /?"), YSPC); } else if (LastMsgNo == MSG_HELP_REM) { SPutC( pbCmdBuf, TEXT("REM /?"), YSPC); } else { DEBUG((MNGRP, DPLVL, "UNBLD: Unknown Type!")); longjmp(CmdJBuf2,-1); } break; default: DEBUG((MNGRP, DPLVL, "UNBLD: Unknown Type!")); longjmp(CmdJBuf2,-1); } return(SUCCESS); } void UnDuRd( IN struct node *pnode, IN PTCHAR pbCmdBuf ) /*++ Routine Description: Unparse any input or output redirection associated with the current node. Arguments: pnode - current parse tree node pbCmdBuf - buffer holding command Return Value: --*/ { struct relem *prelem; TCHAR tmpstr[2]; DEBUG((MNGRP, DPLVL, "UNDURD: Entered")); tmpstr[1] = NULLC; prelem = pnode->rio; while (prelem) { // // this makes big time assumption about size of handle // tmpstr[0] = (TCHAR)prelem->rdhndl + (TCHAR)'0'; SPutC( pbCmdBuf, tmpstr,YSPC); if (prelem->rdop == INOP) SPutC( pbCmdBuf, INSTR,NSPC); else SPutC( pbCmdBuf, prelem->flag ? APPSTR : OUTSTR,NSPC); SPutC( pbCmdBuf, prelem->fname,NSPC); prelem = prelem->nxt; } } void SPutC( IN PTCHAR pbCmdBuf, IN PTCHAR pszInput, IN int flg ) /*++ Routine Description: If within length limits, add the current substring to the command under construction delimiting with a space. Arguments: pbCmdBuf - Where to put string pszInputString - String to put in pbCmdBuf flg - Flags controling placement of spaces Return Value: --*/ { DEBUG((MNGRP, DPLVL, "SPutC: Entered, Adding '%ws'",pszInput)); if ((mystrlen(pbCmdBuf) + mystrlen(pszInput) + 1) > MAXTOKLEN) { PutStdErr(MSG_LINES_TOO_LONG, NOARGS); longjmp(CmdJBuf2,-1); } if (flg && (*(pbCmdBuf+mystrlen(pbCmdBuf)-1) != SPACE) && (*pszInput != SPACE)) { SpaceCat(pbCmdBuf,pbCmdBuf,pszInput); } else { mystrcat(pbCmdBuf,pszInput); } } /*** DelayedEnvVarSub - controls execution time substitution of environment variables. * * Purpose: * Examine a parse tree node and make delayed environment variable substitutions * for those fields in the node that we care about. Don't need to walk * into child parse nodes, as they will go back through Dispatch when * executed and hence to us here. * * int DelayedEnvVarSub(struct node *n) * * Args: * n - pointer to the statement subtree in which the substitutions are * to be made * save - place to save original strings, if we change any * bRestore - TRUE if we are restoring original strings from save parameter * instead of doing substitution. * * Returns: * SUCCESS if all goes well. * FAILURE if an oversized command is found. * * Note: * The variables to be substituted for are found the current environment * block. Only variables names surrounded by exclamation marks will be * substituted (e.g. !varname!). Actual substitution is done by DESubWork * routine. * */ int DelayedEnvVarSub(n, save, bRestore) struct cmdnode *n; struct savtype *save; BOOLEAN bRestore; { int j; /* Temps used to make substitutions... */ struct relem *io; /* M017 - Pointer to redir list */ if (!n) return(SUCCESS); switch (n->type) { case LFTYP: case CSTYP: case ORTYP: case ANDTYP: case PIPTYP: case PARTYP: case SILTYP: for (j=0, io=n->rio; j < 10 && io; j++, io=io->nxt) { if (DESubWork(bRestore, &io->fname, &save->saveptrs[j])) return(FAILURE); } return(SUCCESS); case FORTYP: if (DESubWork(bRestore, &((struct fornode *) n)->arglist, &save->saveptrs[0])) return(FAILURE); return(SUCCESS); case IFTYP: n = ((struct ifnode *)n)->cond; if (n->type == NOTTYP) n = (struct cmdnode *)n->argptr; if (DESubWork(bRestore, &n->cmdline, &save->saveptrs[0])) return(FAILURE); if (DESubWork(bRestore, &n->argptr, &save->saveptrs[1])) return(FAILURE); return(SUCCESS); case REMTYP: case CMDTYP: case CMDVERTYP: case ERRTYP: case DEFTYP: case EXSTYP: case STRTYP: case CMPTYP: if (DESubWork(bRestore, &n->cmdline, &save->saveptrs[0]) || DESubWork(bRestore, &n->argptr, &save->saveptrs[1])) return(FAILURE); for (j=2, io=n->rio; j < 12 && io; j++, io=io->nxt) { if (DESubWork(bRestore, &io->fname, &save->saveptrs[j])) return(FAILURE); } return(SUCCESS); } return(SUCCESS); } /*** DESubWork - does runtime environment variable substitutions * * Purpose: * Make environment variable substitutions for those references in the * passed string. References are identified by valid environment variable * names bracketed by exclamation marks (e.g. !PATH!). If the source * string is modified, a copy of the original is saved in the save * parameter. * * DESubWork(BOOLEAN bRestore, TCHAR **src, TCHAR **save) * * Args: * bRestore - TRUE if we are restoring original strings from save parameter * instead of doing substitution. * src - the string being examined * save - pointer to where to save *src if *src modified. * * Returns: * SUCCESS if substitutions could be made. * FAILURE if the new string is too long. * * Notes: * */ DESubWork(bRestore, src, save) BOOLEAN bRestore; TCHAR **src; TCHAR **save; { TCHAR *dest; TCHAR *dststr; TCHAR *srcstr, *srcpy, *substr, c; int dlen; /* Length of dest string */ int slen; /* Length of src string used */ int sslen; /* Length of substr */ DEBUG((BPGRP, FOLVL, "SFW: Entered.")); // // If we've performed some substitutions and are restoring // the original strings // if (bRestore) { // // If we've saved something then we have work to do // if (*save != NULL) { // // If we have a substitution that we've made, then we // must free this string // if (*src != NULL) { FreeStr( *src ); } // // If the original was saved, then we need to restore it. // if (*save != NULL) *src = *save; else *save = NULL; } return(SUCCESS); } srcpy = *src; // // If there's no source or there's no delayed-sub char ! // then we have nothing to do // if (srcpy == NULL || !_tcschr(srcpy, TEXT('!'))) { return(SUCCESS); } // // Create a substitution string // if (!(dest = mkstr( (MAXTOKLEN+1)*sizeof(TCHAR)))) { return(FAILURE); } srcstr = srcpy; dststr = dest; dlen = 0; // // Walk through the source expanding each found environment variable // while (TRUE) { // // If we have produced a token that's too long, break out // if (dlen > MAXTOKLEN) { break; } // // Get the next character from the input // c = *srcstr++; if (c == TEXT('\0')) { break; } // // See if we have a exclamation character indicating a variable // reference. Process the environment variable when we see it. // if (c == TEXT('!')) { // // Perform complex substitution // substr = MSEnvVar( NULL, srcstr, &slen, c ); // // If we were able to generate a substitution, do a length // check, append the string, and then advance over the // source of the substitution // if (substr != NULL) { sslen = mystrlen( substr ); dlen += sslen; if (dlen > MAXTOKLEN) { break; } _tcscpy( dststr, substr ); dststr += sslen; srcstr += slen; // // No substitution was possible, if we're in a batch file // simply skip over the source // } else if (CurrentBatchFile) { srcstr += slen; // // WEIRD: No substitution, no batch file, just copy the % char and keep // on processing // } else { *dststr++ = c; dlen++; } } else { // // Non-exclamation. If this is a quote and there's a next character, use it. // No next character is end of parsing // if (c == TEXT( '^' )) { c = *srcstr++; if (c == TEXT( '\0' )) { break; } } // // Copy in the single character // *dststr++ = c; dlen++; } } // // If we've gotten too long then free the string and bail // if (dlen > MAXTOKLEN) { FreeStr( dest ); return(FAILURE); } *save = srcpy; if (!(*src = resize( dest, (dlen+1)*sizeof(TCHAR*)))) { FreeStr( dest ); return(FAILURE); } return(SUCCESS); } // // Queries for cmd policy // // 0 = no policy, normal operation // 1 = completely disabled // 2 = interactive prompt disabled, but scripts allowed to run // VOID GetCmdPolicy(INT * iDisabled) { DWORD dwSize, dwType; HKEY hKey; // // Set default // *iDisabled = CMD_POLICY_NORMAL; if (RegOpenKeyEx (HKEY_CURRENT_USER, TEXT("Software\\Policies\\Microsoft\\Windows\\System"), 0, KEY_READ, &hKey) == ERROR_SUCCESS) { dwSize = sizeof(INT); RegQueryValueEx (hKey, TEXT("DisableCMD"), NULL, &dwType, (LPBYTE) iDisabled, &dwSize); RegCloseKey (hKey); } } /*++ Routine Description: This routine dumps string data Arguments: Bytes - Points to bytes to be dumped Length - length of bytes to dump. -1 means dump up to the first zero byte Return Value: None. --*/ void DumpBytes( PBYTE Bytes, ULONG Length ) { ULONG i; if (Length == -1) { Length = strlen( Bytes ); } for (i = 0; i < Length; i++) { if ((i%16) == 0) { printf( "\n%04x: ", i ); } printf( " %02x", Bytes[i] & 0xFF ); } if (Length != 0) { printf( "\n" ); } } void DumpTchars( PTCHAR Chars, ULONG Length ) { ULONG i; if (Length == -1) { Length = _tcslen( Chars ); } for (i = 0; i < Length; i++) { if ((i%16) == 0) { printf( "\n%04x: ", i ); } if (sizeof( TCHAR ) == 1) { printf( " %02x", Chars[i] & 0xFF ); } else { printf( " %04x", Chars[i] & 0xFFFF ); } } if (Length != 0) { printf( "\n" ); } }