Windows NT 4.0 source code leak
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.

1833 lines
47 KiB

4 years ago
  1. #include "cmd.h"
  2. //
  3. // Used in rebuilding command lines for display
  4. //
  5. #define NSPC 0 // Don't use space
  6. #define YSPC 1 // Do use space
  7. extern CPINFO CurrentCPInfo;
  8. extern UINT CurrentCP;
  9. extern ULONG LastMsgNo;
  10. //
  11. // Jump buffers used to return to main loop after some error condition
  12. //
  13. jmp_buf MainEnv ; // SigHand() uses to return to main
  14. jmp_buf CmdJBuf1 ; // Both of these buffers are used by
  15. jmp_buf CmdJBuf2 ; // various parts of Command for error
  16. //
  17. // rioCur points to a linked list of rio structures dynamically
  18. // allocated when redirection is performed. Note that memory is automatically
  19. // freed when Dispatch completed work. rioCur points to last entry.
  20. //
  21. struct rio *rioCur = NULL ;
  22. //
  23. // Retrun code for last external program
  24. //
  25. int LastRetCode ;
  26. //
  27. // Constants used in parsing
  28. //
  29. extern TCHAR PathChar;
  30. extern TCHAR SwitChar;
  31. extern TCHAR Delimiters[];
  32. extern TCHAR Delim2[];
  33. //
  34. // Current Drive:Directory. Set in ChDir
  35. // It it is change temp. SaveDir used used to old original
  36. //
  37. extern TCHAR CurDrvDir[];
  38. //
  39. // Name of NULL device. Used to output to nothing
  40. //
  41. extern TCHAR DevNul[];
  42. //
  43. // flag if we found a label that was target of goto
  44. //
  45. extern BOOLEAN GotoFlag;
  46. //
  47. // Number of elements in Data stack
  48. //
  49. extern ULONG DCount ;
  50. //
  51. // Environment string to locate command shell.
  52. //
  53. extern TCHAR ComSpecStr[] ;
  54. //
  55. // DOS error code
  56. //
  57. extern unsigned DosErr ;
  58. //
  59. // Data for start and executing a batch file. Used in calls
  60. //
  61. extern struct batdata *CurBat ;
  62. //
  63. // Set of /K switch on command line set.
  64. //
  65. extern BOOLEAN fSingleBatchLine;
  66. //
  67. // Set of /c switch on command line set.
  68. //
  69. extern BOOLEAN fSingleCmdLine;
  70. //
  71. // Alternative path (DDPATH) to search
  72. //
  73. extern TCHAR AppendStr[] ;
  74. //
  75. // flag if control-c was seen
  76. //
  77. extern BOOL CtrlCSeen;
  78. extern BOOLEAN fPrintCtrlC;
  79. extern PTCHAR pszTitleCur;
  80. extern BOOLEAN fTitleChanged;
  81. //
  82. // Prototypes
  83. //
  84. PTCHAR
  85. Init();
  86. int
  87. (*GetFuncPtr())() ;
  88. PTCHAR
  89. GetEnvVar() ;
  90. PTCHAR
  91. EatWS() ;
  92. int
  93. UnParse(struct node *, PTCHAR);
  94. int
  95. UnBuild(struct node *, PTCHAR);
  96. void
  97. UnDuRd(struct node *, PTCHAR);
  98. void
  99. SPutC(PTCHAR, PTCHAR,int );
  100. PTCHAR
  101. argstr1();
  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. _CRTAPI1
  121. main()
  122. /*++
  123. Routine Description:
  124. Main entry point for command interpreter
  125. Arguments:
  126. Command line:
  127. /P - Permanent Command. Set permanent CMD flag.
  128. /C - Single command. Build a command line out of the rest of
  129. the args and pass it back to Init.
  130. /K - Same as /C but also set fSingleBatchLine flag.
  131. /Q - No echo
  132. Return Value:
  133. Return: 0 - If success
  134. 1 - Parsing Error
  135. 0xFF - Could not init
  136. n - Return code from command
  137. --*/
  138. {
  139. CHAR VarOnStack;
  140. struct node *pnodeCmdTree ;
  141. //
  142. // When in multi-cmd mode tells parser where to get input from.
  143. //
  144. int InputType ;
  145. //
  146. // Pointer to current command line
  147. //
  148. PTCHAR pszCmdLine;
  149. //
  150. // flag used when a setjmp returns while processing /K
  151. // error and move to next line.
  152. //
  153. unsigned fIgnore = FALSE;
  154. unsigned ReturnCode;
  155. //
  156. // flChkStack is turned ON initially here and it stays ON while
  157. // I believe the information returned by ChkStack() is correct.
  158. //
  159. // It is turned OFF the first time I don't believe that info and
  160. // therefore I don't want to make any decisions changing the CMD's
  161. // behavior.
  162. //
  163. // It will stay OFF until CMD terminates so we will never check
  164. // stack usage again.
  165. //
  166. // I implemented one method to prevent CMD.EXE from the stack overflow:
  167. // Have count and limit of recursion in batch file processing and check
  168. // stack every time we exceed the limit of recursion until we reach 90%
  169. // of stack usage.
  170. // If (stack usage >= 90% of 1 MByte) then terminate batch file
  171. // unconditionally and handle such termination properly (freeing memory
  172. // and stack and saving CMD.EXE)
  173. //
  174. // It is also possible to implement SEH but then we won't know about
  175. // CMD problems.
  176. //
  177. flChkStack = 1;
  178. FixedPtrOnStack = (VOID *) &VarOnStack; // to be used in ChkStack()
  179. if ( ChkStack (FixedPtrOnStack, &GlStackUsage) == FAILURE ) {
  180. flChkStack = 0;
  181. }
  182. //
  183. // Initialize the DBCS lead byte table based on the current locale.
  184. //
  185. InitializeDbcsLeadCharTable( );
  186. //
  187. // Set base APIs to operate in OEM mode
  188. //
  189. #ifndef UNICODE
  190. SetFileApisToOEM();
  191. #endif /* Unicode */
  192. SetTEBLangID();
  193. //
  194. // Init returns NULL when not in single command mode
  195. // (i.e. /c or /k)
  196. //
  197. if ((pszCmdLine = Init()) != NULL) {
  198. if (setjmp(MainEnv)) {
  199. //
  200. // If processing /K and setjmp'd out of init. then ignore
  201. //
  202. if ( fSingleBatchLine ){
  203. fIgnore = TRUE;
  204. } else {
  205. CMDexit(0xff) ;
  206. }
  207. }
  208. if ( !fIgnore ){
  209. DEBUG((MNGRP, MNLVL, "MAIN: Single command mode on `%ws'", pszCmdLine)) ;
  210. if ((pnodeCmdTree = Parser(READSTRING, (int)pszCmdLine, DCount)) == (struct node *) PARSERROR)
  211. CMDexit(MAINERROR) ;
  212. if (pnodeCmdTree == (struct node *) EOF)
  213. CMDexit(SUCCESS) ;
  214. DEBUG((MNGRP, MNLVL, "MAIN: Single command parsed successfully.")) ;
  215. ReturnCode = Dispatch(RIO_MAIN, pnodeCmdTree);
  216. //
  217. // Make sure we have the correct console modes.
  218. //
  219. ResetConsoleMode();
  220. #ifdef JAPAN
  221. //
  222. // Get current CodePage Info. We need this to decide whether
  223. // or not to use half-width characters.
  224. //
  225. GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo);
  226. //
  227. // Maybe console output code page was changed by CHCP or MODE,
  228. // so need to reset LanguageID to correspond to code page.
  229. //
  230. SetTEBLangID();
  231. #endif
  232. if ( !fSingleBatchLine )
  233. CMDexit( ReturnCode );
  234. fSingleBatchLine = FALSE; // Allow ASync exec of GUI apps now
  235. }
  236. }
  237. //
  238. // Through init and single command processing. reset our Setjmp location
  239. // to here for error processing.
  240. //
  241. if (ReturnCode = setjmp(MainEnv)) {
  242. //
  243. // BUGBUG fix later to have a generalized abort
  244. // for not assume this is a real abort from
  245. // eof on stdin redirected.
  246. if (ReturnCode == EXIT_EOF) {
  247. CMDexit(SUCCESS);
  248. }
  249. }
  250. //
  251. // Check if our I/O has been redirected. This is used to tell
  252. // where we should read input from.
  253. //
  254. InputType = (FileIsDevice(STDIN)) ? READSTDIN : READFILE ;
  255. DEBUG((MNGRP,MNLVL,"MAIN: Multi command mode, InputType = %d", InputType)) ;
  256. //
  257. // If we are reading from a file, make sure the input mode is binary.
  258. // CRLF translations screw up the lexer because FillBuf() wants to
  259. // seek around in the file.
  260. //
  261. if(InputType == READFILE) {
  262. _setmode(STDIN,_O_BINARY);
  263. }
  264. //
  265. // Loop till out of input or error parsing.
  266. //
  267. while (TRUE) {
  268. DEBUG((MNGRP, MNLVL, "MAIN: Calling Parser.")) ;
  269. GotoFlag = FALSE ;
  270. ResetCtrlC();
  271. if ((pnodeCmdTree = Parser(InputType, STDIN, FS_FREEALL)) == (struct node *) PARSERROR) {
  272. DEBUG((MNGRP, MNLVL, "MAIN: Parse failed.")) ;
  273. } else if (pnodeCmdTree == (struct node *) EOF)
  274. CMDexit(SUCCESS) ;
  275. else {
  276. ResetCtrlC();
  277. DEBUG((MNGRP, MNLVL, "MAIN: Parsed OK, DISPATCHing.")) ;
  278. Dispatch(RIO_MAIN, pnodeCmdTree) ;
  279. //
  280. // Make sure we have the correct console modes.
  281. //
  282. ResetConsoleMode();
  283. //
  284. // Get current CodePage Info. We need this to decide whether
  285. // or not to use half-width characters.
  286. //
  287. GetCPInfo((CurrentCP=GetConsoleOutputCP()), &CurrentCPInfo);
  288. //
  289. // Maybe console output code page was changed by CHCP or MODE,
  290. // so need to reset LanguageID to correspond to code page.
  291. //
  292. SetTEBLangID();
  293. DEBUG((MNGRP, MNLVL, "MAIN: Dispatch returned.")) ;
  294. }
  295. }
  296. CMDexit(SUCCESS);
  297. return(SUCCESS);
  298. }
  299. int
  300. Dispatch(
  301. IN int RioType,
  302. IN struct node *pnodeCmdTree
  303. )
  304. /*++
  305. Routine Description:
  306. Set up any I/O redirection for the current node. Find out who is
  307. supposed to process this node and call the routine to do it. Reset
  308. stdin/stdout if necessary.
  309. Dispatch() must now be called with all args present since the RioType
  310. is needed to properly identify the redirection list element.
  311. Dispatch() determines the need for redirection by examination of the
  312. RioType and command node and calls SetRedir only if necessary. Also,
  313. in like manner, Dispatch() only calls ResetRedir if redirection was
  314. actually performed.
  315. The conditional that determines whether newline will be issued
  316. following commands (prior to prompt), had to be altered so that the
  317. execution of piped commands did not each issue a newline. The pre-
  318. prompt newline for pipe series is now issued by ePipe().
  319. Arguments:
  320. RioType - tells SetRedir the routine responsible for redirection
  321. pnodeCmdTree - the root of the parse tree to be executed
  322. Return Value:
  323. The return code from the command/function that was executed or
  324. FAILURE if redirection error.
  325. --*/
  326. {
  327. int comretcode ; // Retcode of the cmnd executed
  328. struct cmdnode *pcmdnode ; // pointer to current command node
  329. PTCHAR pbCmdBuf ; // Buffer used in building command
  330. DEBUG((MNGRP, DPLVL, "DISP: pnodeCmdTree = 0x%04x, RioType = %d", pnodeCmdTree, RioType)) ;
  331. //
  332. // If we don't have a parse tree or
  333. // we have a goto label or
  334. // we have a comment line
  335. // then don't execute anything and return.
  336. //
  337. if (!pnodeCmdTree ||
  338. GotoFlag ||
  339. pnodeCmdTree->type == REMTYP) {
  340. return(SUCCESS) ;
  341. }
  342. DEBUG((MNGRP, DPLVL, "DISP: type = 0x%02x", pnodeCmdTree->type)) ;
  343. //
  344. // Copy node ptr pnodeCmdTree to new node ptr pcmdnode
  345. // If command is to be detached or pipelined (but not a pipe)
  346. // If command is Batch file or Internal or Multi-statement command
  347. // "Unparse" tree into string approximating original commandline
  348. // Build new command node (pcmdnode) to spawn a child Command.com
  349. // Make the string ("/C" prepended) the argument of the new node
  350. // Perform redirection on node c
  351. // If node pcmdnode is to be detatched
  352. // Exec async/discard
  353. // else
  354. // Exec async/keep but don't wait for retcode (pipelined)
  355. // else
  356. // If this is a CMDTYP, PARTYP or SILTYP node and there is explicit redirection
  357. //
  358. // Perform redirection on this node
  359. // If operator node or a special type (FOR, IF, DET or REM)
  360. // Call routine identified by GetFuncPtr() to execute it
  361. // Else call FindFixAndRun() to execute the CMDTYP node.
  362. // If redirection was performed
  363. // Reset redirection
  364. //
  365. pcmdnode = (struct cmdnode *)pnodeCmdTree ;
  366. //
  367. // If we are called from ePipe and PIPE command then we need
  368. // to rebuild the command in ascii form (UnParse) and fork
  369. // off another cmd.exe to execute it.
  370. //
  371. if ((RioType == RIO_PIPE && pcmdnode->type != PIPTYP)) {
  372. //
  373. // pbCmdbuf is used as tmp in FindCmd and SFE
  374. //
  375. // BUGBUG: Why add 3 to MAXTOKLEN
  376. //
  377. if (!(pbCmdBuf=mkstr((MAXTOKLEN+3)*sizeof(TCHAR)))) {
  378. return(DISPERROR) ;
  379. }
  380. //
  381. // If current node to execute is not a command or
  382. // could not find it as an internal command or
  383. // it was found as a batch file then
  384. // Do the unparse
  385. //
  386. if (pcmdnode->type != CMDTYP ||
  387. FindCmd(CMDHIGH, pcmdnode->cmdline, pbCmdBuf) != -1 ||
  388. SearchForExecutable(pcmdnode, pbCmdBuf) == SFE_ISBAT) {
  389. DEBUG((MNGRP, DPLVL, "DISP: Now UnParsing")) ;
  390. //
  391. // if pcmdnode an intrnl cmd then pbCmdBuf holds it's switches
  392. // if pcmdnode was a batch file then pbCmdBuf holds location
  393. //
  394. if (UnParse((struct node *)pcmdnode, pbCmdBuf)) {
  395. return(DISPERROR) ;
  396. }
  397. DEBUG((MNGRP, DPLVL, "DISP: UnParsed cmd = %ws", pbCmdBuf)) ;
  398. //
  399. // Build a command node with unparsed command
  400. // Will be exec'd later after redirection is applied
  401. //
  402. pcmdnode = (struct cmdnode *)mknode() ;
  403. if (pcmdnode == NULL) {
  404. return(DISPERROR) ;
  405. }
  406. pcmdnode->type = CMDTYP ;
  407. pcmdnode->cmdline = GetEnvVar(ComSpecStr) ;
  408. pcmdnode->argptr = pbCmdBuf ;
  409. };
  410. //
  411. // Setup I/O redirection
  412. //
  413. if (SetRedir((struct node *)pcmdnode, RioType)) {
  414. return(DISPERROR) ;
  415. }
  416. DEBUG((MNGRP, DPLVL, "DISP:Calling ECWork on piped cmd")) ;
  417. pbCmdBuf[1] = SwitChar ;
  418. pbCmdBuf[2] = TEXT('S') ;
  419. comretcode = ECWork(pcmdnode, AI_KEEP, CW_W_NO) ;
  420. DEBUG((MNGRP, DPLVL, "DISP: ECWork returned %d", comretcode)) ;
  421. } else {
  422. //
  423. // We are here if command was not PIPE
  424. //
  425. // If it was a command node or a paren or a silent operator and
  426. // we have redirection then set redirection.
  427. //
  428. if ((pnodeCmdTree->type == CMDTYP ||
  429. pnodeCmdTree->type == PARTYP ||
  430. pnodeCmdTree->type == SILTYP ||
  431. pnodeCmdTree->type == HELPTYP) &&
  432. pnodeCmdTree->rio) {
  433. //
  434. // Set redirection on node.
  435. //
  436. if (SetRedir(pnodeCmdTree, RioType)) {
  437. return(DISPERROR) ;
  438. }
  439. }
  440. //
  441. // If it is an internal command then find it and execute
  442. // otherwise locate file load and execute
  443. //
  444. if (pnodeCmdTree->type != CMDTYP) {
  445. comretcode = (*GetFuncPtr(pnodeCmdTree->type))((struct cmdnode *)pnodeCmdTree) ;
  446. } else {
  447. comretcode = FindFixAndRun((struct cmdnode *)pnodeCmdTree) ;
  448. }
  449. } // else
  450. //
  451. // Reset and redirection that was previously setup
  452. // pcmdnode is always current node.
  453. //
  454. if ((rioCur) && (rioCur->rnod == (struct node *)pcmdnode)) {
  455. ResetRedir() ;
  456. }
  457. DEBUG((MNGRP, DPLVL, "DISP: returning %d", comretcode)) ;
  458. return(comretcode) ;
  459. }
  460. int
  461. SetRedir(
  462. IN struct node *pnodeCmdTree,
  463. IN int RioType
  464. )
  465. /*++
  466. Routine Description:
  467. Perform the redirection required by the current node
  468. Only individual commands and parenthesised statement groups can have
  469. explicit I/O redirection. All nodes, however, can tolerate redirection of an
  470. implicit nature.
  471. Arguments:
  472. pNode - pointer node containing redirection information
  473. RioType - indicator of source of redirection request
  474. Return Value:
  475. SUCCESS if the redirection was successfully set up.
  476. FAILURE if the redirection was NOT successfully set up.
  477. --*/
  478. {
  479. struct rio *prio ;
  480. int i;
  481. CRTHANDLE OpenStatus;
  482. BOOLEAN fInputRedirected = FALSE;
  483. //
  484. // Temps. Used to hold all of the relocation information for a
  485. // command.
  486. //
  487. struct relem *prelemT ;
  488. struct relem *prelemT2;
  489. TCHAR rgchFileName[MAX_PATH];
  490. DEBUG((MNGRP, RIOLVL, "SETRD:RioType = %d.",RioType)) ;
  491. prelemT = pnodeCmdTree->rio ;
  492. //
  493. // Loop through redirections removing ":" from device names
  494. // and determining if input has been redirected
  495. //
  496. while (prelemT) {
  497. mystrcpy(prelemT->fname, stripit(prelemT->fname) );
  498. //
  499. // skip any redirection that already has been done
  500. //
  501. if (prelemT->svhndl) {
  502. prelemT = prelemT->nxt ;
  503. continue ;
  504. }
  505. //
  506. // check for and remove any COLON that might be in a device name
  507. //
  508. if ((i = mystrlen(prelemT->fname)-1) > 1 && *(prelemT->fname+i) == COLON)
  509. *(prelemT->fname+i) = NULLC ;
  510. //
  511. // If input redirection specified then set flag for later use
  512. //
  513. if (prelemT->rdhndl == STDIN) {
  514. fInputRedirected = TRUE ;
  515. }
  516. prelemT = prelemT->nxt ;
  517. }
  518. DEBUG((MNGRP, RIOLVL, "SETRD: fInputRedirected = %d",fInputRedirected)) ;
  519. //
  520. // Allocate, activate and initialize the rio list element.
  521. // We must skip this if called from AddRedir (test for RIO_REPROCESS)
  522. //
  523. if (RioType != RIO_REPROCESS) {
  524. if (!(prio=(struct rio *)mkstr(sizeof(struct rio)))) {
  525. PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
  526. return ( FAILURE ) ;
  527. } ;
  528. prio->back = rioCur ;
  529. rioCur = prio ;
  530. prio->rnod = pnodeCmdTree ;
  531. prio->type = RioType ;
  532. DEBUG((MNGRP, RIOLVL, "SETRD: rio element built.")) ;
  533. } else {
  534. prio = rioCur;
  535. }
  536. //
  537. // Once the list has been set up for standard and special cases
  538. // the actual handle redirection is performed.
  539. //
  540. // loop thru the list performing all redirection and error recovery.
  541. //
  542. prelemT = pnodeCmdTree->rio ;
  543. while (prelemT) {
  544. //
  545. // Skip any already done.
  546. //
  547. if (prelemT->svhndl) {
  548. prelemT = prelemT->nxt ;
  549. continue ;
  550. } ;
  551. DEBUG((MNGRP, RIOLVL, "SETRD: Old osf handle = %x", CRTTONT(prelemT->rdhndl))) ;
  552. //
  553. // Make sure read handle is open and valid before saving it.
  554. //
  555. if (FileIsDevice(prelemT->rdhndl) || FileIsPipe(prelemT->rdhndl) ||
  556. SetFilePointer(CRTTONT(prelemT->rdhndl), 0L, NULL, FILE_CURRENT) != -1) {
  557. DEBUG((MNGRP, RIOLVL, "SETRD: duping %d", prelemT->rdhndl)) ;
  558. if ((prelemT->svhndl = Cdup(prelemT->rdhndl)) == BADHANDLE) {
  559. DEBUG((MNGRP, RIOLVL, "SETRD: Cdup error=%d, errno=%d", GetLastError(), errno)) ;
  560. PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (unsigned long)prelemT->rdhndl)) ;
  561. prelemT->svhndl = 0 ;
  562. ResetRedir() ;
  563. return(FAILURE) ;
  564. }
  565. DEBUG((MNGRP, RIOLVL, "SETRD: closing %d", prelemT->rdhndl)) ;
  566. Cclose(prelemT->rdhndl) ;
  567. DEBUG((MNGRP,RIOLVL,"SETRD: save handle = %d", prelemT->svhndl));
  568. DEBUG((MNGRP,RIOLVL,"SETRD: --->osf handle = %x", CRTTONT(prelemT->svhndl))) ;
  569. } else {
  570. DEBUG((MNGRP, RIOLVL, "SETRD: FileIsOpen ret'd FALSE")) ;
  571. PutStdErr(MSG_RDR_HNDL_OPEN, ONEARG, argstr1(TEXT("%d"), (unsigned long)prelemT->rdhndl)) ;
  572. prelemT->svhndl = 0 ;
  573. ResetRedir() ;
  574. return(FAILURE) ;
  575. }
  576. //
  577. // Is file name the command seperator character '&'
  578. //
  579. if (*prelemT->fname == CSOP) {
  580. DEBUG((MNGRP,RIOLVL,"SETRD: Handle substitution, %ws %d", prelemT->fname, prelemT->rdhndl)) ;
  581. *(prelemT->fname+2) = NULLC ;
  582. if (Cdup2(*(prelemT->fname+1) - TEXT('0'), prelemT->rdhndl) == BADHANDLE) {
  583. DEBUG((MNGRP, RIOLVL, "SETRD: Cdup2 error=%d, errno=%d", GetLastError(), errno)) ;
  584. ResetRedir() ;
  585. PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (ULONG)prelemT->rdhndl)) ;
  586. return(FAILURE) ;
  587. } ;
  588. DEBUG((MNGRP,RIOLVL,"SETRD: %c forced to %d",*(prelemT->fname+1), (ULONG)prelemT->rdhndl)) ;
  589. } else {
  590. //
  591. // redirecting input from a file. Check to see if file
  592. // exists and can be opened for input.
  593. //
  594. if (prelemT->rdop == INOP) {
  595. DEBUG((MNGRP,RIOLVL,"SETRD: File in = %ws",prelemT->fname)) ;
  596. //
  597. // Try to open file localy first
  598. //
  599. if ((OpenStatus = Copen(prelemT->fname, O_RDONLY|O_BINARY)) == BADHANDLE) {
  600. //
  601. // Now try the DPATH (data path)
  602. //
  603. if ( SearchPath( AppendStr,
  604. prelemT->fname,
  605. NULL,
  606. MAX_PATH,
  607. rgchFileName,
  608. NULL ) != 0 ) {
  609. OpenStatus = Copen(rgchFileName, O_RDONLY|O_BINARY) ;
  610. }
  611. }
  612. } else {
  613. //
  614. // We are not redirecting input so must be output
  615. //
  616. DEBUG((MNGRP,RIOLVL,"SETRD: File out = %ws",prelemT->fname)) ;
  617. //
  618. // Make sure sure we can open the file for output
  619. //
  620. OpenStatus = Copen(prelemT->fname, prelemT->flag ? OP_APPEN : OP_TRUNC) ;
  621. }
  622. //
  623. // If the handle to be redirected was not the lowest numbered,
  624. // unopened handle when open was called, the current handle must
  625. // be forced to it, the handle returned by open must be closed.
  626. //
  627. // BUGBUG: why must it be force to lowest numbered
  628. //
  629. if (OpenStatus != BADHANDLE && OpenStatus != prelemT->rdhndl) {
  630. DEBUG((MNGRP,RIOLVL,"SETRD: Handles don't match...")) ;
  631. DEBUG((MNGRP,RIOLVL,"SETRD: ...forcing %d to %d", i, (ULONG)prelemT->rdhndl)) ;
  632. if (Cdup2(OpenStatus, prelemT->rdhndl) == BADHANDLE) {
  633. DEBUG((MNGRP, RIOLVL, "SETRD: Cdup2 error=%d, errno=%d", GetLastError(), errno)) ;
  634. Cclose(OpenStatus) ;
  635. ResetRedir() ;
  636. PutStdErr(MSG_RDR_HNDL_CREATE, ONEARG, argstr1(TEXT("%d"), (ULONG)prelemT->rdhndl)) ;
  637. return(FAILURE) ;
  638. } else {
  639. Cclose(OpenStatus) ;
  640. OpenStatus = prelemT->rdhndl ;
  641. }
  642. }
  643. //
  644. // Copen error processing must be delayed to here to allow the
  645. // above Cdup2 to occur if necessary. Otherwise, the call to
  646. // ResetRedir in the error handler would attempt to close the
  647. // wrong handle and leave a bogus handle open.
  648. //
  649. if (OpenStatus == BADHANDLE) {
  650. DEBUG((MNGRP,RIOLVL,"SETRD: Bad Open, DosErr = %d",DosErr)) ;
  651. ResetRedir() ;
  652. PrtErr(DosErr) ;
  653. return(FAILURE) ;
  654. }
  655. DEBUG((MNGRP, RIOLVL, "SETRD: new handle = %d", OpenStatus)) ;
  656. DEBUG((MNGRP,RIOLVL,"SETRD: --->osf handle = %x", CRTTONT(OpenStatus))) ;
  657. //
  658. // Keep highest numbered handle
  659. //
  660. prio->stdio = OpenStatus ;
  661. } // else
  662. prelemT = prelemT->nxt ;
  663. } // while
  664. return(SUCCESS) ;
  665. }
  666. AddRedir(
  667. IN struct cmdnode *pcmdnodeOriginal,
  668. IN struct cmdnode *pcmdnodeNew
  669. )
  670. /*++
  671. Routine Description:
  672. Add redirection from a new node to an existing one. Walk the
  673. redirection list of the old node for each element in the new.
  674. Duplicates are removed from the old and replaced by the new,
  675. while unique new ones are added to the end. When the two lists
  676. are merged, reprocess the redirection.
  677. Arguments:
  678. pcmdnodeOriginal - original node to be added to
  679. pcmdnodeNew - new node to merge.
  680. Return Value:
  681. SUCCESS if the redirection was successfully merged.
  682. FAILURE otherwise.
  683. --*/
  684. {
  685. struct relem *prelemOriginal ;
  686. struct relem *prelemNew ;
  687. struct relem *prelemEnd ; // Ptr to end of original list
  688. //
  689. // Flag to set Stack Minimum
  690. //
  691. BOOLEAN fSetStackMin = FALSE;
  692. PTCHAR oldname ; /* Sanity check */
  693. struct rio *rn ; /* Possible rio element */
  694. //
  695. // Won't be here unless pcmdnodeNew-reio exists
  696. //
  697. prelemNew = pcmdnodeNew->rio ;
  698. // If there was no redirection associated with the original node, we must
  699. // also create a rio element so that the redirection can be reset at
  700. // command completion or receipt of signal. We have to create it here
  701. // rather than in SetRedir in order to include it on the data stack when
  702. // we set a new level.
  703. if (!(prelemEnd = prelemOriginal = pcmdnodeOriginal->rio)) {
  704. DEBUG((MNGRP, RIOLVL, "ADDRD: No old redirection.")) ;
  705. //
  706. // New list becomes original
  707. //
  708. pcmdnodeOriginal->rio = prelemNew ;
  709. if (!(rn=(struct rio *)mkstr(sizeof(struct rio)))) {
  710. PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS);
  711. return(FAILURE) ;
  712. }
  713. //
  714. // Create dummy redirection node.
  715. //
  716. rn->back = rioCur ;
  717. rioCur = rn ;
  718. rn->rnod = (struct node *)pcmdnodeOriginal ;
  719. rn->type = RIO_BATLOOP ;
  720. DEBUG((MNGRP, RIOLVL, "ADDRD: rio element built.")) ;
  721. fSetStackMin = TRUE ; /* Must save current datacount */
  722. prelemNew = NULL ; /* Skip the while loops */
  723. } else {
  724. //
  725. // Find the end of the orignal list
  726. //
  727. while (prelemEnd->nxt) {
  728. prelemEnd = prelemEnd->nxt ;
  729. }
  730. }
  731. //
  732. // If prelemNew is non-null, we've two lists which we integrate by
  733. // eliminating any duplicate entries and adding any unique entries in
  734. // the new list to the end of the original. Note that if unique entries
  735. // exist, we must save the current data count to avoid losing their
  736. // malloc'd data when we go on to SetBat().
  737. //
  738. //
  739. // For each new redirection, look at the original
  740. //
  741. while (prelemNew) {
  742. while(prelemOriginal) {
  743. //
  744. // Do we have a duplicate
  745. //
  746. if (prelemNew->rdhndl != prelemOriginal->rdhndl) {
  747. prelemOriginal = prelemOriginal->nxt ;
  748. continue ;
  749. } else {
  750. if (prelemOriginal->svhndl && (prelemOriginal->svhndl != BADHANDLE)) {
  751. //
  752. // BUGBUG put an assert here
  753. //
  754. Cdup2(prelemOriginal->svhndl, prelemOriginal->rdhndl) ;
  755. Cclose(prelemOriginal->svhndl) ;
  756. } else {
  757. if (prelemOriginal->svhndl == BADHANDLE) {
  758. Cclose(prelemOriginal->rdhndl) ;
  759. }
  760. }
  761. prelemOriginal->svhndl = 0 ; /* ...and replace it */
  762. prelemOriginal->flag = prelemNew->flag ;
  763. prelemOriginal->rdop = prelemNew->rdop ;
  764. oldname = prelemOriginal->fname ;
  765. prelemOriginal->fname = resize(prelemOriginal->fname, mystrlen(prelemNew->fname) + 1) ;
  766. mystrcpy(prelemOriginal->fname, prelemNew->fname) ;
  767. if (prelemOriginal->fname != oldname) {
  768. fSetStackMin = TRUE ;
  769. }
  770. pcmdnodeNew->rio = prelemNew->nxt ;
  771. break ;
  772. }
  773. }
  774. //
  775. // If no old entry remove from new and add to original
  776. // update the end pointer, zero next pointer and preserve datacount
  777. //
  778. if (prelemNew == pcmdnodeNew->rio) {
  779. pcmdnodeNew->rio = prelemNew->nxt ;
  780. prelemEnd->nxt = prelemNew ;
  781. prelemEnd = prelemEnd->nxt ;
  782. prelemEnd->nxt = NULL ;
  783. fSetStackMin = TRUE ;
  784. }
  785. prelemNew = pcmdnodeNew->rio ;
  786. prelemOriginal = pcmdnodeOriginal->rio ;
  787. }
  788. //
  789. // All duplicates are eliminated. Now save the data count and call
  790. // SetRedir to reprocess the redirection list for any unimplimented
  791. // redirection (io->svhndl == 0).
  792. //
  793. if (fSetStackMin) {
  794. if (CurBat->stacksize < (CurBat->stackmin = DCount)) {
  795. CurBat->stacksize = DCount ;
  796. }
  797. }
  798. return(SetRedir((struct node *)pcmdnodeOriginal, RIO_REPROCESS)) ;
  799. }
  800. void
  801. ResetRedir()
  802. /*++
  803. Routine Description:
  804. Reset the redirection identified by the last rio list element
  805. as pointed to by rioCur. When finished, remove the rio element
  806. from the list.
  807. Arguments:
  808. Return Value:
  809. --*/
  810. {
  811. struct rio *prio = rioCur;
  812. struct relem *prelemT ;
  813. CRTHANDLE handleT;
  814. DEBUG((MNGRP, RIOLVL, "RESETR: Entered.")) ;
  815. prelemT = prio->rnod->rio ;
  816. while (prelemT) {
  817. if (prelemT->svhndl && (prelemT->svhndl != BADHANDLE)) {
  818. DEBUG((MNGRP,RIOLVL,"RESETR: Resetting %d",(ULONG)prelemT->rdhndl)) ;
  819. DEBUG((MNGRP,RIOLVL,"RESETR: From save %d",(ULONG)prelemT->svhndl)) ;
  820. handleT = Cdup2(prelemT->svhndl, prelemT->rdhndl) ;
  821. Cclose(prelemT->svhndl) ;
  822. DEBUG((MNGRP,RIOLVL,"RESETR: Dup2 retcode = %d", handleT)) ;
  823. } else {
  824. if (prelemT->svhndl == BADHANDLE) {
  825. DEBUG((MNGRP,RIOLVL,"RESETR: Closing %d",(ULONG)prelemT->rdhndl)) ;
  826. Cclose(prelemT->rdhndl) ;
  827. }
  828. }
  829. prelemT->svhndl = 0 ;
  830. prelemT = prelemT->nxt ;
  831. }
  832. //
  833. // Kill list element
  834. //
  835. rioCur = prio->back ;
  836. DEBUG((MNGRP, RIOLVL, "RESETR: List element destroyed.")) ;
  837. }
  838. int
  839. FindFixAndRun (
  840. IN struct cmdnode *pcmdnode
  841. )
  842. /*++
  843. Routine Description:
  844. If the command name is in the form d: or, just change drives.
  845. Otherwise, search for the nodes command name in the jump table.
  846. If it is found, check the arguments for bad drivespecs or unneeded
  847. switches and call the function which executes the command.
  848. Otherwise, assume it is an external command and call ExtCom.
  849. Arguments:
  850. pcmdnode - the node of the command to be executed
  851. Return Value:
  852. SUCCESS or FAILURE if changing drives.
  853. Otherwise, whatever is returned by the function which is called to
  854. execute the command.
  855. --*/
  856. {
  857. PTCHAR pszTokStr ;
  858. USHORT DriveNum;
  859. ULONG JmpTblIdx;
  860. TCHAR cname[MAX_PATH] ;
  861. TCHAR cflags;
  862. int (*funcptr)(struct cmdnode *) ;
  863. unsigned cbTokStr;
  864. PTCHAR pszTitle;
  865. ULONG rc;
  866. //
  867. // I haven't found where in CMD we end up with NULL pointer here
  868. // (all failing mallocs cause CMD to exit)
  869. // however I saw one strange stress failure.
  870. // So lets not cause AV and just return FAILURE if NULL.
  871. //
  872. if (pcmdnode->cmdline == NULL)
  873. return(FAILURE) ;
  874. //
  875. // Validate any drive letter
  876. //
  877. if (*(pcmdnode->cmdline+1) == COLON) {
  878. if (!IsValidDrv(*pcmdnode->cmdline)) {
  879. PutStdErr(ERROR_INVALID_DRIVE, NOARGS);
  880. return(FAILURE) ;
  881. } else {
  882. //
  883. // Make sure it isn't locked either
  884. //
  885. if ( IsDriveLocked(*pcmdnode->cmdline)) {
  886. PutStdErr( GetLastError() , NOARGS);
  887. return(FAILURE) ;
  888. }
  889. }
  890. //
  891. // Pull out drive letter and convert to drive number
  892. // BUGBUG: it is not neccessary to do this conversion
  893. //
  894. DriveNum = (USHORT)(_totupper(*pcmdnode->cmdline) - SILOP) ;
  895. //
  896. // If this is just a change in drive do it here
  897. //
  898. if (mystrlen(pcmdnode->cmdline) == 2) {
  899. //
  900. // ChangeDrive set CurDrvDir in addition to changing the drive
  901. ChangeDrive(DriveNum) ;
  902. DEBUG((MNGRP,DPLVL,"FFAR: Drv chng to %ws", CurDrvDir)) ;
  903. return(SUCCESS) ;
  904. }
  905. //
  906. // Note that if the cmdline contains a drivespec, no attempt is made at
  907. // internal command matching whatsoever.
  908. //
  909. return(ExtCom(pcmdnode)) ;
  910. }
  911. //
  912. // The sequence below works as follows:
  913. // - A match between the previously-parsed first non-delimiter character
  914. // group in the cmdline and the command table is attempted. A match
  915. // sets JmpTblIdx to the command index; no match sets JmpTblIdx to -1.
  916. // - FixCom is then called, and using the value of 'i', it detects cases
  917. // of internal commands only (i == -1) which have no standard delimiter
  918. // (whitespace or "=;,") between them and their arguments such as the
  919. // "cd\foo". Note that a file foo.exe in subdirectory "cd" cannot be
  920. // executed except through full path or drive specification. FixCom
  921. // actually fixes up the cmdline and argptr fields of the node.
  922. // - The command is then executed using ExtCom (i == -1) or the internal
  923. // function indicated by the index
  924. //
  925. // Added second clause to detect REM commands which were parsed incorrectly
  926. // as CMDTYP due to semi-delimiter characters appended. If REM, we know
  927. // its OK, so just return success. If any other of the special types,
  928. // FOR, DET, EXT, etc., allow to continue and fail in ExtCom since they
  929. // weren'tparsed correctly and will bomb.
  930. //
  931. JmpTblIdx = FindAndFix( pcmdnode, (PTCHAR )&cflags ) ;
  932. DEBUG((MNGRP, DPLVL, "FFAR: After FixCom pcmdnode->cmdline = '%ws'", pcmdnode->cmdline)) ;
  933. //
  934. // Check if it was not an internal command, if so then exec it
  935. //
  936. if (JmpTblIdx == -1) {
  937. DEBUG((MNGRP, DPLVL, "FFAR: Calling ExtCom on %ws", pcmdnode->cmdline)) ;
  938. return(ExtCom(pcmdnode)) ;
  939. }
  940. //
  941. // CMD was found in table. If function field is NULL as in the
  942. // case of REM, this is a dummy entry and must return SUCCESS.
  943. //
  944. if ((funcptr = GetFuncPtr(JmpTblIdx)) == NULL) {
  945. DEBUG((MNGRP, DPLVL, "FFAR: Found internal with NULL entry")) ;
  946. DEBUG((MNGRP, DPLVL, " Returning SUCESS")) ;
  947. return(SUCCESS) ;
  948. }
  949. //
  950. // If the command is supposed to have the drivespecs on its args
  951. // validated before the command is executed, do it. If the command
  952. // is not allowed toto contain switches and it has one, complain.
  953. //
  954. //
  955. // Set up extra delimiter for seperating out switches
  956. //
  957. cname[0] = SwitChar;
  958. cname[1] = NULLC;
  959. pszTokStr = TokStr(pcmdnode->argptr, cname, TS_SDTOKENS) ;
  960. // this hack to allow environment variables to contain /?
  961. if (JmpTblIdx != SETTYP || !pszTokStr || (_tcsncmp(pszTokStr,TEXT("/\0?"),4) == 0)) {
  962. // this is to exclude START command
  963. if (JmpTblIdx != STRTTYP) {
  964. if (CheckHelpSwitch(JmpTblIdx, pszTokStr) ) {
  965. return( FAILURE );
  966. }
  967. }
  968. }
  969. DEBUG((MNGRP, DPLVL, "FFAR: Internal command, about to validate args")) ;
  970. for (;(pszTokStr != NULL) && *pszTokStr ; pszTokStr += mystrlen(pszTokStr)+1) {
  971. cbTokStr = mystrlen(pszTokStr);
  972. mystrcpy( pszTokStr, stripit( pszTokStr ) );
  973. DEBUG((MNGRP, DPLVL, "FFAR: Checking args; arg = %ws", pszTokStr)) ;
  974. if ((cflags & CHECKDRIVES) && *(pszTokStr+1) == COLON) {
  975. if (!IsValidDrv(*pszTokStr)) {
  976. PutStdErr(ERROR_INVALID_DRIVE, NOARGS);
  977. return(LastRetCode = FAILURE) ;
  978. } else {
  979. //
  980. // If not the copy command (A->B B->A swaps)
  981. // then check if drive is locked
  982. // if drive locked then
  983. // display error return code message
  984. // terminate this command's processing
  985. //
  986. if (JmpTblIdx != CPYTYP) {
  987. if ( IsDriveLocked(*pszTokStr)) {
  988. PutStdErr( GetLastError() , NOARGS);
  989. return(LastRetCode = FAILURE) ;
  990. }
  991. }
  992. }
  993. }
  994. if ((cflags & NOSWITCHES) && (pszTokStr != NULL) && *pszTokStr == SwitChar) {
  995. PutStdErr(MSG_BAD_SYNTAX, NOARGS);
  996. return(LastRetCode = FAILURE) ;
  997. }
  998. }
  999. DEBUG((MNGRP, DPLVL, "FFAR: calling function, cmd = `%ws'", pcmdnode->cmdline)) ;
  1000. //
  1001. // Call internal routine to execute the command
  1002. //
  1003. if ((pszTitle = GetTitle(pcmdnode)) != NULL) {
  1004. SetConTitle(pszTitle);
  1005. }
  1006. rc = (*funcptr)(pcmdnode);
  1007. ResetConTitle(pszTitleCur);
  1008. return(rc) ;
  1009. }
  1010. int
  1011. FindAndFix (
  1012. IN struct cmdnode *pcmdnode,
  1013. IN PTCHAR pbCmdFlags
  1014. )
  1015. /*++
  1016. Routine Description:
  1017. This routine separates the command and its following
  1018. switch character if there is no space between the
  1019. command and the switch character.
  1020. This routine is used for both left side and right side
  1021. of PIPE.
  1022. Arguments:
  1023. pcmdnode - pointer to node the contains command to locate
  1024. pbCmdFlags -
  1025. Return Value:
  1026. --*/
  1027. {
  1028. TCHAR chCur; // current character we are looking at
  1029. TCHAR rgchCmdStr[MAX_PATH] ;
  1030. PTCHAR pszArgT; // Temp. used to build a new arguemt string
  1031. ULONG JmpTableIdx; // index into jump table of function pointers
  1032. ULONG iCmdStr; // index into command string
  1033. ULONG cbCmdStr; // length of command string
  1034. BOOLEAN fQuoteFound, fQuoteFound2;
  1035. BOOLEAN fDone;
  1036. fQuoteFound = FALSE;
  1037. fQuoteFound2 = FALSE;
  1038. //
  1039. // Extract only commnand from the command string (pcmdnode->cmdline)
  1040. //
  1041. for (iCmdStr = 0 ; iCmdStr < MAX_PATH-1; iCmdStr++) {
  1042. chCur = *(pcmdnode->cmdline + iCmdStr);
  1043. //
  1044. // If we found a quote invert the current quote state
  1045. // for both first quote (fQuoteFound) and end quote (fQuoteFound2)
  1046. //
  1047. if ( chCur == QUOTE ) {
  1048. fQuoteFound = (BOOLEAN)!fQuoteFound;
  1049. fQuoteFound2 = (BOOLEAN)!fQuoteFound;
  1050. }
  1051. //
  1052. // If we have a character and
  1053. // have found either a begin or end quote or cur char is not delimeter
  1054. // and cur char is not a special (+[] etc.) delimiter
  1055. //
  1056. if ((chCur) &&
  1057. ( fQuoteFound || fQuoteFound2 || !mystrchr(Delimiters,chCur) &&
  1058. !mystrchr(Delim2,chCur))) {
  1059. rgchCmdStr[iCmdStr] = chCur ;
  1060. fQuoteFound2 = FALSE;
  1061. }
  1062. else {
  1063. break ;
  1064. }
  1065. }
  1066. if (iCmdStr == 0) {
  1067. return -1;
  1068. }
  1069. rgchCmdStr[iCmdStr] = NULLC ;
  1070. //
  1071. // See if command is in jump table (is an internal command)
  1072. // If it is not found amoung the normal internal command
  1073. // check amoung the special parse type if it was a comment
  1074. //
  1075. if ((JmpTableIdx = FindCmd(CMDHIGH, rgchCmdStr, pbCmdFlags)) == -1) {
  1076. if (FindCmd(CMDMAX, rgchCmdStr, pbCmdFlags) == REMTYP) {
  1077. return(REMTYP) ;
  1078. }
  1079. } else if (JmpTableIdx == GOTYP)
  1080. pcmdnode->flag = CMDNODE_FLAG_GOTO;
  1081. fQuoteFound = FALSE;
  1082. fQuoteFound2 = FALSE;
  1083. //
  1084. // If the command is not found, check the length of command string
  1085. // for the case of DBCS. Count the characters that are not white space
  1086. // remaining in command
  1087. if ( JmpTableIdx == -1 ) {
  1088. fDone = FALSE;
  1089. while ( !fDone ) {
  1090. chCur = *(pcmdnode->cmdline+iCmdStr);
  1091. if ( chCur && chCur == QUOTE ) {
  1092. fQuoteFound = (BOOLEAN)!fQuoteFound;
  1093. fQuoteFound2 = (BOOLEAN)!fQuoteFound;
  1094. }
  1095. if ( chCur && ( fQuoteFound || fQuoteFound2 ||
  1096. !_istspace(chCur) &&
  1097. !mystrchr(Delimiters, chCur) &&
  1098. !(chCur == SwitChar))) {
  1099. iCmdStr++;
  1100. fQuoteFound2 = FALSE;
  1101. } else {
  1102. fDone = TRUE;
  1103. }
  1104. }
  1105. }
  1106. //
  1107. // If cmdstr contains more than command, strip of extra part
  1108. // and put it in front of the existing command argument pcmdnode-argptr
  1109. //
  1110. //
  1111. if (iCmdStr != (cbCmdStr = mystrlen(pcmdnode->cmdline))) {
  1112. int ArgLen;
  1113. ArgLen = mystrlen(pcmdnode->argptr);
  1114. ArgLen += cbCmdStr;
  1115. if (!(pszArgT = mkstr(ArgLen*sizeof(TCHAR)))) {
  1116. PutStdErr(MSG_NO_MEMORY, NOARGS);
  1117. Abort() ;
  1118. }
  1119. //
  1120. // create argument string and copy the 'extra' part of command
  1121. // it.
  1122. //
  1123. mystrcpy(pszArgT, pcmdnode->cmdline+iCmdStr) ;
  1124. //
  1125. // If we have a argument pointer stuff in the front
  1126. //
  1127. if (pcmdnode->argptr) {
  1128. mystrcat(pszArgT, pcmdnode->argptr) ;
  1129. }
  1130. pcmdnode->argptr = pszArgT ;
  1131. *(pcmdnode->cmdline+iCmdStr) = NULLC ;
  1132. }
  1133. return(JmpTableIdx) ;
  1134. }
  1135. int
  1136. UnParse(
  1137. IN struct node *pnode,
  1138. IN PTCHAR pbCmdBuf )
  1139. /*++
  1140. Routine Description:
  1141. Do setup and call UnBuild to deparse a node tree.
  1142. Arguments:
  1143. pnode - pointer to root of parse tree to UnParse
  1144. pbCmdBuf -
  1145. Uses Global pointer CBuf and assumes a string of MAXTOKLEN+1 bytes
  1146. has already been allocated to it (as done by Dispatch).
  1147. Return Value:
  1148. --*/
  1149. {
  1150. int rc;
  1151. DEBUG((MNGRP, DPLVL, "UNPRS: Entered")) ;
  1152. if (!pnode) {
  1153. DEBUG((MNGRP, DPLVL, "UNPRS: Found NULL node")) ;
  1154. return(FAILURE) ;
  1155. }
  1156. //
  1157. // Leave space in front of command for a /s
  1158. // Setup command buffer for a single command execution
  1159. //
  1160. pbCmdBuf[0] = SPACE ;
  1161. pbCmdBuf[1] = SPACE ;
  1162. pbCmdBuf[2] = SPACE ;
  1163. pbCmdBuf[3] = SPACE ;
  1164. pbCmdBuf[4] = SwitChar ;
  1165. pbCmdBuf[5] = TEXT('C') ;
  1166. pbCmdBuf[6] = TEXT('\"');
  1167. pbCmdBuf[7] = NULLC;
  1168. //
  1169. // Setup to handle an exception during detach.
  1170. if (setjmp(CmdJBuf2)) {
  1171. DEBUG((MNGRP, DPLVL, "UNPRS: Longjmp return occurred!")) ;
  1172. return(FAILURE) ;
  1173. }
  1174. //
  1175. // DisAssemble the current command
  1176. //
  1177. rc = (UnBuild(pnode, pbCmdBuf)) ;
  1178. mystrcat( pbCmdBuf, TEXT("\"") );
  1179. return( rc );
  1180. }
  1181. UnBuild(
  1182. IN struct node *pnode,
  1183. IN PTCHAR pbCmdBuf
  1184. )
  1185. /*++
  1186. Routine Description:
  1187. Recursively take apart a parse tree of nodes, building a string of
  1188. their components.
  1189. Arguments:
  1190. pnode - root of parse tree to UnBuild
  1191. pbCmdBuf - Where to put UnBuilt command
  1192. Return Value:
  1193. --*/
  1194. {
  1195. //
  1196. // Different kinds of nodes to Unbuild
  1197. //
  1198. struct cmdnode *pcmdnode;
  1199. struct fornode *pfornode ;
  1200. struct ifnode *pifnode ;
  1201. PTCHAR op ;
  1202. DEBUG((MNGRP, DPLVL, "UNBLD: Entered")) ;
  1203. switch (pnode->type) {
  1204. case CSTYP:
  1205. case ORTYP:
  1206. case ANDTYP:
  1207. case PIPTYP:
  1208. case PARTYP:
  1209. case SILTYP:
  1210. DEBUG((MNGRP, DPLVL, "UNBLD: Found OPERATOR")) ;
  1211. UnDuRd(pnode, pbCmdBuf) ;
  1212. switch (pnode->type) {
  1213. case CSTYP:
  1214. op = CSSTR ;
  1215. break ;
  1216. case ORTYP:
  1217. op = ORSTR ;
  1218. break ;
  1219. case ANDTYP:
  1220. op = ANDSTR ;
  1221. break ;
  1222. case PIPTYP:
  1223. op = PIPSTR ;
  1224. break ;
  1225. case PARTYP:
  1226. SPutC(pbCmdBuf, LEFTPSTR,YSPC) ;
  1227. op = RPSTR ;
  1228. break ;
  1229. case SILTYP:
  1230. SPutC(pbCmdBuf, SILSTR,YSPC) ;
  1231. op = SPCSTR ;
  1232. break ;
  1233. } ;
  1234. //
  1235. // Recurse down undoing the left hand side
  1236. //
  1237. UnBuild(pnode->lhs, pbCmdBuf) ;
  1238. //
  1239. // Now that left side there copy in operator and do right side
  1240. //
  1241. SPutC(pbCmdBuf, op,YSPC) ;
  1242. if (pnode->type != PARTYP && pnode->type != SILTYP)
  1243. UnBuild(pnode->rhs, pbCmdBuf) ;
  1244. break ;
  1245. case FORTYP:
  1246. DEBUG((MNGRP, DPLVL, "UNBLD: Found FORTYP")) ;
  1247. pfornode = (struct fornode *) pnode ;
  1248. //
  1249. // Put in the FOR keywoard, arguements and list
  1250. //
  1251. SPutC( pbCmdBuf, pfornode->cmdline,YSPC) ;
  1252. SPutC( pbCmdBuf, LEFTPSTR,YSPC) ;
  1253. SPutC( pbCmdBuf, pfornode->arglist,NSPC) ;
  1254. SPutC( pbCmdBuf, RPSTR,NSPC) ;
  1255. SPutC( pbCmdBuf, pfornode->cmdline+DOPOS,YSPC) ;
  1256. //
  1257. // Now get the for body
  1258. //
  1259. UnBuild(pfornode->body, pbCmdBuf) ;
  1260. break ;
  1261. case IFTYP:
  1262. DEBUG((MNGRP, DPLVL, "UNBLD: Found IFTYP")) ;
  1263. //
  1264. // put ine IF keyword
  1265. pifnode = (struct ifnode *) pnode ;
  1266. SPutC( pbCmdBuf, pifnode->cmdline,YSPC) ;
  1267. //
  1268. // Get the condition part of the statement
  1269. //
  1270. UnBuild((struct node *)pifnode->cond, pbCmdBuf) ;
  1271. //
  1272. // Unbuild the body of the IF
  1273. //
  1274. UnBuild(pifnode->ifbody, pbCmdBuf) ;
  1275. if (pifnode->elsebody) {
  1276. SPutC( pbCmdBuf, pifnode->elseline,YSPC) ;
  1277. UnBuild(pifnode->elsebody, pbCmdBuf) ;
  1278. } ;
  1279. break ;
  1280. case NOTTYP:
  1281. DEBUG((MNGRP, DPLVL, "UNBLD: Found NOTTYP")) ;
  1282. pcmdnode = (struct cmdnode *) pnode ;
  1283. SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC) ;
  1284. UnBuild((struct node *)pcmdnode->argptr, pbCmdBuf) ;
  1285. break ;
  1286. case REMTYP:
  1287. case CMDTYP:
  1288. case ERRTYP:
  1289. case EXSTYP:
  1290. case STRTYP:
  1291. DEBUG((MNGRP, DPLVL, "UNBLD: Found CMDTYP")) ;
  1292. pcmdnode = (struct cmdnode *) pnode ;
  1293. SPutC( pbCmdBuf, pcmdnode->cmdline,YSPC) ;
  1294. if (pcmdnode->argptr)
  1295. SPutC( pbCmdBuf, pcmdnode->argptr,NSPC) ;
  1296. UnDuRd((struct node *)pcmdnode, pbCmdBuf) ;
  1297. break ;
  1298. case HELPTYP:
  1299. DEBUG((MNGRP, DPLVL, "UNBLD: Found HELPTYP")) ;
  1300. if (LastMsgNo == MSG_HELP_FOR) {
  1301. SPutC( pbCmdBuf, TEXT("FOR /?"), YSPC) ;
  1302. }
  1303. else if (LastMsgNo == MSG_HELP_IF) {
  1304. SPutC( pbCmdBuf, TEXT("IF /?"), YSPC) ;
  1305. }
  1306. else if (LastMsgNo == MSG_HELP_REM) {
  1307. SPutC( pbCmdBuf, TEXT("REM /?"), YSPC) ;
  1308. }
  1309. else {
  1310. DEBUG((MNGRP, DPLVL, "UNBLD: Unknown Type!")) ;
  1311. longjmp(CmdJBuf2,-1) ;
  1312. }
  1313. break;
  1314. default:
  1315. DEBUG((MNGRP, DPLVL, "UNBLD: Unknown Type!")) ;
  1316. longjmp(CmdJBuf2,-1) ;
  1317. }
  1318. return(SUCCESS) ;
  1319. }
  1320. void
  1321. UnDuRd(
  1322. IN struct node *pnode,
  1323. IN PTCHAR pbCmdBuf
  1324. )
  1325. /*++
  1326. Routine Description:
  1327. Unparse any input or output redirection associated with the
  1328. current node.
  1329. Arguments:
  1330. pnode - current parse tree node
  1331. pbCmdBuf - buffer holding command
  1332. Return Value:
  1333. --*/
  1334. {
  1335. struct relem *prelem ;
  1336. TCHAR tmpstr[2] ;
  1337. DEBUG((MNGRP, DPLVL, "UNDURD: Entered")) ;
  1338. tmpstr[1] = NULLC ;
  1339. prelem = pnode->rio ;
  1340. while (prelem) {
  1341. //
  1342. // BUGBUG this makes big time assumption about size of
  1343. // handle
  1344. //
  1345. tmpstr[0] = (TCHAR)prelem->rdhndl + (TCHAR)'0' ;
  1346. SPutC( pbCmdBuf, tmpstr,YSPC) ;
  1347. if (prelem->rdop == INOP)
  1348. SPutC( pbCmdBuf, INSTR,NSPC) ;
  1349. else
  1350. SPutC( pbCmdBuf, prelem->flag ? APPSTR : OUTSTR,NSPC) ;
  1351. SPutC( pbCmdBuf, prelem->fname,NSPC) ;
  1352. prelem = prelem->nxt ;
  1353. }
  1354. }
  1355. void SPutC(
  1356. IN PTCHAR pbCmdBuf,
  1357. IN PTCHAR pszInput,
  1358. IN int flg
  1359. )
  1360. /*++
  1361. Routine Description:
  1362. If within length limits, add the current substring to the
  1363. command under construction delimiting with a space.
  1364. Arguments:
  1365. pbCmdBuf - Where to put string
  1366. pszInputString - String to put in pbCmdBuf
  1367. flg - Flags controling placement of spaces
  1368. Return Value:
  1369. --*/
  1370. {
  1371. DEBUG((MNGRP, DPLVL, "SPutC: Entered, Adding '%ws'",pszInput)) ;
  1372. if ((mystrlen(pbCmdBuf) + mystrlen(pszInput) + 1) > MAXTOKLEN) {
  1373. PutStdErr(MSG_LINES_TOO_LONG, NOARGS);
  1374. longjmp(CmdJBuf2,-1) ;
  1375. }
  1376. if (flg && (*(pbCmdBuf+mystrlen(pbCmdBuf)-1) != SPACE) && (*pszInput != SPACE)) {
  1377. SpaceCat(pbCmdBuf,pbCmdBuf,pszInput) ;
  1378. } else {
  1379. mystrcat(pbCmdBuf,pszInput) ;
  1380. }
  1381. }