Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2449 lines
64 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. cmd.c
  5. Abstract:
  6. Top-level driver for CMD
  7. --*/
  8. #include "cmd.h"
  9. //
  10. // Used in rebuilding command lines for display
  11. //
  12. #define NSPC 0 // Don't use space
  13. #define YSPC 1 // Do use space
  14. extern CPINFO CurrentCPInfo;
  15. extern UINT CurrentCP;
  16. extern ULONG LastMsgNo;
  17. //
  18. // Jump buffers used to return to main loop after some error condition
  19. //
  20. jmp_buf MainEnv; // SigHand() uses to return to main
  21. jmp_buf CmdJBuf1; // Both of these buffers are used by
  22. jmp_buf CmdJBuf2; // various parts of Command for error
  23. //
  24. // rioCur points to a linked list of rio structures dynamically
  25. // allocated when redirection is performed. Note that memory is automatically
  26. // freed when Dispatch completed work. rioCur points to last entry.
  27. //
  28. struct rio *rioCur = NULL;
  29. //
  30. // Retrun code for last external program
  31. //
  32. int LastRetCode;
  33. //
  34. // Constants used in parsing
  35. //
  36. extern TCHAR PathChar;
  37. extern TCHAR SwitChar;
  38. extern TCHAR Delimiters[];
  39. extern TCHAR Delim2[];
  40. extern TCHAR Delim4[];
  41. extern TCHAR Delim5[];
  42. extern TCHAR ForStr[];
  43. extern TCHAR ForLoopStr[];
  44. extern TCHAR ForDirTooStr[];
  45. extern TCHAR ForParseStr[];
  46. extern TCHAR ForRecurseStr[];
  47. //
  48. // Current Drive:Directory. Set in ChDir
  49. // It it is change temp. SaveDir used used to old original
  50. //
  51. extern TCHAR CurDrvDir[];
  52. //
  53. // Name of NULL device. Used to output to nothing
  54. //
  55. extern TCHAR DevNul[];
  56. //
  57. // Number of elements in Data stack
  58. //
  59. extern ULONG DCount;
  60. //
  61. // Environment string to locate command shell.
  62. //
  63. extern TCHAR ComSpecStr[];
  64. //
  65. // DOS error code
  66. //
  67. extern unsigned DosErr;
  68. //
  69. // Alternative path (DDPATH) to search
  70. //
  71. extern TCHAR AppendStr[];
  72. //
  73. // flag if control-c was seen
  74. //
  75. extern BOOL CtrlCSeen;
  76. extern BOOLEAN fPrintCtrlC;
  77. extern PTCHAR pszTitleCur;
  78. extern BOOLEAN fTitleChanged;
  79. //
  80. // Prototypes
  81. //
  82. PTCHAR
  83. GetEnvVar();
  84. PTCHAR
  85. EatWS();
  86. int
  87. UnParse(struct node *, PTCHAR);
  88. int
  89. UnBuild(struct node *, PTCHAR);
  90. void
  91. UnDuRd(struct node *, PTCHAR);
  92. void
  93. SPutC(PTCHAR, PTCHAR,int );
  94. PTCHAR
  95. argstr1();
  96. int DelayedEnvVarSub(struct cmdnode *, struct savtype *, BOOLEAN);
  97. int DESubWork(BOOLEAN, TCHAR **, TCHAR **);
  98. VOID GetCmdPolicy(INT * iDisabled);
  99. #define CMD_POLICY_NORMAL 0
  100. #define CMD_POLICY_DISABLE_SCRIPTS 1
  101. #define CMD_POLICY_ALLOW_SCRIPTS 2
  102. //
  103. // Used to set and reset ctlcseen flag
  104. //
  105. VOID SetCtrlC();
  106. VOID ResetCtrlC();
  107. //
  108. // to monitor stack usage
  109. //
  110. extern BOOLEAN flChkStack;
  111. extern PVOID FixedPtrOnStack;
  112. typedef struct {
  113. PVOID Base;
  114. PVOID GuardPage;
  115. PVOID Bottom;
  116. PVOID ApprxSP;
  117. } STACK_USE;
  118. extern STACK_USE GlStackUsage;
  119. extern int ChkStack (PVOID pFixed, STACK_USE *pStackUse);
  120. int
  121. __cdecl
  122. main()
  123. /*++
  124. Routine Description:
  125. Main entry point for command interpreter
  126. Arguments:
  127. Command line:
  128. /P - Permanent Command. Set permanent CMD flag.
  129. /C - Single command. Build a command line out of the rest of
  130. the args and pass it back to Init.
  131. /K - Same as /C but also set SingleBatchInvocation flag.
  132. /Q - No echo
  133. Return Value:
  134. Return: 0 - If success
  135. 1 - Parsing Error
  136. 0xFF - Could not init
  137. n - Return code from command
  138. --*/
  139. {
  140. CHAR VarOnStack;
  141. struct node *pnodeCmdTree;
  142. //
  143. // When in multi-cmd mode tells parser where to get input from.
  144. //
  145. int InputType;
  146. //
  147. // Pointer to initial command lines
  148. //
  149. PTCHAR InitialCmds[ 3 ];
  150. int i, iDisabled;
  151. BOOL bInit;
  152. //
  153. // flag used when a setjmp returns while processing /K
  154. // error and move to next line.
  155. //
  156. unsigned fIgnore = FALSE;
  157. unsigned ReturnCode, rc;
  158. //
  159. // Since we operate in a multilingual environment, we must set up the
  160. // system/user/thread locales correctly BEFORE ever issuing a message
  161. //
  162. #if !defined( WIN95_CMD )
  163. CmdSetThreadUILanguage(0);
  164. #endif
  165. __try {
  166. //
  167. // Check policy to see if cmd is disabled
  168. //
  169. GetCmdPolicy (&iDisabled);
  170. //
  171. // flChkStack is turned ON initially here and it stays ON while
  172. // I believe the information returned by ChkStack() is correct.
  173. //
  174. // It is turned OFF the first time I don't believe that info and
  175. // therefore I don't want to make any decisions changing the CMD's
  176. // behavior.
  177. //
  178. // It will stay OFF until CMD terminates so we will never check
  179. // stack usage again.
  180. //
  181. // I implemented one method to prevent CMD.EXE from the stack overflow:
  182. // Have count and limit of recursion in batch file processing and check
  183. // stack every time we exceed the limit of recursion until we reach 90%
  184. // of stack usage.
  185. // If (stack usage >= 90% of 1 MByte) then terminate batch file
  186. // unconditionally and handle such termination properly (freeing memory
  187. // and stack and saving CMD.EXE)
  188. //
  189. // It is also possible to implement SEH but then we won't know about
  190. // CMD problems.
  191. //
  192. flChkStack = 1;
  193. FixedPtrOnStack = (VOID *) &VarOnStack; // to be used in ChkStack()
  194. if ( ChkStack (FixedPtrOnStack, &GlStackUsage) == FAILURE ) {
  195. flChkStack = 0;
  196. }
  197. //
  198. // Initialize the DBCS lead byte table based on the current locale.
  199. //
  200. InitializeDbcsLeadCharTable( );
  201. //
  202. // Set base APIs to operate in OEM mode
  203. //
  204. #ifndef UNICODE
  205. SetFileApisToOEM();
  206. #endif /* Unicode */
  207. //
  208. // Init returns TRUE if there are any commands to run before
  209. // entering the main loop (e.g. /C or /K and/or AutoRun from registry)
  210. //
  211. memset( &InitialCmds, 0, sizeof( InitialCmds ) );
  212. ReturnCode = 0;
  213. bInit = Init( InitialCmds );
  214. if (CMD_POLICY_DISABLE_SCRIPTS == iDisabled) {
  215. PutStdOut( MSG_DISABLED_BY_POLICY, NOARGS ) ;
  216. ePause(0);
  217. CMDexit( 0xFF );
  218. }
  219. if ( bInit ) {
  220. if (setjmp(MainEnv)) {
  221. //
  222. // If processing /K and setjmp'd out of init. then ignore
  223. //
  224. fIgnore = TRUE;
  225. if ( SingleCommandInvocation )
  226. ReturnCode = 0xFF;
  227. }
  228. if ( !fIgnore ) {
  229. //
  230. // Loop over any initial commands read from registry of from /C or /K
  231. //
  232. for (i=0; i<3; i++)
  233. if (InitialCmds[i] != NULL) {
  234. DEBUG((MNGRP, MNLVL, "MAIN: Single command mode on `%ws'", InitialCmds[i]));
  235. if ((pnodeCmdTree = Parser(READSTRING, (INT_PTR)InitialCmds[i], DCount)) == (struct node *) PARSERROR)
  236. CMDexit(MAINERROR);
  237. if (pnodeCmdTree == (struct node *) EOF)
  238. CMDexit(SUCCESS);
  239. DEBUG((MNGRP, MNLVL, "MAIN: Single command parsed successfully."));
  240. rc = Dispatch(RIO_MAIN, pnodeCmdTree);
  241. if (rc != 0)
  242. ReturnCode = rc;
  243. }
  244. //
  245. // Make sure we have the correct console modes.
  246. //
  247. ResetConsoleMode();
  248. //
  249. // Get current CodePage Info. We need this to decide whether
  250. // or not to use half-width characters.
  251. //
  252. GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo);
  253. //
  254. // Maybe console output code page was changed by CHCP or MODE,
  255. // so need to reset LanguageID to correspond to code page.
  256. //
  257. #if !defined( WIN95_CMD )
  258. CmdSetThreadUILanguage(0);
  259. #endif
  260. }
  261. //
  262. // All done if /C specified.
  263. //
  264. if ( SingleCommandInvocation )
  265. CMDexit( ReturnCode );
  266. SingleBatchInvocation = FALSE; // Allow ASync exec of GUI apps now
  267. }
  268. //
  269. // Through init and single command processing. reset our Setjmp location
  270. // to here for error processing.
  271. //
  272. if (ReturnCode = setjmp(MainEnv)) {
  273. //
  274. // fix later to have a generalized abort
  275. // for now assume this is a real abort from
  276. // eof on stdin redirected.
  277. if (ReturnCode == EXIT_EOF) {
  278. CMDexit(SUCCESS);
  279. }
  280. }
  281. //
  282. // Exit now if the interactive command prompt
  283. //
  284. if (CMD_POLICY_ALLOW_SCRIPTS == iDisabled) {
  285. PutStdOut( MSG_DISABLED_BY_POLICY, NOARGS ) ;
  286. ePause(0);
  287. CMDexit( 0xFF );
  288. }
  289. //
  290. // Check if our I/O has been redirected. This is used to tell
  291. // where we should read input from.
  292. //
  293. InputType = (FileIsDevice(STDIN)) ? READSTDIN : READFILE;
  294. DEBUG((MNGRP,MNLVL,"MAIN: Multi command mode, InputType = %d", InputType));
  295. //
  296. // If we are reading from a file, make sure the input mode is binary.
  297. // CRLF translations mess up the lexer because FillBuf() wants to
  298. // seek around in the file.
  299. //
  300. if(InputType == READFILE) {
  301. _setmode(STDIN,_O_BINARY);
  302. }
  303. //
  304. // Loop till out of input or error parsing.
  305. //
  306. while (TRUE) {
  307. DEBUG((MNGRP, MNLVL, "MAIN: Calling Parser."));
  308. GotoFlag = FALSE;
  309. ResetCtrlC();
  310. if ((pnodeCmdTree = Parser(InputType, STDIN, FS_FREEALL)) == (struct node *) PARSERROR) {
  311. DEBUG((MNGRP, MNLVL, "MAIN: Parse failed."));
  312. } else if (pnodeCmdTree == (struct node *) EOF)
  313. CMDexit(SUCCESS);
  314. else {
  315. ResetCtrlC();
  316. DEBUG((MNGRP, MNLVL, "MAIN: Parsed OK, DISPATCHing."));
  317. //
  318. // Get current CodePage Info. We need this to decide whether
  319. // or not to use half-width characters.
  320. //
  321. GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo);
  322. //
  323. // Maybe console output code page was changed by console property sheet
  324. // so need to reset LanguageID to correspond to code page.
  325. //
  326. #if !defined( WIN95_CMD )
  327. CmdSetThreadUILanguage(0);
  328. #endif
  329. Dispatch(RIO_MAIN, pnodeCmdTree);
  330. //
  331. // Make sure we have the correct console modes.
  332. //
  333. ResetConsoleMode();
  334. //
  335. // Get current CodePage Info. We need this to decide whether
  336. // or not to use half-width characters.
  337. //
  338. GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo);
  339. //
  340. // Maybe console output code page was changed by CHCP or MODE,
  341. // so need to reset LanguageID to correspond to code page.
  342. //
  343. #if !defined( WIN95_CMD )
  344. CmdSetThreadUILanguage(0);
  345. #endif
  346. DEBUG((MNGRP, MNLVL, "MAIN: Dispatch returned."));
  347. }
  348. }
  349. CMDexit(SUCCESS);
  350. ReturnCode = SUCCESS;
  351. } __except(EXCEPTION_EXECUTE_HANDLER) {
  352. ReturnCode = -1;
  353. }
  354. return ReturnCode;
  355. }
  356. FARPROC WINAPI CmdDelayHook(
  357. UINT dliNotify,
  358. PDelayLoadInfo pdli
  359. )
  360. {
  361. if (ReportDelayLoadErrors) {
  362. if (dliNotify == dliFailLoadLib) {
  363. PutStdErr( MSG_CANNOT_LOAD_LIB, ONEARG, pdli->szDll );
  364. PutStdErr( pdli->dwLastError, NOARGS );
  365. } else if (dliNotify == dliFailGetProc) {
  366. if (pdli->dlp.fImportByName) {
  367. PutStdErr( MSG_CANNOT_FIND_FUNC_NAME, TWOARGS, pdli->szDll, pdli->dlp.szProcName );
  368. } else {
  369. PutStdErr( MSG_CANNOT_FIND_FUNC_ORDINAL, TWOARGS, pdli->szDll, pdli->dlp.dwOrdinal );
  370. }
  371. PutStdErr( pdli->dwLastError, NOARGS );
  372. }
  373. }
  374. return 0;
  375. }
  376. //
  377. // Override the standard definition of __pfnDliNotifyHook that's part of
  378. // DELAYHLP.LIB
  379. //
  380. PfnDliHook __pfnDliFailureHook = CmdDelayHook;
  381. int
  382. Dispatch(
  383. IN int RioType,
  384. IN struct node *pnodeCmdTree
  385. )
  386. /*++
  387. Routine Description:
  388. Set up any I/O redirection for the current node. Find out who is
  389. supposed to process this node and call the routine to do it. Reset
  390. stdin/stdout if necessary.
  391. Dispatch() must now be called with all args present since the RioType
  392. is needed to properly identify the redirection list element.
  393. Dispatch() determines the need for redirection by examination of the
  394. RioType and command node and calls SetRedir only if necessary. Also,
  395. in like manner, Dispatch() only calls ResetRedir if redirection was
  396. actually performed.
  397. The conditional that determines whether newline will be issued
  398. following commands (prior to prompt), had to be altered so that the
  399. execution of piped commands did not each issue a newline. The pre-
  400. prompt newline for pipe series is now issued by ePipe().
  401. Arguments:
  402. RioType - tells SetRedir the routine responsible for redirection
  403. pnodeCmdTree - the root of the parse tree to be executed
  404. Return Value:
  405. The return code from the command/function that was executed or
  406. FAILURE if redirection error.
  407. --*/
  408. {
  409. int comretcode; // Retcode of the cmnd executed
  410. struct cmdnode *pcmdnode; // pointer to current command node
  411. PTCHAR pbCmdBuf; // Buffer used in building command
  412. struct savtype save;
  413. DEBUG((MNGRP, DPLVL, "DISP: pnodeCmdTree = 0x%04x, RioType = %d", pnodeCmdTree, RioType));
  414. //
  415. // If we don't have a parse tree or
  416. // we have a goto label or
  417. // we have a comment line
  418. // then don't execute anything and return.
  419. //
  420. if (!pnodeCmdTree ||
  421. GotoFlag ||
  422. pnodeCmdTree->type == REMTYP) {
  423. return(SUCCESS);
  424. }
  425. comretcode = DISPERROR;
  426. DEBUG((MNGRP, DPLVL, "DISP: type = 0x%02x", pnodeCmdTree->type));
  427. //
  428. // Copy node ptr pnodeCmdTree to new node ptr pcmdnode
  429. // If command is to be detached or pipelined (but not a pipe)
  430. // If command is Batch file or Internal or Multi-statement command
  431. // "Unparse" tree into string approximating original commandline
  432. // Build new command node (pcmdnode) to spawn a child Command.com
  433. // Make the string ("/C" prepended) the argument of the new node
  434. // Perform redirection on node c
  435. // If node pcmdnode is to be detatched
  436. // Exec async/discard
  437. // else
  438. // Exec async/keep but don't wait for retcode (pipelined)
  439. // else
  440. // If this is a CMDTYP, PARTYP or SILTYP node and there is explicit redirection
  441. //
  442. // Perform redirection on this node
  443. // If operator node or a special type (FOR, IF, DET or REM)
  444. // Call routine identified by GetFuncPtr() to execute it
  445. // Else call FindFixAndRun() to execute the CMDTYP node.
  446. // If redirection was performed
  447. // Reset redirection
  448. //
  449. pcmdnode = (struct cmdnode *)pnodeCmdTree;
  450. if (fDelayedExpansion) {
  451. memset(&save, 0, sizeof(save));
  452. if (DelayedEnvVarSub(pcmdnode, &save, FALSE)) {
  453. goto dispatchExit;
  454. }
  455. }
  456. //
  457. // If we are called from ePipe and PIPE command then we need
  458. // to rebuild the command in ascii form (UnParse) and fork
  459. // off another cmd.exe to execute it.
  460. //
  461. if ((RioType == RIO_PIPE && pcmdnode->type != PIPTYP)) {
  462. //
  463. // pbCmdbuf is used as tmp in FindCmd and SFE
  464. //
  465. if (!(pbCmdBuf = mkstr( MAXTOKLEN * sizeof( TCHAR )))) {
  466. goto dispatchExit;
  467. }
  468. //
  469. // If current node to execute is not a command or
  470. // could not find it as an internal command or
  471. // it was found as a batch file then
  472. // Do the unparse
  473. //
  474. if (pcmdnode->type != CMDTYP ||
  475. FindCmd(CMDHIGH, pcmdnode->cmdline, pbCmdBuf) != -1 ||
  476. SearchForExecutable(pcmdnode, pbCmdBuf) == SFE_ISBAT) {
  477. DEBUG((MNGRP, DPLVL, "DISP: Now UnParsing"));
  478. //
  479. // if pcmdnode an intrnl cmd then pbCmdBuf holds it's switches
  480. // if pcmdnode was a batch file then pbCmdBuf holds location
  481. //
  482. if (UnParse((struct node *)pcmdnode, pbCmdBuf)) {
  483. goto dispatchExit;
  484. }
  485. DEBUG((MNGRP, DPLVL, "DISP: UnParsed cmd = %ws", pbCmdBuf));
  486. //
  487. // Build a command node with unparsed command
  488. // Will be exec'd later after redirection is applied
  489. //
  490. pcmdnode = (struct cmdnode *)mknode();
  491. if (pcmdnode == NULL) {
  492. goto dispatchExit;
  493. }
  494. pcmdnode->type = CMDTYP;
  495. pcmdnode->cmdline = GetEnvVar(ComSpecStr);
  496. pcmdnode->argptr = pbCmdBuf;
  497. }
  498. //
  499. // Setup I/O redirection
  500. //
  501. if (SetRedir((struct node *)pcmdnode, RioType)) {
  502. goto dispatchExit;
  503. }
  504. DEBUG((MNGRP, DPLVL, "DISP:Calling ECWork on piped cmd"));
  505. pbCmdBuf[1] = SwitChar;
  506. pbCmdBuf[2] = TEXT('S');
  507. comretcode = ECWork(pcmdnode, AI_KEEP, CW_W_NO);
  508. DEBUG((MNGRP, DPLVL, "DISP: ECWork returned %d", comretcode));
  509. } else {
  510. //
  511. // We are here if command was not PIPE
  512. //
  513. // If it was a command node or a paren or a silent operator and
  514. // we have redirection then set redirection.
  515. //
  516. if ((pnodeCmdTree->type == CMDTYP ||
  517. pnodeCmdTree->type == PARTYP ||
  518. pnodeCmdTree->type == SILTYP ||
  519. pnodeCmdTree->type == HELPTYP) &&
  520. pnodeCmdTree->rio) {
  521. //
  522. // Set redirection on node.
  523. //
  524. if (SetRedir(pnodeCmdTree, RioType)) {
  525. goto dispatchExit;
  526. }
  527. }
  528. //
  529. // If it is an internal command then find it and execute
  530. // otherwise locate file load and execute
  531. //
  532. if (pnodeCmdTree->type != CMDTYP) {
  533. comretcode = (*GetFuncPtr(pnodeCmdTree->type))((struct cmdnode *)pnodeCmdTree);
  534. } else {
  535. comretcode = FindFixAndRun((struct cmdnode *)pnodeCmdTree);
  536. }
  537. } // else
  538. //
  539. // Reset and redirection that was previously setup
  540. // pcmdnode is always current node.
  541. //
  542. if ((rioCur) && (rioCur->rnod == (struct node *)pcmdnode)) {
  543. ResetRedir();
  544. }
  545. dispatchExit:
  546. if (fDelayedExpansion) {
  547. DelayedEnvVarSub(pcmdnode, &save, TRUE);
  548. }
  549. DEBUG((MNGRP, DPLVL, "DISP: returning %d", comretcode));
  550. return(comretcode);
  551. }
  552. int
  553. SetRedir(
  554. IN struct node *pnodeCmdTree,
  555. IN int RioType
  556. )
  557. /*++
  558. Routine Description:
  559. Perform the redirection required by the current node
  560. Only individual commands and parenthesised statement groups can have
  561. explicit I/O redirection. All nodes, however, can tolerate redirection of an
  562. implicit nature.
  563. Arguments:
  564. pNode - pointer node containing redirection information
  565. RioType - indicator of source of redirection request
  566. Return Value:
  567. SUCCESS if the redirection was successfully set up.
  568. FAILURE if the redirection was NOT successfully set up.
  569. --*/
  570. {
  571. struct rio *prio;
  572. int i;
  573. CRTHANDLE OpenStatus;
  574. BOOLEAN fInputRedirected = FALSE;
  575. //
  576. // Temps. Used to hold all of the relocation information for a
  577. // command.
  578. //
  579. struct relem *prelemT;
  580. struct relem *prelemT2;
  581. TCHAR rgchFileName[MAX_PATH];
  582. const TCHAR *p;
  583. DEBUG((MNGRP, RIOLVL, "SETRD:RioType = %d.",RioType));
  584. prelemT = pnodeCmdTree->rio;
  585. //
  586. // Loop through redirections removing ":" from device names
  587. // and determining if input has been redirected
  588. //
  589. while (prelemT) {
  590. mystrcpy(prelemT->fname, StripQuotes(prelemT->fname) );
  591. //
  592. // skip any redirection that already has been done
  593. //
  594. if (prelemT->svhndl) {
  595. prelemT = prelemT->nxt;
  596. continue;
  597. }
  598. //
  599. // check for and remove any COLON that might be in a device name
  600. //
  601. if ((i = mystrlen(prelemT->fname)-1) > 1 && *(prelemT->fname+i) == COLON)
  602. *(prelemT->fname+i) = NULLC;
  603. //
  604. // If input redirection specified then set flag for later use
  605. //
  606. if (prelemT->rdhndl == STDIN) {
  607. fInputRedirected = TRUE;
  608. }
  609. prelemT = prelemT->nxt;
  610. }
  611. DEBUG((MNGRP, RIOLVL, "SETRD: fInputRedirected = %d",fInputRedirected));
  612. //
  613. // Allocate, activate and initialize the rio list element.
  614. // We must skip this if called from AddRedir (test for RIO_REPROCESS)
  615. //
  616. if (RioType != RIO_REPROCESS) {
  617. if (!(prio=(struct rio *)mkstr(sizeof(struct rio)))) {
  618. PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
  619. return ( FAILURE );
  620. }
  621. prio->back = rioCur;
  622. rioCur = prio;
  623. prio->rnod = pnodeCmdTree;
  624. prio->type = RioType;
  625. DEBUG((MNGRP, RIOLVL, "SETRD: rio element built."));
  626. } else {
  627. prio = rioCur;
  628. }
  629. //
  630. // Once the list has been set up for standard and special cases
  631. // the actual handle redirection is performed.
  632. //
  633. // loop thru the list performing all redirection and error recovery.
  634. //
  635. prelemT = pnodeCmdTree->rio;
  636. while (prelemT) {
  637. //
  638. // Skip any already done.
  639. //
  640. if (prelemT->svhndl) {
  641. prelemT = prelemT->nxt;
  642. continue;
  643. }
  644. DEBUG((MNGRP, RIOLVL, "SETRD: Old osf handle = %x", CRTTONT(prelemT->rdhndl)));
  645. //
  646. // Make sure read handle is open and valid before saving it.
  647. //
  648. if (CRTTONT(prelemT->rdhndl) == INVALID_HANDLE_VALUE) {
  649. prelemT->svhndl = BADHANDLE;
  650. }
  651. else
  652. if (FileIsDevice(prelemT->rdhndl) || FileIsPipe(prelemT->rdhndl) ||
  653. SetFilePointer(CRTTONT(prelemT->rdhndl), 0L, NULL, FILE_CURRENT) != -1) {
  654. DEBUG((MNGRP, RIOLVL, "SETRD: duping %d", prelemT->rdhndl));
  655. if ((prelemT->svhndl = Cdup(prelemT->rdhndl)) == BADHANDLE) {
  656. DEBUG((MNGRP, RIOLVL, "SETRD: Cdup error=%d, errno=%d", GetLastError(), errno));
  657. PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (unsigned long)prelemT->rdhndl));
  658. prelemT->svhndl = 0;
  659. ResetRedir();
  660. return(FAILURE);
  661. }
  662. DEBUG((MNGRP, RIOLVL, "SETRD: closing %d", prelemT->rdhndl));
  663. Cclose(prelemT->rdhndl);
  664. DEBUG((MNGRP,RIOLVL,"SETRD: save handle = %d", prelemT->svhndl));
  665. DEBUG((MNGRP,RIOLVL,"SETRD: --->osf handle = %x", CRTTONT(prelemT->svhndl)));
  666. } else {
  667. DEBUG((MNGRP, RIOLVL, "SETRD: FileIsOpen ret'd FALSE"));
  668. PutStdErr(MSG_RDR_HNDL_OPEN, ONEARG, argstr1(TEXT("%d"), (unsigned long)prelemT->rdhndl));
  669. prelemT->svhndl = 0;
  670. ResetRedir();
  671. return(FAILURE);
  672. }
  673. //
  674. // Is file name the command seperator character '&'
  675. //
  676. if (*prelemT->fname == CSOP) {
  677. DEBUG((MNGRP,RIOLVL,"SETRD: Handle substitution, %ws %d", prelemT->fname, prelemT->rdhndl));
  678. *(prelemT->fname+2) = NULLC;
  679. if (Cdup2(*(prelemT->fname+1) - TEXT('0'), prelemT->rdhndl) == BADHANDLE) {
  680. DEBUG((MNGRP, RIOLVL, "SETRD: Cdup2 error=%d, errno=%d", GetLastError(), errno));
  681. ResetRedir();
  682. PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (ULONG)prelemT->rdhndl));
  683. return(FAILURE);
  684. }
  685. DEBUG((MNGRP,RIOLVL,"SETRD: %c forced to %d",*(prelemT->fname+1), (ULONG)prelemT->rdhndl));
  686. } else {
  687. //
  688. // redirecting input from a file. Check to see if file
  689. // exists and can be opened for input.
  690. //
  691. if (prelemT->rdop == INOP) {
  692. DEBUG((MNGRP,RIOLVL,"SETRD: File in = %ws",prelemT->fname));
  693. //
  694. // Try to open file localy first
  695. //
  696. if ((OpenStatus = Copen(prelemT->fname, O_RDONLY|O_BINARY)) == BADHANDLE) {
  697. //
  698. // Now try the DPATH (data path)
  699. //
  700. p = MyGetEnvVarPtr(AppendStr);
  701. if ( p != NULL &&
  702. SearchPath( p,
  703. prelemT->fname,
  704. NULL,
  705. MAX_PATH,
  706. rgchFileName,
  707. NULL ) != 0 ) {
  708. OpenStatus = Copen(rgchFileName, O_RDONLY|O_BINARY);
  709. }
  710. }
  711. } else {
  712. //
  713. // We are not redirecting input so must be output
  714. //
  715. DEBUG((MNGRP,RIOLVL,"SETRD: File out = %ws",prelemT->fname));
  716. //
  717. // Make sure sure we can open the file for output
  718. //
  719. OpenStatus = Copen(prelemT->fname, prelemT->flag ? OP_APPEN : OP_TRUNC);
  720. }
  721. //
  722. // If the handle to be redirected was not the lowest numbered,
  723. // unopened handle when open was called, the current handle must
  724. // be forced to it, the handle returned by open must be closed.
  725. //
  726. if (OpenStatus != BADHANDLE && OpenStatus != prelemT->rdhndl) {
  727. DEBUG((MNGRP,RIOLVL,"SETRD: Handles don't match..."));
  728. DEBUG((MNGRP,RIOLVL,"SETRD: ...forcing %d to %d", i, (ULONG)prelemT->rdhndl));
  729. if (Cdup2(OpenStatus, prelemT->rdhndl) == BADHANDLE) {
  730. DEBUG((MNGRP, RIOLVL, "SETRD: Cdup2 error=%d, errno=%d", GetLastError(), errno));
  731. Cclose(OpenStatus);
  732. ResetRedir();
  733. PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (ULONG)prelemT->rdhndl));
  734. return(FAILURE);
  735. } else {
  736. Cclose(OpenStatus);
  737. OpenStatus = prelemT->rdhndl;
  738. }
  739. }
  740. //
  741. // Copen error processing must be delayed to here to allow the
  742. // above Cdup2 to occur if necessary. Otherwise, the call to
  743. // ResetRedir in the error handler would attempt to close the
  744. // wrong handle and leave a bogus handle open.
  745. //
  746. if (OpenStatus == BADHANDLE) {
  747. DEBUG((MNGRP,RIOLVL,"SETRD: Bad Open, DosErr = %d",DosErr));
  748. ResetRedir();
  749. PrtErr(DosErr);
  750. return(FAILURE);
  751. }
  752. DEBUG((MNGRP, RIOLVL, "SETRD: new handle = %d", OpenStatus));
  753. DEBUG((MNGRP,RIOLVL,"SETRD: --->osf handle = %x", CRTTONT(OpenStatus)));
  754. //
  755. // Keep highest numbered handle
  756. //
  757. prio->stdio = OpenStatus;
  758. } // else
  759. prelemT = prelemT->nxt;
  760. } // while
  761. return(SUCCESS);
  762. }
  763. AddRedir(
  764. IN struct cmdnode *pcmdnodeOriginal,
  765. IN struct cmdnode *pcmdnodeNew
  766. )
  767. /*++
  768. Routine Description:
  769. Add redirection from a new node to an existing one. Walk the
  770. redirection list of the old node for each element in the new.
  771. Duplicates are removed from the old and replaced by the new,
  772. while unique new ones are added to the end. When the two lists
  773. are merged, reprocess the redirection.
  774. Arguments:
  775. pcmdnodeOriginal - original node to be added to
  776. pcmdnodeNew - new node to merge.
  777. Return Value:
  778. SUCCESS if the redirection was successfully merged.
  779. FAILURE otherwise.
  780. --*/
  781. {
  782. struct relem *prelemOriginal;
  783. struct relem *prelemNew;
  784. struct relem *prelemEnd; // Ptr to end of original list
  785. //
  786. // Flag to set Stack Minimum
  787. //
  788. BOOLEAN fSetStackMin = FALSE;
  789. PTCHAR oldname; /* Sanity check */
  790. struct rio *rn; /* Possible rio element */
  791. //
  792. // Won't be here unless pcmdnodeNew-reio exists
  793. //
  794. prelemNew = pcmdnodeNew->rio;
  795. // If there was no redirection associated with the original node, we must
  796. // also create a rio element so that the redirection can be reset at
  797. // command completion or receipt of signal. We have to create it here
  798. // rather than in SetRedir in order to include it on the data stack when
  799. // we set a new level.
  800. if (!(prelemEnd = prelemOriginal = pcmdnodeOriginal->rio)) {
  801. DEBUG((MNGRP, RIOLVL, "ADDRD: No old redirection."));
  802. //
  803. // New list becomes original
  804. //
  805. pcmdnodeOriginal->rio = prelemNew;
  806. if (!(rn=(struct rio *)mkstr(sizeof(struct rio)))) {
  807. PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
  808. return(FAILURE);
  809. }
  810. //
  811. // Create dummy redirection node.
  812. //
  813. rn->back = rioCur;
  814. rioCur = rn;
  815. rn->rnod = (struct node *)pcmdnodeOriginal;
  816. rn->type = RIO_BATLOOP;
  817. DEBUG((MNGRP, RIOLVL, "ADDRD: rio element built."));
  818. fSetStackMin = TRUE; /* Must save current datacount */
  819. prelemNew = NULL; /* Skip the while loops */
  820. } else {
  821. //
  822. // Find the end of the orignal list
  823. //
  824. while (prelemEnd->nxt) {
  825. prelemEnd = prelemEnd->nxt;
  826. }
  827. }
  828. //
  829. // If prelemNew is non-null, we've two lists which we integrate by
  830. // eliminating any duplicate entries and adding any unique entries in
  831. // the new list to the end of the original. Note that if unique entries
  832. // exist, we must save the current data count to avoid losing their
  833. // malloc'd data when we go on to SetBat().
  834. //
  835. //
  836. // For each new redirection, look at the original
  837. //
  838. while (prelemNew) {
  839. while(prelemOriginal) {
  840. //
  841. // Do we have a duplicate
  842. //
  843. if (prelemNew->rdhndl != prelemOriginal->rdhndl) {
  844. prelemOriginal = prelemOriginal->nxt;
  845. continue;
  846. } else {
  847. if (prelemOriginal->svhndl && (prelemOriginal->svhndl != BADHANDLE)) {
  848. //
  849. // put an assert here
  850. //
  851. Cdup2(prelemOriginal->svhndl, prelemOriginal->rdhndl);
  852. Cclose(prelemOriginal->svhndl);
  853. } else {
  854. if (prelemOriginal->svhndl == BADHANDLE) {
  855. Cclose(prelemOriginal->rdhndl);
  856. }
  857. }
  858. prelemOriginal->svhndl = 0; /* ...and replace it */
  859. prelemOriginal->flag = prelemNew->flag;
  860. prelemOriginal->rdop = prelemNew->rdop;
  861. oldname = prelemOriginal->fname;
  862. prelemOriginal->fname =
  863. resize( prelemOriginal->fname,
  864. (mystrlen( prelemNew->fname ) + 1) * sizeof( TCHAR ));
  865. mystrcpy(prelemOriginal->fname, prelemNew->fname);
  866. if (prelemOriginal->fname != oldname) {
  867. fSetStackMin = TRUE;
  868. }
  869. pcmdnodeNew->rio = prelemNew->nxt;
  870. break;
  871. }
  872. }
  873. //
  874. // If no old entry remove from new and add to original
  875. // update the end pointer, zero next pointer and preserve datacount
  876. //
  877. if (prelemNew == pcmdnodeNew->rio) {
  878. pcmdnodeNew->rio = prelemNew->nxt;
  879. prelemEnd->nxt = prelemNew;
  880. prelemEnd = prelemEnd->nxt;
  881. prelemEnd->nxt = NULL;
  882. fSetStackMin = TRUE;
  883. }
  884. prelemNew = pcmdnodeNew->rio;
  885. prelemOriginal = pcmdnodeOriginal->rio;
  886. }
  887. //
  888. // All duplicates are eliminated. Now save the data count and call
  889. // SetRedir to reprocess the redirection list for any unimplimented
  890. // redirection (io->svhndl == 0).
  891. //
  892. if (fSetStackMin) {
  893. if (CurrentBatchFile->stacksize < (CurrentBatchFile->stackmin = DCount)) {
  894. CurrentBatchFile->stacksize = DCount;
  895. }
  896. }
  897. return(SetRedir((struct node *)pcmdnodeOriginal, RIO_REPROCESS));
  898. }
  899. void
  900. ResetRedir()
  901. /*++
  902. Routine Description:
  903. Reset the redirection identified by the last rio list element
  904. as pointed to by rioCur. When finished, remove the rio element
  905. from the list.
  906. Arguments:
  907. Return Value:
  908. --*/
  909. {
  910. struct rio *prio = rioCur;
  911. struct relem *prelemT;
  912. CRTHANDLE handleT;
  913. DEBUG((MNGRP, RIOLVL, "RESETR: Entered."));
  914. prelemT = prio->rnod->rio;
  915. while (prelemT) {
  916. if (prelemT->svhndl && (prelemT->svhndl != BADHANDLE)) {
  917. DEBUG((MNGRP,RIOLVL,"RESETR: Resetting %d",(ULONG)prelemT->rdhndl));
  918. DEBUG((MNGRP,RIOLVL,"RESETR: From save %d",(ULONG)prelemT->svhndl));
  919. handleT = Cdup2(prelemT->svhndl, prelemT->rdhndl);
  920. Cclose(prelemT->svhndl);
  921. DEBUG((MNGRP,RIOLVL,"RESETR: Dup2 retcode = %d", handleT));
  922. } else {
  923. if (prelemT->svhndl == BADHANDLE) {
  924. DEBUG((MNGRP,RIOLVL,"RESETR: Closing %d",(ULONG)prelemT->rdhndl));
  925. Cclose(prelemT->rdhndl);
  926. }
  927. }
  928. prelemT->svhndl = 0;
  929. prelemT = prelemT->nxt;
  930. }
  931. //
  932. // Kill list element
  933. //
  934. rioCur = prio->back;
  935. DEBUG((MNGRP, RIOLVL, "RESETR: List element destroyed."));
  936. }
  937. int
  938. FindFixAndRun (
  939. IN struct cmdnode *pcmdnode
  940. )
  941. /*++
  942. Routine Description:
  943. If the command name is in the form d: or, just change drives.
  944. Otherwise, search for the nodes command name in the jump table.
  945. If it is found, check the arguments for bad drivespecs or unneeded
  946. switches and call the function which executes the command.
  947. Otherwise, assume it is an external command and call ExtCom.
  948. Arguments:
  949. pcmdnode - the node of the command to be executed
  950. Return Value:
  951. SUCCESS or FAILURE if changing drives.
  952. Otherwise, whatever is returned by the function which is called to
  953. execute the command.
  954. --*/
  955. {
  956. PTCHAR pszTokStr;
  957. USHORT DriveNum;
  958. ULONG JmpTblIdx;
  959. TCHAR cname[MAX_PATH];
  960. TCHAR cflags;
  961. int (*funcptr)(struct cmdnode *);
  962. unsigned cbTokStr;
  963. PTCHAR pszTitle;
  964. ULONG rc;
  965. //
  966. // I haven't found where in CMD we end up with NULL pointer here
  967. // (all failing mallocs cause CMD to exit)
  968. // however I saw one strange stress failure.
  969. // So lets not cause AV and just return FAILURE if NULL.
  970. //
  971. if (pcmdnode->cmdline == NULL)
  972. return(FAILURE);
  973. //
  974. // Validate any drive letter
  975. //
  976. if (*(pcmdnode->cmdline+1) == COLON) {
  977. if (!IsValidDrv(*pcmdnode->cmdline)) {
  978. PutStdErr(ERROR_INVALID_DRIVE, NOARGS);
  979. return(FAILURE);
  980. } else {
  981. //
  982. // Make sure it isn't locked either
  983. //
  984. if ( IsDriveLocked(*pcmdnode->cmdline)) {
  985. PutStdErr( GetLastError() , NOARGS);
  986. return(FAILURE);
  987. }
  988. }
  989. //
  990. // Pull out drive letter and convert to drive number
  991. //
  992. DriveNum = (USHORT)(_totupper(*pcmdnode->cmdline) - SILOP);
  993. //
  994. // If this is just a change in drive do it here
  995. //
  996. if (mystrlen(pcmdnode->cmdline) == 2) {
  997. //
  998. // ChangeDrive set CurDrvDir in addition to changing the drive
  999. ChangeDrive(DriveNum);
  1000. DEBUG((MNGRP,DPLVL,"FFAR: Drv chng to %ws", CurDrvDir));
  1001. return(SUCCESS);
  1002. }
  1003. //
  1004. // Note that if the cmdline contains a drivespec, no attempt is made at
  1005. // internal command matching whatsoever.
  1006. //
  1007. return(ExtCom(pcmdnode));
  1008. }
  1009. //
  1010. // The sequence below works as follows:
  1011. // - A match between the previously-parsed first non-delimiter character
  1012. // group in the cmdline and the command table is attempted. A match
  1013. // sets JmpTblIdx to the command index; no match sets JmpTblIdx to -1.
  1014. // - FixCom is then called, and using the value of 'i', it detects cases
  1015. // of internal commands only (i == -1) which have no standard delimiter
  1016. // (whitespace or "=;,") between them and their arguments such as the
  1017. // "cd\foo". Note that a file foo.exe in subdirectory "cd" cannot be
  1018. // executed except through full path or drive specification. FixCom
  1019. // actually fixes up the cmdline and argptr fields of the node.
  1020. // - The command is then executed using ExtCom (i == -1) or the internal
  1021. // function indicated by the index
  1022. //
  1023. // Added second clause to detect REM commands which were parsed incorrectly
  1024. // as CMDTYP due to semi-delimiter characters appended. If REM, we know
  1025. // its OK, so just return success. If any other of the special types,
  1026. // FOR, DET, EXT, etc., allow to continue and fail in ExtCom since they
  1027. // weren'tparsed correctly and will bomb.
  1028. //
  1029. JmpTblIdx = FindAndFix( pcmdnode, (PTCHAR )&cflags );
  1030. DEBUG((MNGRP, DPLVL, "FFAR: After FixCom pcmdnode->cmdline = '%ws'", pcmdnode->cmdline));
  1031. //
  1032. // Check if it was not an internal command, if so then exec it
  1033. //
  1034. if (JmpTblIdx == -1) {
  1035. DEBUG((MNGRP, DPLVL, "FFAR: Calling ExtCom on %ws", pcmdnode->cmdline));
  1036. return(ExtCom(pcmdnode));
  1037. }
  1038. //
  1039. // CMD was found in table. If function field is NULL as in the
  1040. // case of REM, this is a dummy entry and must return SUCCESS.
  1041. //
  1042. if ((funcptr = GetFuncPtr(JmpTblIdx)) == NULL) {
  1043. DEBUG((MNGRP, DPLVL, "FFAR: Found internal with NULL entry"));
  1044. DEBUG((MNGRP, DPLVL, " Returning SUCESS"));
  1045. return(SUCCESS);
  1046. }
  1047. //
  1048. // If the command is supposed to have the drivespecs on its args
  1049. // validated before the command is executed, do it. If the command
  1050. // is not allowed toto contain switches and it has one, complain.
  1051. //
  1052. //
  1053. // Set up extra delimiter for seperating out switches
  1054. //
  1055. cname[0] = SwitChar;
  1056. cname[1] = NULLC;
  1057. pszTokStr = TokStr(pcmdnode->argptr, cname, TS_SDTOKENS);
  1058. // this hack to allow environment variables to contain /?
  1059. if (JmpTblIdx != SETTYP || !pszTokStr || (_tcsncmp(pszTokStr,TEXT("/\0?"),4) == 0)) {
  1060. // this is to exclude START command
  1061. if (JmpTblIdx != STRTTYP) {
  1062. if (CheckHelpSwitch(JmpTblIdx, pszTokStr) ) {
  1063. return( FAILURE );
  1064. }
  1065. }
  1066. }
  1067. DEBUG((MNGRP, DPLVL, "FFAR: Internal command, about to validate args"));
  1068. for (;(pszTokStr != NULL) && *pszTokStr; pszTokStr += mystrlen(pszTokStr)+1) {
  1069. cbTokStr = mystrlen(pszTokStr);
  1070. mystrcpy( pszTokStr, StripQuotes( pszTokStr ) );
  1071. DEBUG((MNGRP, DPLVL, "FFAR: Checking args; arg = %ws", pszTokStr));
  1072. if ((cflags & CHECKDRIVES) && *(pszTokStr+1) == COLON) {
  1073. if (!IsValidDrv(*pszTokStr)) {
  1074. PutStdErr(ERROR_INVALID_DRIVE, NOARGS);
  1075. return(LastRetCode = FAILURE);
  1076. } else {
  1077. //
  1078. // If not the copy command (A->B B->A swaps)
  1079. // then check if drive is locked
  1080. // if drive locked then
  1081. // display error return code message
  1082. // terminate this command's processing
  1083. //
  1084. if (JmpTblIdx != CPYTYP) {
  1085. if ( IsDriveLocked(*pszTokStr)) {
  1086. PutStdErr( GetLastError() , NOARGS);
  1087. return(LastRetCode = FAILURE);
  1088. }
  1089. }
  1090. }
  1091. }
  1092. if ((cflags & NOSWITCHES) && (pszTokStr != NULL) && *pszTokStr == SwitChar) {
  1093. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  1094. return(LastRetCode = FAILURE);
  1095. }
  1096. }
  1097. DEBUG((MNGRP, DPLVL, "FFAR: calling function, cmd = `%ws'", pcmdnode->cmdline));
  1098. //
  1099. // Call internal routine to execute the command
  1100. //
  1101. if ((pszTitle = GetTitle(pcmdnode)) != NULL) {
  1102. SetConTitle(pszTitle);
  1103. }
  1104. rc = (*funcptr)(pcmdnode);
  1105. ResetConTitle(pszTitleCur);
  1106. return(rc);
  1107. }
  1108. int
  1109. FindAndFix (
  1110. IN struct cmdnode *pcmdnode,
  1111. IN PTCHAR pbCmdFlags
  1112. )
  1113. /*++
  1114. Routine Description:
  1115. This routine separates the command and its following
  1116. switch character if there is no space between the
  1117. command and the switch character.
  1118. This routine is used for both left side and right side
  1119. of PIPE.
  1120. Arguments:
  1121. pcmdnode - pointer to node the contains command to locate
  1122. pbCmdFlags -
  1123. Return Value:
  1124. --*/
  1125. {
  1126. TCHAR chCur; // current character we are looking at
  1127. TCHAR rgchCmdStr[MAX_PATH];
  1128. PTCHAR pszArgT; // Temp. used to build a new arguemt string
  1129. ULONG JmpTableIdx; // index into jump table of function pointers
  1130. ULONG iCmdStr; // index into command string
  1131. LONG iDelim5CmdStr; // index into command string
  1132. ULONG cbCmdStr; // length of command string
  1133. DWORD dwFileAttr;
  1134. BOOLEAN fQuoteFound, fQuoteFound2;
  1135. BOOLEAN fDone;
  1136. fQuoteFound = FALSE;
  1137. fQuoteFound2 = FALSE;
  1138. iDelim5CmdStr = -1;
  1139. //
  1140. // Extract only commnand from the command string (pcmdnode->cmdline)
  1141. //
  1142. for (iCmdStr = 0; iCmdStr < MAX_PATH-1; iCmdStr++) {
  1143. chCur = *(pcmdnode->cmdline + iCmdStr);
  1144. //
  1145. // If we found a quote invert the current quote state
  1146. // for both first quote (fQuoteFound) and end quote (fQuoteFound2)
  1147. //
  1148. if ( chCur == QUOTE ) {
  1149. fQuoteFound = (BOOLEAN)!fQuoteFound;
  1150. fQuoteFound2 = (BOOLEAN)!fQuoteFound;
  1151. }
  1152. //
  1153. // If we have a character and
  1154. // have found either a begin or end quote or cur char is not delimiter
  1155. // and cur char is not a special (+[] etc.) delimiter
  1156. //
  1157. if ((chCur) &&
  1158. ( fQuoteFound || fQuoteFound2 || !mystrchr(Delim4,chCur))) {
  1159. if (iDelim5CmdStr == -1 && mystrchr(Delim5,chCur)) {
  1160. //
  1161. // If extensions not enabled, then path characters terminate
  1162. // the scan
  1163. if (!fEnableExtensions)
  1164. break;
  1165. iDelim5CmdStr = iCmdStr;
  1166. }
  1167. rgchCmdStr[iCmdStr] = chCur;
  1168. fQuoteFound2 = FALSE;
  1169. }
  1170. else {
  1171. break;
  1172. }
  1173. }
  1174. if (iCmdStr == 0) {
  1175. return -1;
  1176. }
  1177. //
  1178. // Null terminate command name. If a path delimiter was found somewhere
  1179. // in the command name, then see if the whole command name is the name of
  1180. // an existing file. If so, then that is the command, which will launch
  1181. // the file through its association
  1182. //
  1183. rgchCmdStr[iCmdStr] = NULLC;
  1184. if (iDelim5CmdStr != -1 &&
  1185. ((dwFileAttr = GetFileAttributes(rgchCmdStr)) == -1 ||
  1186. (dwFileAttr & FILE_ATTRIBUTE_DIRECTORY)
  1187. )
  1188. ) {
  1189. iCmdStr = iDelim5CmdStr;
  1190. rgchCmdStr[iCmdStr] = NULLC;
  1191. }
  1192. //
  1193. // See if command is in jump table (is an internal command)
  1194. // If it is not found amoung the normal internal command
  1195. // check amoung the special parse type if it was a comment
  1196. //
  1197. if ((JmpTableIdx = FindCmd(CMDHIGH, rgchCmdStr, pbCmdFlags)) == -1) {
  1198. if (FindCmd(CMDMAX, rgchCmdStr, pbCmdFlags) == REMTYP) {
  1199. return(REMTYP);
  1200. }
  1201. } else if (JmpTableIdx == GOTYP)
  1202. pcmdnode->flag = CMDNODE_FLAG_GOTO;
  1203. fQuoteFound = FALSE;
  1204. fQuoteFound2 = FALSE;
  1205. //
  1206. // If the command is not found, check the length of command string
  1207. // for the case of DBCS. Count the characters that are not white space
  1208. // remaining in command
  1209. if ( JmpTableIdx == -1 ) {
  1210. iCmdStr = 0;
  1211. fDone = FALSE;
  1212. while ( !fDone ) {
  1213. chCur = *(pcmdnode->cmdline+iCmdStr);
  1214. if ( chCur && chCur == QUOTE ) {
  1215. fQuoteFound = (BOOLEAN)!fQuoteFound;
  1216. fQuoteFound2 = (BOOLEAN)!fQuoteFound;
  1217. }
  1218. if ( chCur && ( fQuoteFound || fQuoteFound2 ||
  1219. !_istspace(chCur) &&
  1220. !mystrchr(Delimiters, chCur) &&
  1221. !(chCur == SwitChar))) {
  1222. iCmdStr++;
  1223. fQuoteFound2 = FALSE;
  1224. } else {
  1225. fDone = TRUE;
  1226. }
  1227. }
  1228. }
  1229. //
  1230. // If cmdstr contains more than command, strip of extra part
  1231. // and put it in front of the existing command argument pcmdnode-argptr
  1232. //
  1233. //
  1234. if (iCmdStr != (cbCmdStr = mystrlen(pcmdnode->cmdline))) {
  1235. int ArgLen;
  1236. ArgLen = mystrlen(pcmdnode->argptr);
  1237. ArgLen += cbCmdStr;
  1238. if (!(pszArgT = mkstr(ArgLen*sizeof(TCHAR)))) {
  1239. PutStdErr(MSG_NO_MEMORY, NOARGS);
  1240. Abort();
  1241. }
  1242. //
  1243. // create argument string and copy the 'extra' part of command
  1244. // it.
  1245. //
  1246. mystrcpy(pszArgT, pcmdnode->cmdline+iCmdStr);
  1247. //
  1248. // If we have a argument pointer stuff in the front
  1249. //
  1250. if (pcmdnode->argptr) {
  1251. mystrcat(pszArgT, pcmdnode->argptr);
  1252. }
  1253. pcmdnode->argptr = pszArgT;
  1254. *(pcmdnode->cmdline+iCmdStr) = NULLC;
  1255. }
  1256. return(JmpTableIdx);
  1257. }
  1258. int
  1259. UnParse(
  1260. IN struct node *pnode,
  1261. IN PTCHAR pbCmdBuf )
  1262. /*++
  1263. Routine Description:
  1264. Do setup and call UnBuild to deparse a node tree.
  1265. Arguments:
  1266. pnode - pointer to root of parse tree to UnParse
  1267. pbCmdBuf -
  1268. Uses Global pointer CBuf and assumes a string of MAXTOKLEN+1 bytes
  1269. has already been allocated to it (as done by Dispatch).
  1270. Return Value:
  1271. --*/
  1272. {
  1273. int rc;
  1274. DEBUG((MNGRP, DPLVL, "UNPRS: Entered"));
  1275. if (!pnode) {
  1276. DEBUG((MNGRP, DPLVL, "UNPRS: Found NULL node"));
  1277. return(FAILURE);
  1278. }
  1279. //
  1280. // Leave space in front of command for a /s
  1281. // Setup command buffer for a single command execution
  1282. //
  1283. mystrcpy( pbCmdBuf, TEXT( " /D /c\"" ));
  1284. //
  1285. // Setup to handle an exception during detach.
  1286. if (setjmp(CmdJBuf2)) {
  1287. DEBUG((MNGRP, DPLVL, "UNPRS: Longjmp return occurred!"));
  1288. return(FAILURE);
  1289. }
  1290. //
  1291. // DisAssemble the current command
  1292. //
  1293. rc = (UnBuild(pnode, pbCmdBuf));
  1294. mystrcat( pbCmdBuf, TEXT("\"") );
  1295. return( rc );
  1296. }
  1297. UnBuild(
  1298. IN struct node *pnode,
  1299. IN PTCHAR pbCmdBuf
  1300. )
  1301. /*++
  1302. Routine Description:
  1303. Recursively take apart a parse tree of nodes, building a string of
  1304. their components.
  1305. Arguments:
  1306. pnode - root of parse tree to UnBuild
  1307. pbCmdBuf - Where to put UnBuilt command
  1308. Return Value:
  1309. --*/
  1310. {
  1311. //
  1312. // Different kinds of nodes to Unbuild
  1313. //
  1314. struct cmdnode *pcmdnode;
  1315. struct fornode *pfornode;
  1316. struct ifnode *pifnode;
  1317. PTCHAR op;
  1318. DEBUG((MNGRP, DPLVL, "UNBLD: Entered"));
  1319. switch (pnode->type) {
  1320. case LFTYP:
  1321. case CSTYP:
  1322. case ORTYP:
  1323. case ANDTYP:
  1324. case PIPTYP:
  1325. case PARTYP:
  1326. case SILTYP:
  1327. DEBUG((MNGRP, DPLVL, "UNBLD: Found OPERATOR"));
  1328. UnDuRd(pnode, pbCmdBuf);
  1329. switch (pnode->type) {
  1330. case LFTYP:
  1331. case CSTYP:
  1332. op = CSSTR;
  1333. break;
  1334. case ORTYP:
  1335. op = ORSTR;
  1336. break;
  1337. case ANDTYP:
  1338. op = ANDSTR;
  1339. break;
  1340. case PIPTYP:
  1341. op = PIPSTR;
  1342. break;
  1343. case PARTYP:
  1344. SPutC(pbCmdBuf, LEFTPSTR,YSPC);
  1345. op = RPSTR;
  1346. break;
  1347. case SILTYP:
  1348. SPutC(pbCmdBuf, SILSTR,YSPC);
  1349. op = SPCSTR;
  1350. break;
  1351. }
  1352. //
  1353. // Recurse down undoing the left hand side
  1354. //
  1355. UnBuild(pnode->lhs, pbCmdBuf);
  1356. //
  1357. // Now that left side there copy in operator and do right side
  1358. //
  1359. SPutC(pbCmdBuf, op,YSPC);
  1360. if (pnode->type != PARTYP && pnode->type != SILTYP)
  1361. UnBuild(pnode->rhs, pbCmdBuf);
  1362. break;
  1363. case FORTYP:
  1364. DEBUG((MNGRP, DPLVL, "UNBLD: Found FORTYP"));
  1365. pfornode = (struct fornode *) pnode;
  1366. //
  1367. // Put in the FOR keyword, arguements and list
  1368. //
  1369. SPutC( pbCmdBuf, ForStr,YSPC);
  1370. if (fEnableExtensions) {
  1371. if (pfornode->flag & FOR_LOOP) {
  1372. SPutC( pbCmdBuf, ForLoopStr,YSPC);
  1373. }
  1374. else
  1375. if (pfornode->flag & FOR_MATCH_DIRONLY) {
  1376. SPutC( pbCmdBuf, ForDirTooStr,YSPC);
  1377. }
  1378. else
  1379. if (pfornode->flag & FOR_MATCH_PARSE) {
  1380. SPutC( pbCmdBuf, ForParseStr,YSPC);
  1381. if (pfornode->parseOpts)
  1382. SPutC( pbCmdBuf, pfornode->parseOpts,YSPC);
  1383. }
  1384. else
  1385. if (pfornode->flag & FOR_MATCH_RECURSE) {
  1386. SPutC( pbCmdBuf, ForRecurseStr,YSPC);
  1387. if (pfornode->recurseDir)
  1388. SPutC( pbCmdBuf, pfornode->recurseDir,YSPC);
  1389. }
  1390. }
  1391. SPutC( pbCmdBuf, pfornode->cmdline+_tcslen(ForStr),YSPC);
  1392. SPutC( pbCmdBuf, LEFTPSTR,YSPC);
  1393. SPutC( pbCmdBuf, pfornode->arglist,NSPC);
  1394. SPutC( pbCmdBuf, RPSTR,NSPC);
  1395. SPutC( pbCmdBuf, pfornode->cmdline+DOPOS,YSPC);
  1396. //
  1397. // Now get the for body
  1398. //
  1399. UnBuild(pfornode->body, pbCmdBuf);
  1400. break;
  1401. case IFTYP:
  1402. DEBUG((MNGRP, DPLVL, "UNBLD: Found IFTYP"));
  1403. //
  1404. // put ine IF keyword
  1405. pifnode = (struct ifnode *) pnode;
  1406. SPutC( pbCmdBuf, pifnode->cmdline,YSPC);
  1407. op = NULL;
  1408. if (pifnode->cond->type != NOTTYP) {
  1409. if (pifnode->cond->flag == CMDNODE_FLAG_IF_IGNCASE)
  1410. op = TEXT("/I");
  1411. }
  1412. else
  1413. if (((struct cmdnode *)(pifnode->cond->argptr))->flag == CMDNODE_FLAG_IF_IGNCASE)
  1414. op = TEXT("/I");
  1415. if (op)
  1416. SPutC( pbCmdBuf, op,YSPC);
  1417. //
  1418. // Get the condition part of the statement
  1419. //
  1420. UnBuild((struct node *)pifnode->cond, pbCmdBuf);
  1421. //
  1422. // Unbuild the body of the IF
  1423. //
  1424. UnBuild(pifnode->ifbody, pbCmdBuf);
  1425. if (pifnode->elsebody) {
  1426. SPutC( pbCmdBuf, pifnode->elseline,YSPC);
  1427. UnBuild(pifnode->elsebody, pbCmdBuf);
  1428. }
  1429. break;
  1430. case NOTTYP:
  1431. DEBUG((MNGRP, DPLVL, "UNBLD: Found NOTTYP"));
  1432. pcmdnode = (struct cmdnode *) pnode;
  1433. SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC);
  1434. UnBuild((struct node *)pcmdnode->argptr, pbCmdBuf);
  1435. break;
  1436. case REMTYP:
  1437. case CMDTYP:
  1438. case ERRTYP:
  1439. case EXSTYP:
  1440. case DEFTYP:
  1441. case CMDVERTYP:
  1442. DEBUG((MNGRP, DPLVL, "UNBLD: Found CMDTYP"));
  1443. pcmdnode = (struct cmdnode *) pnode;
  1444. SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC);
  1445. if (pcmdnode->argptr)
  1446. SPutC( pbCmdBuf, pcmdnode->argptr,NSPC);
  1447. UnDuRd((struct node *)pcmdnode, pbCmdBuf);
  1448. break;
  1449. case CMPTYP:
  1450. case STRTYP:
  1451. pcmdnode = (struct cmdnode *) pnode;
  1452. op = TEXT("== ");
  1453. //
  1454. // If extensions are enabled, handle displaying the
  1455. // new forms of comparison operators.
  1456. //
  1457. if (fEnableExtensions) {
  1458. if (pcmdnode->cmdarg == CMDNODE_ARG_IF_EQU)
  1459. op = TEXT("EQU ");
  1460. else
  1461. if (pcmdnode->cmdarg == CMDNODE_ARG_IF_NEQ)
  1462. op = TEXT("NEQ ");
  1463. else
  1464. if (pcmdnode->cmdarg == CMDNODE_ARG_IF_LSS)
  1465. op = TEXT("LSS ");
  1466. else
  1467. if (pcmdnode->cmdarg == CMDNODE_ARG_IF_LEQ)
  1468. op = TEXT("LEQ ");
  1469. else
  1470. if (pcmdnode->cmdarg == CMDNODE_ARG_IF_GTR)
  1471. op = TEXT("GTR ");
  1472. else
  1473. if (pcmdnode->cmdarg == CMDNODE_ARG_IF_GEQ)
  1474. op = TEXT("GEQ ");
  1475. }
  1476. SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC);
  1477. SPutC( pbCmdBuf, op, NSPC);
  1478. if (pcmdnode->argptr)
  1479. SPutC( pbCmdBuf, pcmdnode->argptr,NSPC);
  1480. UnDuRd((struct node *)pcmdnode, pbCmdBuf);
  1481. break;
  1482. case HELPTYP:
  1483. DEBUG((MNGRP, DPLVL, "UNBLD: Found HELPTYP"));
  1484. if (LastMsgNo == MSG_HELP_FOR) {
  1485. SPutC( pbCmdBuf, TEXT("FOR /?"), YSPC);
  1486. }
  1487. else if (LastMsgNo == MSG_HELP_IF) {
  1488. SPutC( pbCmdBuf, TEXT("IF /?"), YSPC);
  1489. }
  1490. else if (LastMsgNo == MSG_HELP_REM) {
  1491. SPutC( pbCmdBuf, TEXT("REM /?"), YSPC);
  1492. }
  1493. else {
  1494. DEBUG((MNGRP, DPLVL, "UNBLD: Unknown Type!"));
  1495. longjmp(CmdJBuf2,-1);
  1496. }
  1497. break;
  1498. default:
  1499. DEBUG((MNGRP, DPLVL, "UNBLD: Unknown Type!"));
  1500. longjmp(CmdJBuf2,-1);
  1501. }
  1502. return(SUCCESS);
  1503. }
  1504. void
  1505. UnDuRd(
  1506. IN struct node *pnode,
  1507. IN PTCHAR pbCmdBuf
  1508. )
  1509. /*++
  1510. Routine Description:
  1511. Unparse any input or output redirection associated with the
  1512. current node.
  1513. Arguments:
  1514. pnode - current parse tree node
  1515. pbCmdBuf - buffer holding command
  1516. Return Value:
  1517. --*/
  1518. {
  1519. struct relem *prelem;
  1520. TCHAR tmpstr[2];
  1521. DEBUG((MNGRP, DPLVL, "UNDURD: Entered"));
  1522. tmpstr[1] = NULLC;
  1523. prelem = pnode->rio;
  1524. while (prelem) {
  1525. //
  1526. // this makes big time assumption about size of handle
  1527. //
  1528. tmpstr[0] = (TCHAR)prelem->rdhndl + (TCHAR)'0';
  1529. SPutC( pbCmdBuf, tmpstr,YSPC);
  1530. if (prelem->rdop == INOP)
  1531. SPutC( pbCmdBuf, INSTR,NSPC);
  1532. else
  1533. SPutC( pbCmdBuf, prelem->flag ? APPSTR : OUTSTR,NSPC);
  1534. SPutC( pbCmdBuf, prelem->fname,NSPC);
  1535. prelem = prelem->nxt;
  1536. }
  1537. }
  1538. void SPutC(
  1539. IN PTCHAR pbCmdBuf,
  1540. IN PTCHAR pszInput,
  1541. IN int flg
  1542. )
  1543. /*++
  1544. Routine Description:
  1545. If within length limits, add the current substring to the
  1546. command under construction delimiting with a space.
  1547. Arguments:
  1548. pbCmdBuf - Where to put string
  1549. pszInputString - String to put in pbCmdBuf
  1550. flg - Flags controling placement of spaces
  1551. Return Value:
  1552. --*/
  1553. {
  1554. DEBUG((MNGRP, DPLVL, "SPutC: Entered, Adding '%ws'",pszInput));
  1555. if ((mystrlen(pbCmdBuf) + mystrlen(pszInput) + 1) > MAXTOKLEN) {
  1556. PutStdErr(MSG_LINES_TOO_LONG, NOARGS);
  1557. longjmp(CmdJBuf2,-1);
  1558. }
  1559. if (flg && (*(pbCmdBuf+mystrlen(pbCmdBuf)-1) != SPACE) && (*pszInput != SPACE)) {
  1560. SpaceCat(pbCmdBuf,pbCmdBuf,pszInput);
  1561. } else {
  1562. mystrcat(pbCmdBuf,pszInput);
  1563. }
  1564. }
  1565. /*** DelayedEnvVarSub - controls execution time substitution of environment variables.
  1566. *
  1567. * Purpose:
  1568. * Examine a parse tree node and make delayed environment variable substitutions
  1569. * for those fields in the node that we care about. Don't need to walk
  1570. * into child parse nodes, as they will go back through Dispatch when
  1571. * executed and hence to us here.
  1572. *
  1573. * int DelayedEnvVarSub(struct node *n)
  1574. *
  1575. * Args:
  1576. * n - pointer to the statement subtree in which the substitutions are
  1577. * to be made
  1578. * save - place to save original strings, if we change any
  1579. * bRestore - TRUE if we are restoring original strings from save parameter
  1580. * instead of doing substitution.
  1581. *
  1582. * Returns:
  1583. * SUCCESS if all goes well.
  1584. * FAILURE if an oversized command is found.
  1585. *
  1586. * Note:
  1587. * The variables to be substituted for are found the current environment
  1588. * block. Only variables names surrounded by exclamation marks will be
  1589. * substituted (e.g. !varname!). Actual substitution is done by DESubWork
  1590. * routine.
  1591. *
  1592. */
  1593. int DelayedEnvVarSub(n, save, bRestore)
  1594. struct cmdnode *n;
  1595. struct savtype *save;
  1596. BOOLEAN bRestore;
  1597. {
  1598. int j; /* Temps used to make substitutions... */
  1599. struct relem *io; /* M017 - Pointer to redir list */
  1600. if (!n)
  1601. return(SUCCESS);
  1602. switch (n->type) {
  1603. case LFTYP:
  1604. case CSTYP:
  1605. case ORTYP:
  1606. case ANDTYP:
  1607. case PIPTYP:
  1608. case PARTYP:
  1609. case SILTYP:
  1610. for (j=0, io=n->rio; j < 10 && io; j++, io=io->nxt) {
  1611. if (DESubWork(bRestore, &io->fname, &save->saveptrs[j]))
  1612. return(FAILURE);
  1613. }
  1614. return(SUCCESS);
  1615. case FORTYP:
  1616. if (DESubWork(bRestore, &((struct fornode *) n)->arglist, &save->saveptrs[0]))
  1617. return(FAILURE);
  1618. return(SUCCESS);
  1619. case IFTYP:
  1620. n = ((struct ifnode *)n)->cond;
  1621. if (n->type == NOTTYP)
  1622. n = (struct cmdnode *)n->argptr;
  1623. if (DESubWork(bRestore, &n->cmdline, &save->saveptrs[0]))
  1624. return(FAILURE);
  1625. if (DESubWork(bRestore, &n->argptr, &save->saveptrs[1]))
  1626. return(FAILURE);
  1627. return(SUCCESS);
  1628. case REMTYP:
  1629. case CMDTYP:
  1630. case CMDVERTYP:
  1631. case ERRTYP:
  1632. case DEFTYP:
  1633. case EXSTYP:
  1634. case STRTYP:
  1635. case CMPTYP:
  1636. if (DESubWork(bRestore, &n->cmdline, &save->saveptrs[0]) ||
  1637. DESubWork(bRestore, &n->argptr, &save->saveptrs[1]))
  1638. return(FAILURE);
  1639. for (j=2, io=n->rio; j < 12 && io; j++, io=io->nxt) {
  1640. if (DESubWork(bRestore, &io->fname, &save->saveptrs[j]))
  1641. return(FAILURE);
  1642. }
  1643. return(SUCCESS);
  1644. }
  1645. return(SUCCESS);
  1646. }
  1647. /*** DESubWork - does runtime environment variable substitutions
  1648. *
  1649. * Purpose:
  1650. * Make environment variable substitutions for those references in the
  1651. * passed string. References are identified by valid environment variable
  1652. * names bracketed by exclamation marks (e.g. !PATH!). If the source
  1653. * string is modified, a copy of the original is saved in the save
  1654. * parameter.
  1655. *
  1656. * DESubWork(BOOLEAN bRestore, TCHAR **src, TCHAR **save)
  1657. *
  1658. * Args:
  1659. * bRestore - TRUE if we are restoring original strings from save parameter
  1660. * instead of doing substitution.
  1661. * src - the string being examined
  1662. * save - pointer to where to save *src if *src modified.
  1663. *
  1664. * Returns:
  1665. * SUCCESS if substitutions could be made.
  1666. * FAILURE if the new string is too long.
  1667. *
  1668. * Notes:
  1669. *
  1670. */
  1671. DESubWork(bRestore, src, save)
  1672. BOOLEAN bRestore;
  1673. TCHAR **src;
  1674. TCHAR **save;
  1675. {
  1676. TCHAR *dest;
  1677. TCHAR *dststr;
  1678. TCHAR *srcstr, *srcpy, *substr, c;
  1679. int dlen; /* Length of dest string */
  1680. int slen; /* Length of src string used */
  1681. int sslen; /* Length of substr */
  1682. DEBUG((BPGRP, FOLVL, "SFW: Entered."));
  1683. //
  1684. // If we've performed some substitutions and are restoring
  1685. // the original strings
  1686. //
  1687. if (bRestore) {
  1688. //
  1689. // If we've saved something then we have work to do
  1690. //
  1691. if (*save != NULL) {
  1692. //
  1693. // If we have a substitution that we've made, then we
  1694. // must free this string
  1695. //
  1696. if (*src != NULL) {
  1697. FreeStr( *src );
  1698. }
  1699. //
  1700. // If the original was saved, then we need to restore it.
  1701. //
  1702. if (*save != NULL)
  1703. *src = *save;
  1704. else
  1705. *save = NULL;
  1706. }
  1707. return(SUCCESS);
  1708. }
  1709. srcpy = *src;
  1710. //
  1711. // If there's no source or there's no delayed-sub char !
  1712. // then we have nothing to do
  1713. //
  1714. if (srcpy == NULL || !_tcschr(srcpy, TEXT('!'))) {
  1715. return(SUCCESS);
  1716. }
  1717. //
  1718. // Create a substitution string
  1719. //
  1720. if (!(dest = mkstr( (MAXTOKLEN+1)*sizeof(TCHAR)))) {
  1721. return(FAILURE);
  1722. }
  1723. srcstr = srcpy;
  1724. dststr = dest;
  1725. dlen = 0;
  1726. //
  1727. // Walk through the source expanding each found environment variable
  1728. //
  1729. while (TRUE) {
  1730. //
  1731. // If we have produced a token that's too long, break out
  1732. //
  1733. if (dlen > MAXTOKLEN) {
  1734. break;
  1735. }
  1736. //
  1737. // Get the next character from the input
  1738. //
  1739. c = *srcstr++;
  1740. if (c == TEXT('\0')) {
  1741. break;
  1742. }
  1743. //
  1744. // See if we have a exclamation character indicating a variable
  1745. // reference. Process the environment variable when we see it.
  1746. //
  1747. if (c == TEXT('!')) {
  1748. //
  1749. // Perform complex substitution
  1750. //
  1751. substr = MSEnvVar( NULL, srcstr, &slen, c );
  1752. //
  1753. // If we were able to generate a substitution, do a length
  1754. // check, append the string, and then advance over the
  1755. // source of the substitution
  1756. //
  1757. if (substr != NULL) {
  1758. sslen = mystrlen( substr );
  1759. dlen += sslen;
  1760. if (dlen > MAXTOKLEN) {
  1761. break;
  1762. }
  1763. _tcscpy( dststr, substr );
  1764. dststr += sslen;
  1765. srcstr += slen;
  1766. //
  1767. // No substitution was possible, if we're in a batch file
  1768. // simply skip over the source
  1769. //
  1770. } else if (CurrentBatchFile) {
  1771. srcstr += slen;
  1772. //
  1773. // WEIRD: No substitution, no batch file, just copy the % char and keep
  1774. // on processing
  1775. //
  1776. } else {
  1777. *dststr++ = c;
  1778. dlen++;
  1779. }
  1780. } else {
  1781. //
  1782. // Non-exclamation. If this is a quote and there's a next character, use it.
  1783. // No next character is end of parsing
  1784. //
  1785. if (c == TEXT( '^' )) {
  1786. c = *srcstr++;
  1787. if (c == TEXT( '\0' )) {
  1788. break;
  1789. }
  1790. }
  1791. //
  1792. // Copy in the single character
  1793. //
  1794. *dststr++ = c;
  1795. dlen++;
  1796. }
  1797. }
  1798. //
  1799. // If we've gotten too long then free the string and bail
  1800. //
  1801. if (dlen > MAXTOKLEN) {
  1802. FreeStr( dest );
  1803. return(FAILURE);
  1804. }
  1805. *save = srcpy;
  1806. if (!(*src = resize( dest, (dlen+1)*sizeof(TCHAR*)))) {
  1807. FreeStr( dest );
  1808. return(FAILURE);
  1809. }
  1810. return(SUCCESS);
  1811. }
  1812. //
  1813. // Queries for cmd policy
  1814. //
  1815. // 0 = no policy, normal operation
  1816. // 1 = completely disabled
  1817. // 2 = interactive prompt disabled, but scripts allowed to run
  1818. //
  1819. VOID GetCmdPolicy(INT * iDisabled)
  1820. {
  1821. DWORD dwSize, dwType;
  1822. HKEY hKey;
  1823. //
  1824. // Set default
  1825. //
  1826. *iDisabled = CMD_POLICY_NORMAL;
  1827. if (RegOpenKeyEx (HKEY_CURRENT_USER, TEXT("Software\\Policies\\Microsoft\\Windows\\System"),
  1828. 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
  1829. dwSize = sizeof(INT);
  1830. RegQueryValueEx (hKey, TEXT("DisableCMD"), NULL, &dwType,
  1831. (LPBYTE) iDisabled, &dwSize);
  1832. RegCloseKey (hKey);
  1833. }
  1834. }
  1835. /*++
  1836. Routine Description:
  1837. This routine dumps string data
  1838. Arguments:
  1839. Bytes - Points to bytes to be dumped
  1840. Length - length of bytes to dump. -1 means dump up to the first zero byte
  1841. Return Value:
  1842. None.
  1843. --*/
  1844. void
  1845. DumpBytes(
  1846. PBYTE Bytes,
  1847. ULONG Length
  1848. )
  1849. {
  1850. ULONG i;
  1851. if (Length == -1) {
  1852. Length = strlen( Bytes );
  1853. }
  1854. for (i = 0; i < Length; i++) {
  1855. if ((i%16) == 0) {
  1856. printf( "\n%04x: ", i );
  1857. }
  1858. printf( " %02x", Bytes[i] & 0xFF );
  1859. }
  1860. if (Length != 0) {
  1861. printf( "\n" );
  1862. }
  1863. }
  1864. void
  1865. DumpTchars(
  1866. PTCHAR Chars,
  1867. ULONG Length
  1868. )
  1869. {
  1870. ULONG i;
  1871. if (Length == -1) {
  1872. Length = _tcslen( Chars );
  1873. }
  1874. for (i = 0; i < Length; i++) {
  1875. if ((i%16) == 0) {
  1876. printf( "\n%04x: ", i );
  1877. }
  1878. if (sizeof( TCHAR ) == 1) {
  1879. printf( " %02x", Chars[i] & 0xFF );
  1880. } else {
  1881. printf( " %04x", Chars[i] & 0xFFFF );
  1882. }
  1883. }
  1884. if (Length != 0) {
  1885. printf( "\n" );
  1886. }
  1887. }