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.

1923 lines
58 KiB

  1. /*++
  2. Copyright (c) 1988-1999 Microsoft Corporation
  3. Module Name:
  4. cparse.c
  5. Abstract:
  6. Command parsing
  7. --*/
  8. #include "cmd.h"
  9. /*
  10. NT Command Interpreter
  11. This file contains all of the routines which make up Command's
  12. parser. The main routines below have names like ParseXXX where
  13. XXX refers to the name of the production that routine is supposed
  14. to parse.
  15. None of the routines in this file, except Parser, will return if
  16. they detect a syntax error. Instead, they will all call PSError() or
  17. PError(). These routines do a longjmp() back to Parser.
  18. Command Language Grammar:
  19. statement -> s0
  20. s0 -> s1 "&" s0 | s1
  21. s1 -> s2 "||" s1 | s2
  22. s2 -> s3 "&&" s2 | s3
  23. s3 -> s4 "|" s3 | s4
  24. s4 -> redir s5 | s5 redir | s5
  25. s5 -> "(" statement ")" |
  26. "@" statement |
  27. FOR var IN "(" arglist ")" DO s1 |
  28. IF condition statement ELSE statement |
  29. IF condition statement |
  30. cmd arglist
  31. var -> "%"c | "%%"c
  32. c -> any-character
  33. arglist -> (arg)*
  34. arg -> any-string
  35. condition -> NOT cond | cond
  36. cond -> ERRORLEVEL n | arg == arg | EXIST fname
  37. n -> any-number
  38. fname -> any-file-name
  39. cmd -> internal-command | external-command
  40. redir -> in out | out in
  41. in -> "<" arg | epsilon
  42. out -> ( ">" | ">>" ) arg | epsilon
  43. Operator precedence from lowest to highest:
  44. & Command Separator
  45. || Or Operator
  46. && And Operator
  47. | Pipe Operator
  48. < > >> I/O Redirectors
  49. () Command Grouper
  50. Examples:
  51. x & y | z => x & (y | z)
  52. x | y & z => (x | y) & z
  53. x || y | z => x || (y | z)
  54. a & b || c && d | e || f => a & ((b || (c && (d | e))) || f)
  55. */
  56. extern jmp_buf CmdJBuf1 ; /* Used by setjmp() and longjmp() */
  57. extern TCHAR TmpBuf[] ; /* The 1st 1/2 of this is used as the token buffer */
  58. /* M007 */
  59. TCHAR *TokBuf = TmpBuf ; /* Current token buffer */ /* M007 */
  60. unsigned TokTyp ; /* " " " " type */
  61. unsigned TokLen ; /* " " " " length */
  62. extern unsigned DebGroup ;
  63. extern unsigned DebLevel ;
  64. extern TCHAR Delimiters[] ; /* M022 */
  65. /* Command strings */
  66. extern TCHAR ForStr[], IfStr[], DetStr[], InStr[], DoStr[], ElseStr[], ErrStr[] ;
  67. extern TCHAR ForHelpStr[], IfHelpStr[], RemHelpStr[];
  68. extern TCHAR ForLoopStr[];
  69. extern TCHAR ForDirTooStr[];
  70. extern TCHAR ForParseStr[];
  71. extern TCHAR ForRecurseStr[];
  72. extern TCHAR ExsStr[], NotStr[] ;
  73. extern TCHAR CmdExtVerStr[], DefinedStr[] ;
  74. extern TCHAR RemStr[] ; /* M002 */
  75. extern TCHAR ExtprocStr[] ; /* M023 */
  76. extern TCHAR Fmt19[] ; /* M018 */
  77. extern TCHAR Fmt00[] ; /* @@4 */
  78. int NulNode = FALSE ; /* M018 */
  79. unsigned global_dfvalue; /* @@4 */
  80. BOOLEAN fDumpTokens;
  81. BOOLEAN fDumpParse;
  82. void DuOp(TCHAR *op, struct node *n, int pad);
  83. void DuRd(struct node *n);
  84. /*** DumpParseTree - display a parse tree
  85. *
  86. * Purpose:
  87. * Display a parse tree in a reasonable format.
  88. *
  89. * DumpParseTree(struct node *n, int pad)
  90. *
  91. * Args:
  92. * n - the root of the tree to be displayed
  93. * pad - the number of spaces to indent the listing of the current node
  94. *
  95. * Notes:
  96. * This program is only compiled when DBG is defined.
  97. *
  98. */
  99. void
  100. DumpParseTree( struct node *n, int pad)
  101. {
  102. struct cmdnode *c ; /* All node ptrs are used to display that node type */
  103. struct fornode *f ;
  104. struct ifnode *i ;
  105. int j ; /* Used to pad output */
  106. if (!n)
  107. return ;
  108. for (j = 0 ; j < pad ; j++)
  109. cmd_printf(Fmt19, SPACE) ; /* M018 */
  110. pad += 2 ;
  111. switch (n->type) {
  112. case LFTYP:
  113. DuOp(TEXT("CRLF"), n, pad-2) ;
  114. break ;
  115. case CSTYP:
  116. DuOp(CSSTR, n, pad) ;
  117. break ;
  118. case ORTYP:
  119. DuOp(ORSTR, n, pad) ;
  120. break ;
  121. case ANDTYP:
  122. DuOp(ANDSTR, n, pad) ;
  123. break ;
  124. case PIPTYP:
  125. DuOp(PIPSTR, n, pad) ;
  126. break ;
  127. case PARTYP:
  128. DuOp(LEFTPSTR, n, pad) ;
  129. break ;
  130. /* M015 - Added new type
  131. */
  132. case SILTYP:
  133. DuOp(SILSTR, n, pad) ;
  134. break ;
  135. case FORTYP:
  136. f = (struct fornode *) n ;
  137. cmd_printf(TEXT("%s (%s) %s\n"), f->cmdline, f->arglist, f->cmdline+DOPOS) ; /* M010 */
  138. DumpParseTree(f->body, pad) ;
  139. break ;
  140. case IFTYP:
  141. i = (struct ifnode *) n ;
  142. cmd_printf(TEXT("%s\n"), i->cmdline) ; /* M010 */
  143. DumpParseTree((struct node *)i->cond, pad) ;
  144. DumpParseTree(i->ifbody, pad) ;
  145. if (i->elsebody) {
  146. for (j = 0 ; j < pad-2 ; j++)
  147. cmd_printf(Fmt19, SPACE) ; /* M018 */
  148. cmd_printf(TEXT("%s\n"), i->elseline) ; /* M010 */
  149. DumpParseTree(i->elsebody, pad) ;
  150. } ;
  151. break ;
  152. case NOTTYP:
  153. c = (struct cmdnode *) n ;
  154. cmd_printf(TEXT("%s\n"), c->cmdline) ; /* M010 */
  155. DumpParseTree((struct node *)c->argptr, pad) ;
  156. break ;
  157. case REMTYP: /* M002 - New REM type */
  158. case CMDTYP:
  159. case ERRTYP:
  160. case EXSTYP:
  161. case STRTYP:
  162. case CMDVERTYP:
  163. case DEFTYP:
  164. c = (struct cmdnode *) n ;
  165. cmd_printf( TEXT("Cmd: %s Type: %x "), c->cmdline, c->type) ; /* M010 */
  166. if (c->argptr)
  167. cmd_printf( TEXT("Args: `%s' "), c->argptr) ; /* M010 */
  168. DuRd((struct node *)c) ;
  169. break ;
  170. default:
  171. cmd_printf(TEXT("*** Unknown type: %x\n"), n->type) ; /* M010 */
  172. } ;
  173. }
  174. void
  175. DuOp(TCHAR *op, struct node *n, int pad)
  176. {
  177. cmd_printf( TEXT("%s "), op) ; /* M010 */
  178. DuRd(n) ;
  179. DumpParseTree(n->lhs, pad) ;
  180. DumpParseTree(n->rhs, pad) ;
  181. }
  182. /* M013 - The DuRd function has been extensively modified to conform
  183. * to new data structures for redirection.
  184. * M014 - Altered to handle input redirection for other than handle 0.
  185. */
  186. void
  187. DuRd(struct node *n)
  188. {
  189. struct relem *tmp ;
  190. if (tmp = n->rio)
  191. CmdPutString( TEXT("Redir: ")) ;
  192. while (tmp) {
  193. cmd_printf(TEXT(" %x %c"), tmp->rdhndl, tmp->rdop) ;
  194. if (tmp->flag)
  195. CmdPutString( TEXT( ">" )) ;
  196. CmdPutString( tmp->fname );
  197. tmp = tmp->nxt ;
  198. } ;
  199. CmdPutString( CrLf );
  200. }
  201. /*** Parser - controls statement parsing
  202. *
  203. * Purpose:
  204. * Initialize the lexer, free memory previously used by the parser, and
  205. * call Parse to parse the next statement.
  206. *
  207. * struct node *Parser(unsigned dfvalue, INT_PTR dpvalue, int fsarg)
  208. *
  209. * Args:
  210. * dfvalue - the value to be assigned to DataFlag by InitLex
  211. * dpvalue - the value to be assigned to DataPtr by InitLex
  212. * fsarg - passed to FreeStack, tells how many elements should be left on
  213. * the data stack
  214. *
  215. * Returns:
  216. * The root of the parse tree of the statement just parsed or PARSERROR
  217. * if an error was detected.
  218. *
  219. */
  220. struct node *ParseStatement(int type) ;
  221. #define MAX_STATEMENT_DEPTH 256
  222. int StatementDepth;
  223. BYTE StatementType[ MAX_STATEMENT_DEPTH ];
  224. int PendingParens;
  225. struct node *Parser(dfvalue, dpvalue, fsarg)
  226. unsigned dfvalue ;
  227. INT_PTR dpvalue ;
  228. int fsarg ;
  229. {
  230. struct node *n ; /* Root of the parse tree */
  231. INT_PTR retcode ; /* setjmp()'s return code */
  232. unsigned GeToken() ;
  233. DEBUG((PAGRP, PALVL, "PARSER: Entered.")) ;
  234. global_dfvalue = dfvalue; /* @@4 */
  235. FreeStack(fsarg) ; /* Free unneeded memory */
  236. InitLex(dfvalue, dpvalue) ; /* Initialize the lexer */
  237. if ((retcode = setjmp(CmdJBuf1)) != 0)
  238. return((struct node *) retcode) ; /* Return a parser error code */
  239. StatementDepth = 0;
  240. PendingParens = 0;
  241. n = ParseStatement(0) ;
  242. /* Error if any data left in the buffer. N is NULL then a blank line was
  243. * found. In this case, it's alright that the buffer isn't empty.
  244. */
  245. /* M011 - Ref to IsData() was ref to Peek()
  246. * M018 - Reorganized to save null/non-null state of last node.
  247. */
  248. if (n) {
  249. if(IsData() && GeToken(GT_NORMAL) != NLN && TokTyp != EOS)
  250. PSError() ;
  251. NulNode = FALSE ;
  252. } else {
  253. NulNode = TRUE ;
  254. } ;
  255. if (fDumpParse)
  256. DumpParseTree(n, 0) ;
  257. DEBUG((PAGRP, PALVL, "PARSER: Exited.")) ;
  258. return(n) ;
  259. }
  260. /*** ParseStatement - parse the statement production
  261. *
  262. * Purpose:
  263. * Parse the statement production.
  264. *
  265. * struct node *ParseStatement()
  266. *
  267. * Returns:
  268. * A pointer to the statement parsed or PARSERROR.
  269. *
  270. * Note:
  271. * Removed the tests for and calls to parse FOR, IF, DET and REM
  272. * from ParseStatement to ParseS5 (M012).
  273. *
  274. */
  275. extern int AtIsToken; /* @@4 */
  276. extern int ColonIsToken;
  277. struct node *ParseStatement(int type)
  278. {
  279. struct node *n;
  280. #ifdef USE_STACKAVAIL
  281. if (stackavail() > MINSTACKNEED) { /* @WM1 Is there enough stack left? */
  282. #endif
  283. StatementType[StatementDepth++] = (UCHAR)type;
  284. if (type == PARTYP)
  285. PendingParens += 1;
  286. AtIsToken = 1; /* @@4 */
  287. GeToken(GT_LPOP) ; /* M000 - Was GT_NORMAL */
  288. AtIsToken = 0; /* @@4 */
  289. DEBUG((PAGRP, PALVL, "PST: Entered, token = `%ws'", TokBuf)) ;
  290. if (TokTyp == EOS)
  291. longjmp(CmdJBuf1, EOF) ;
  292. n = ParseS0() ;
  293. StatementDepth -= 1;
  294. if (type == PARTYP)
  295. PendingParens -= 1;
  296. return n;
  297. #ifdef USE_STACKAVAIL
  298. } else {
  299. PutStdErr( MSG_TRAPC, ONEARG, Fmt00 ); /* @@4 */
  300. Abort(); /* @@4 No, return error to user */
  301. }
  302. #endif
  303. }
  304. /*** ParseFor - parse for loops
  305. *
  306. * Purpose:
  307. * Parse a FOR statement.
  308. *
  309. * Returns:
  310. * A pointer to a parsed FOR statement or PARSERROR.
  311. *
  312. * struct node *ParseFor()
  313. *
  314. * Notes:
  315. * Microsoft's DOS manual says for loop vars can't be digits but the IBM
  316. * documentation makes no such restriction. Because my for/batch var
  317. * substitution function will have no problem with it, I have taken out
  318. * the check for that. - EKE
  319. * - M022 * Changed FOR parser to treat all cases of FOR variable the
  320. * same now that variable substitution is done in the lexer. Note
  321. * that all commands will now look the same whether in batch files or
  322. * on the command line.
  323. *
  324. */
  325. struct node *ParseFor()
  326. {
  327. struct fornode *n ; /* Holds ptr to the for node to be built and filled */
  328. struct cmdnode *LoadNodeTC() ;
  329. BOOL Help=FALSE;
  330. DEBUG((PAGRP, PALVL, "PFOR: Entered.")) ;
  331. // if "for/?", convert to "for /?"
  332. if (_tcsicmp(ForHelpStr, TokBuf) == 0) {
  333. TokBuf[_tcslen(ForStr)] = NULLC;
  334. Help=TRUE;
  335. }
  336. TokLen = FORLINLEN ; /* NEEDED for LoadNodeTC call following */
  337. n = (struct fornode *) LoadNodeTC(FORTYP) ;
  338. /* Get and verify the for loop variable */
  339. if (Help) {
  340. TokBuf[0] = SWITCHAR;
  341. TokBuf[1] = QMARK;
  342. TokBuf[2] = NULLC;
  343. } else {
  344. GeToken(GT_NORMAL) ;
  345. }
  346. if (TokBufCheckHelp( TokBuf, FORTYP ) ) {
  347. n->type = HELPTYP ;
  348. n->cmdline = NULL;
  349. return((struct node *) n) ;
  350. //Abort();
  351. }
  352. n->flag = 0;
  353. //
  354. // If extensions are enabled, check for additional forms of the FOR
  355. // statement, all identified by a switch character after the FOR
  356. // keyword.
  357. //
  358. if (fEnableExtensions) {
  359. while (TRUE) {
  360. //
  361. // FOR /L %i in (start,step,end) do
  362. //
  363. if (_tcsicmp(ForLoopStr, TokBuf) == 0) {
  364. n->flag |= FOR_LOOP;
  365. GeToken(GT_NORMAL) ;
  366. continue;
  367. } else
  368. //
  369. // FOR /D %i in (set) do
  370. //
  371. if (_tcsicmp(ForDirTooStr, TokBuf) == 0) {
  372. n->flag |= FOR_MATCH_DIRONLY;
  373. GeToken(GT_NORMAL) ;
  374. continue;
  375. } else
  376. //
  377. // FOR /F ["parse options"] %i in (set) do
  378. //
  379. if (_tcsicmp(ForParseStr, TokBuf) == 0) {
  380. n->flag |= FOR_MATCH_PARSE;
  381. GeToken(GT_NORMAL) ;
  382. //
  383. // If next token does not begin with % then must be
  384. // parse options
  385. //
  386. if (*TokBuf != PERCENT && *TokBuf != SWITCHAR) {
  387. if (n->parseOpts != NULL) {
  388. PSError( );
  389. }
  390. n->parseOpts = gmkstr((TokLen+3)*sizeof(TCHAR)) ;
  391. mystrcpy(n->parseOpts, TokBuf) ;
  392. GeToken(GT_NORMAL) ;
  393. }
  394. continue;
  395. } else
  396. //
  397. // FOR /R [directoryPath] %i in (set) do
  398. //
  399. if (_tcsicmp(ForRecurseStr, TokBuf) == 0) {
  400. n->flag |= FOR_MATCH_RECURSE;
  401. GeToken(GT_NORMAL) ;
  402. //
  403. // If next token does not begin with % then must be
  404. // directory path to start recursive walk from
  405. //
  406. if (n->recurseDir != NULL) {
  407. PSError( );
  408. }
  409. if (*TokBuf != PERCENT && *TokBuf != SWITCHAR) {
  410. n->recurseDir = gmkstr((TokLen+1)*sizeof(TCHAR)) ;
  411. mystrcpy(n->recurseDir, TokBuf) ;
  412. GeToken(GT_NORMAL) ;
  413. }
  414. continue;
  415. } else
  416. break;
  417. }
  418. //
  419. // Check for validity of switches:
  420. // FOR_LOOP with no others
  421. // FOR_MATCH_DIRONLY possibly with FOR_MATCH_RECURSE
  422. // FOR_MATCH_PARSE with no others
  423. // FOR_MATCH_RECURSE possibly with FOR_MATCH_DIRONLY
  424. //
  425. if (n->flag == FALSE
  426. || n->flag == FOR_LOOP
  427. || n->flag == FOR_MATCH_DIRONLY
  428. || n->flag == (FOR_MATCH_DIRONLY | FOR_MATCH_RECURSE)
  429. || n->flag == FOR_MATCH_PARSE
  430. || n->flag == FOR_MATCH_RECURSE
  431. ) {
  432. } else {
  433. PSError( );
  434. }
  435. }
  436. if (*TokBuf != PERCENT ||
  437. (_istspace(TokBuf[1]) || mystrchr(Delimiters, (TCHAR)(n->forvar = (unsigned)TokBuf[1]))) ||
  438. TokLen != 3) {
  439. PSError() ;
  440. } ;
  441. DEBUG((PAGRP, PALVL, "PFOR: var = %c", n->forvar)) ;
  442. SpaceCat(n->cmdline, n->cmdline, TokBuf) ; /* End of var verify */
  443. GetCheckStr(InStr) ; /* M005 */
  444. SpaceCat(n->cmdline, n->cmdline, TokBuf) ;
  445. n->arglist = BuildArgList() ;
  446. GetCheckStr(DoStr) ; /* M005 */
  447. mystrcpy(n->cmdline+DOPOS, TokBuf) ;
  448. if (!(n->body = ParseStatement(FORTYP)))
  449. PSError() ;
  450. DEBUG((PAGRP, PALVL, "PFOR: Exiting.")) ;
  451. return((struct node *) n) ;
  452. }
  453. /*** ParseIf - parse if statements
  454. *
  455. * Purpose:
  456. * Parse a IF statement.
  457. *
  458. * struct node *ParseIf()
  459. *
  460. * Returns:
  461. * A pointer to a parsed IF statement or PARSERROR.
  462. *
  463. * *** W A R N I N G ! ***
  464. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  465. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  466. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  467. *
  468. */
  469. struct node *ParseIf()
  470. {
  471. struct ifnode *n ; /* hold ptr to if node to be built and filled */
  472. BOOL Help=FALSE;
  473. int fIgnoreCase;
  474. DEBUG((PAGRP, PALVL, "PIF: Entered.")) ;
  475. // if "if/?", convert to "if /?"
  476. if (_tcsicmp(IfHelpStr, TokBuf) == 0) {
  477. TokBuf[_tcslen(IfStr)] = NULLC;
  478. Help=TRUE;
  479. }
  480. n = (struct ifnode *) LoadNodeTC(IFTYP) ;
  481. if (Help) {
  482. TokBuf[0] = SWITCHAR;
  483. TokBuf[1] = QMARK;
  484. TokBuf[2] = NULLC;
  485. } else {
  486. GeToken(GT_NORMAL) ;
  487. }
  488. //
  489. // Check for help flag
  490. //
  491. if (TokBufCheckHelp(TokBuf, IFTYP)) {
  492. n->type = HELPTYP ;
  493. n->cmdline = NULL;
  494. return((struct node *) n) ;
  495. // Abort();
  496. } else {
  497. fIgnoreCase = FALSE;
  498. //
  499. // If extensions are enabled, check for the /I switch which
  500. // specifies case insensitive comparison.
  501. //
  502. if (fEnableExtensions && !_tcsicmp(TokBuf, TEXT("/I"))) {
  503. fIgnoreCase = TRUE;
  504. } else
  505. //
  506. // if no help flag then put it all back and
  507. // have ParseCond refetch token
  508. //
  509. Lex(LX_UNGET,0) ;
  510. }
  511. n->cond = ParseCond(PC_NOTS) ;
  512. if (n->cond && fIgnoreCase)
  513. if (n->cond->type != NOTTYP)
  514. n->cond->flag = CMDNODE_FLAG_IF_IGNCASE;
  515. else
  516. ((struct cmdnode *)(n->cond->argptr))->flag = CMDNODE_FLAG_IF_IGNCASE;
  517. if (!(n->ifbody = ParseStatement(IFTYP)))
  518. PSError() ;
  519. if (IsData()) { /* M011 - Was Peek() */
  520. GeToken(GT_NORMAL) ;
  521. if (_tcsicmp(ElseStr, TokBuf) == 0) {
  522. DEBUG((PAGRP, PALVL, "PIF: Found else.")) ;
  523. n->elseline = gmkstr(TokLen*sizeof(TCHAR)) ; /*WARNING*/
  524. mystrcpy(n->elseline, TokBuf) ;
  525. if (!(n->elsebody = ParseStatement(IFTYP)))
  526. PSError() ;
  527. } else
  528. Lex(LX_UNGET,0) ;
  529. } ;
  530. DEBUG((PAGRP, PALVL, "PIF: Entered.")) ;
  531. return((struct node *) n) ;
  532. }
  533. /*** ParseRem - parse REM statements (M002 - New function)
  534. *
  535. * Purpose:
  536. * Parse a REM statement.
  537. *
  538. * struct node *ParseRem()
  539. *
  540. * Returns:
  541. * A pointer to a parsed REM statement.
  542. * Returns FAILURE if not able to allocate memory
  543. *
  544. *
  545. * *** W A R N I N G ! ***
  546. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  547. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  548. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  549. */
  550. struct node *ParseRem()
  551. {
  552. struct cmdnode *n ; /* Ptr to REM node to build/fill */
  553. BOOL Help=FALSE;
  554. DEBUG((PAGRP, PALVL, "PREM: Entered.")) ;
  555. // if rem/?, convert to rem /?
  556. if (_tcsicmp(RemHelpStr, TokBuf) == 0) {
  557. TokBuf[_tcslen(RemStr)] = NULLC;
  558. Help=TRUE;
  559. }
  560. n = LoadNodeTC(REMTYP) ;
  561. if (Help) {
  562. TokBuf[0] = SWITCHAR;
  563. TokBuf[1] = QMARK;
  564. TokBuf[2] = NULLC;
  565. } else {
  566. GeToken(GT_NORMAL) ;
  567. }
  568. //
  569. // Check for help flag
  570. //
  571. if (TokBufCheckHelp(TokBuf, REMTYP)) {
  572. n->type = HELPTYP ;
  573. n->cmdline = NULL;
  574. return((struct node *) n) ;
  575. //Abort();
  576. } else {
  577. //
  578. // if no help flag then put it all back and
  579. // have ParseCond refetch token
  580. //
  581. Lex(LX_UNGET,0) ;
  582. }
  583. if (IsData()) { /* Read in args, if any (M011 - Was Peek()) */
  584. if (GeToken(GT_REM) == TEXTOKEN) {
  585. n->argptr = gmkstr(TokLen*sizeof(TCHAR)) ; /*WARNING*/
  586. mystrcpy(n->argptr, TokBuf) ;
  587. DEBUG((PAGRP, PALVL, "PREM: args = `%ws'", n->argptr)) ;
  588. } else
  589. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken() */
  590. } ;
  591. DEBUG((PAGRP, PALVL, "PREM: Exited.")) ;
  592. return((struct node *) n) ;
  593. }
  594. /*** ParseS0 - parse production s0
  595. *
  596. * Purpose:
  597. * Parse the s0 production.
  598. *
  599. * struct node *ParseS0()
  600. *
  601. * Returns:
  602. * A pointer to the production just parsed or PARSERROR.
  603. *
  604. * Notes:
  605. * If a parenthesised statement group is NOT being parsed, NLN
  606. * is consider to be an empty side of a command separator so NULL is
  607. * returned.
  608. *
  609. */
  610. struct node *ParseS0()
  611. {
  612. DEBUG((PAGRP, PALVL, "PS0: Entered.")) ;
  613. if (!ColonIsToken && TokTyp == TEXTOKEN && TokBuf[0] == COLON) {
  614. do {
  615. GeToken(GT_NORMAL) ;
  616. } while (TokBuf[0] != NULLC && TokBuf[0] != NLN);
  617. if (StatementType[StatementDepth-1] != PARTYP) {
  618. return(NULL) ;
  619. }
  620. GeToken(GT_NORMAL) ;
  621. }
  622. if (StatementType[StatementDepth-1] != PARTYP) {
  623. if (TokTyp == TEXTOKEN && !_tcsicmp(TokBuf, RPSTR)) {
  624. //
  625. // If a goto was done inside a parenthesized list of statements
  626. // we will eventually hit the terminating right paren. Skip over
  627. // it so we dont declare an error
  628. //
  629. do {
  630. GeToken(GT_NORMAL) ;
  631. } while (TokBuf[0] != NULLC && TokBuf[0] != NLN);
  632. }
  633. if (TokTyp == NLN) {
  634. DEBUG((PAGRP, PALVL, "PS0: Returning null statement.")) ;
  635. return(NULL) ;
  636. }
  637. }
  638. return(BinaryOperator(CSSTR, CSTYP, (PPARSE_ROUTINE)ParseS0, (PPARSE_ROUTINE)ParseS1)) ;
  639. }
  640. /*** ParseS1 - parse production s1
  641. *
  642. * Purpose:
  643. * Parse the s1 production.
  644. *
  645. * struct node *ParseS1()
  646. *
  647. * Returns:
  648. * A pointer to the production just parsed or PARSERROR.
  649. *
  650. */
  651. struct node *ParseS1()
  652. {
  653. DEBUG((PAGRP, PALVL, "PS1: Entered.")) ;
  654. return(BinaryOperator(ORSTR, ORTYP, (PPARSE_ROUTINE)ParseS1, (PPARSE_ROUTINE)ParseS2)) ;
  655. }
  656. /*** ParseS2 - parse production s2
  657. *
  658. * Purpose:
  659. * Parse the s2 production.
  660. *
  661. * struct node *ParseS2()
  662. *
  663. * Returns:
  664. * A pointer to the production just parsed or PARSERROR.
  665. *
  666. */
  667. struct node *ParseS2()
  668. {
  669. DEBUG((PAGRP, PALVL, "PS2: Entered.")) ;
  670. return(BinaryOperator(ANDSTR, ANDTYP, (PPARSE_ROUTINE)ParseS2, (PPARSE_ROUTINE)ParseS3)) ;
  671. }
  672. /*** ParseS3 - parse production s3
  673. *
  674. * Purpose:
  675. * Parse the s3 production.
  676. *
  677. * struct node *ParseS3()
  678. *
  679. * Returns:
  680. * A pointer to the production just parsed or PARSERROR.
  681. *
  682. */
  683. struct node *ParseS3()
  684. {
  685. DEBUG((PAGRP, PALVL, "PS3: Entered.")) ;
  686. return(BinaryOperator(PIPSTR, PIPTYP, (PPARSE_ROUTINE)ParseS3, (PPARSE_ROUTINE)ParseS4)) ;
  687. }
  688. /*** ParseS4 - parse production s4
  689. *
  690. * Purpose:
  691. * Parse the s4 production.
  692. *
  693. * struct node *ParseS4()
  694. *
  695. * Returns:
  696. * A pointer to the production just parsed or PARSERROR.
  697. *
  698. * Notes:
  699. * M013 - Almost the whole of this function was rewritten to
  700. * conform to new structures and methods of redirection parsing.
  701. * The primary data item is a linked list of structures which
  702. * identify the individual redirection commands.
  703. */
  704. struct node *ParseS4()
  705. {
  706. struct node *n ; /* Node ptr to add redir info to */
  707. struct relem *io = NULL ;
  708. struct relem *tmpio ;
  709. int flg = 0;
  710. int i ;
  711. DEBUG((PAGRP, PALVL, "PS4: Entered.")) ;
  712. /* Parse leading redirection receiving in return a list of redirection
  713. * structure elements pointed to by io. Get a new token for ParseS5
  714. * if necessary (ParseRedir successful).
  715. */
  716. if (ParseRedir(&io)) { /* M013 - Now use list of structs */
  717. GeToken(GT_LPOP) ; /* M011 - '(' Now operator */
  718. DEBUG((PAGRP,PALVL,"PS4: List ptr io = %04x",io)) ;
  719. } ;
  720. DEBUG((PAGRP, PALVL, "PS4: Calling PS5.")) ;
  721. n = ParseS5() ;
  722. DEBUG((PAGRP,PALVL,"PS4: Post PS5 io= %04x, n->rio= %04x",io,n->rio)) ;
  723. /* If more redirection was found beyond ParseS5 (in ParseCmd), n-rio in
  724. * the node will point to another list. If two lists exist, integrate
  725. * them, giving priority to the later one by forcing them to be in
  726. * chronological order (io becomes n->rio and n->rio is appended).
  727. *
  728. * NOTE: FOR and IF nodes are explicitly barred from having leading
  729. * redirection (use will result in a Syntax Error). This restriction may
  730. * later be removed by inserting code to walk the parse tree and insert
  731. * the leading redirection in the first non-IF/FOR/DET node.
  732. */
  733. if (io) { /* If leading redirection... */
  734. DEBUG((PAGRP,PALVL,"PS4: Have leading redirection.")) ;
  735. if (n->type == FORTYP ||
  736. n->type == IFTYP) {
  737. DEBUG((PAGRP,PALVL,"PS4: n=IF/FOR !!ERROR!!")) ;
  738. mystrcpy(TokBuf,((struct cmdnode *)n)->cmdline) ;
  739. PSError() ;
  740. } ;
  741. tmpio = n->rio ; /* ...save possible Cmd redir... */
  742. n->rio = io ; /* ...install leading redir... */
  743. if (tmpio) { /* ...and if Cmd redirection... */
  744. DEBUG((PAGRP,PALVL,"PS4: Have Cmd redirection.")) ;
  745. while (io->nxt) /* ...find list end... */
  746. io = io->nxt ;
  747. io->nxt = tmpio ; /* ...and install it */
  748. } ;
  749. } ;
  750. /* The nodes n->rio field points to the head of a single list (or NULL
  751. * if no redirection). If further input remains in Lexer buffer, a
  752. * new list is created from that and appended to any existing one.
  753. */
  754. DEBUG((PAGRP,PALVL,"PS4: After fixup, n->rio = %04x",n->rio)) ;
  755. if (IsData()) { /* If data remains in buffer */
  756. DEBUG((PAGRP, PALVL, "PS4: Doing 2nd ParseRedir call.")) ;
  757. GeToken(GT_NORMAL) ; /* Get token for ParseRedir... */
  758. io = NULL ; /* ...zero the pointer & call */
  759. if (ParseRedir(&io)) { /* If redir, then... */
  760. if (tmpio = n->rio) { /* ...fix list */
  761. while (tmpio->nxt)
  762. tmpio = tmpio->nxt ;
  763. tmpio->nxt = io ;
  764. } else
  765. n->rio = io ;
  766. } else /* Else, if no redir... */
  767. Lex(LX_UNGET,0) ; /* ...unget the token. */
  768. } ;
  769. #if DBG
  770. if (io = n->rio) {
  771. i = 0 ;
  772. while (io) {
  773. DEBUG((PAGRP,PALVL,"PS4: RD#%d - io is at %04x",i,io)) ;
  774. DEBUG((PAGRP,PALVL,"PS4: RD#%d - io->rdhndl = %04x",i,io->rdhndl)) ;
  775. DEBUG((PAGRP,PALVL,"PS4: RD#%d - io->fname = %ws",i,io->fname)) ;
  776. DEBUG((PAGRP,PALVL,"PS4: RD#%d - io->flag = %d",i,io->flag)) ;
  777. DEBUG((PAGRP,PALVL,"PS4: RD#%d - io->rdop = %c",i,io->rdop)) ;
  778. DEBUG((PAGRP,PALVL,"PS4: RD#%d - io->nxt = %04x",i,io->nxt)) ;
  779. io = io->nxt ;
  780. ++i ;
  781. } ;
  782. } ;
  783. #endif
  784. /* The pointer n->rio heads a single list of redirection structures with
  785. * possible duplicates for a single handle. The code below eliminates
  786. * any duplicates giving priority to the later of the two.
  787. */
  788. if (tmpio = n->rio) {
  789. while (tmpio) {
  790. i = 1 << tmpio->rdhndl ;
  791. if (flg & i) {
  792. i = tmpio->rdhndl ;
  793. tmpio = n->rio ;
  794. while (tmpio) {
  795. if (i == tmpio->rdhndl) {
  796. if (tmpio == n->rio)
  797. n->rio = tmpio->nxt ;
  798. else
  799. io->nxt = tmpio->nxt ;
  800. flg = 0 ;
  801. tmpio = n->rio ;
  802. break ;
  803. } ;
  804. io = tmpio ;
  805. tmpio = io->nxt ;
  806. } ;
  807. continue ;
  808. } else
  809. flg |= i ;
  810. io = tmpio ;
  811. tmpio = io->nxt ;
  812. } ;
  813. } ;
  814. DEBUG((PAGRP, PALVL, "PS4: Redir handles flag = %02x",flg)) ;
  815. DEBUG((PAGRP, PALVL, "PS4: Redir list = %04x",n->rio)) ;
  816. DEBUG((PAGRP, PALVL, "PS4: Exited")) ;
  817. return(n) ;
  818. }
  819. /*** ParseS5 - parse production s5
  820. *
  821. * Purpose:
  822. * Parse the s5 production.
  823. *
  824. * struct node *ParseS5()
  825. *
  826. * Returns:
  827. * A pointer to the production just parsed or PARSERROR.
  828. *
  829. */
  830. struct node *ParseS5()
  831. {
  832. struct node *n ; /* Ptr to paren group node to build and fill */
  833. DEBUG((PAGRP, PALVL, "PS5: Entered, TokTyp = %04x", TokTyp)) ;
  834. /* M012 - Moved functionality for parsing FOR, IF and REM to
  835. * ParseS5 from ParseStatement to give these four commands a
  836. * lower precedence than the operators.
  837. */
  838. if (TokTyp == TEXTOKEN) {
  839. if ((_tcsicmp(ForStr, TokBuf) == 0) ||
  840. (_tcsicmp(ForHelpStr, TokBuf) == 0))
  841. return(ParseFor()) ;
  842. else if ((_tcsicmp(IfStr, TokBuf) == 0) ||
  843. (_tcsicmp(IfHelpStr, TokBuf) == 0))
  844. return(ParseIf()) ;
  845. /* M002 - Treat REM as unique command
  846. */
  847. else if ((_tcsicmp(RemStr, TokBuf) == 0) ||
  848. (_tcsicmp(RemHelpStr, TokBuf) == 0))
  849. return(ParseRem()) ; /* ...parse seperate */
  850. /* M002 ends */
  851. else {
  852. n = ParseCmd() ;
  853. // if (_tcsicmp(ExtprocStr, ((struct cmdnode *)n)->cmdline) == 0) {
  854. // n->type = EXTTYP ;
  855. //
  856. // DEBUG((PAGRP, PALVL, "PS5: Found EXTPROC type = %d", n->type)) ;
  857. //} ;
  858. return(n) ;
  859. } ;
  860. /* M015 - Added code to handle new SILTYP unary operator like left paren
  861. */
  862. } else if (*TokBuf == LPOP || *TokBuf == SILOP) { /* M015 */
  863. n = mknode() ;
  864. if (n == NULL) {
  865. Abort();
  866. }
  867. if (*TokBuf == LPOP) { /* M015 */
  868. n->type = PARTYP ;
  869. /* M004 - Strip leading newlines from the current paren group
  870. */
  871. do {
  872. GeToken(GT_NORMAL|GT_RPOP) ;
  873. } while (*TokBuf == NLN) ;
  874. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken */
  875. /* M004 ends */
  876. n->lhs = ParseStatement(PARTYP) ;
  877. } else { /* M015 */
  878. n->type = SILTYP ; /* M015 */
  879. DEBUG((PAGRP,PALVL,"PS5: Silent mode starts")) ;
  880. n->lhs = ParseStatement(0) ;
  881. } ;
  882. DEBUG((PAGRP, PALVL, "PS5: ParseStatement has returned.")) ;
  883. if (n->type == SILTYP) /* M015 */
  884. return(n) ; /* M015 */
  885. /* M015 ends */
  886. if (GeToken(GT_RPOP) == RPOP) { /* M000 - Was GT_NORMAL */
  887. return(n) ;
  888. } ;
  889. } ;
  890. DEBUG((PAGRP, PALVL, "PS5: Error, no right paren. Token = `%ws'", TokBuf)) ;
  891. PSError() ;
  892. return NULL;
  893. }
  894. /*** ParseCond - parse the condition production
  895. *
  896. * Purpose:
  897. * Parse a CONDITION production.
  898. *
  899. * struct cmdnode *ParseCond(unsigned pcflag)
  900. *
  901. * Args:
  902. * pcflag - nonzero if "NOT"s are not allowed because one has been found
  903. * already
  904. *
  905. * Returns:
  906. * A pointer to a parsed COND production or PARSERROR.
  907. *
  908. * Notes:
  909. * The token after "errorlevel" is checked to make sure it is a number.
  910. * If it isn't, a syntax error is generated.
  911. *
  912. * If a valid "NOT" is found, ParseCond() is called recursively to parse
  913. * the rest of the condition. A pointer to the node is put in the
  914. * argptr field of the node.
  915. *
  916. * M020 - "ERRORLEVEL=x" not being parsed correctly due to entire
  917. * string being lexed as single token. Now Lex first as GT_NORMAL
  918. * and if not ERRORLEVEL, re-Lex as GT_EQOK.
  919. *
  920. */
  921. struct cmdnode *ParseCond(pcflag)
  922. unsigned pcflag ;
  923. {
  924. struct cmdnode *n ; /* Ptr to cond node to build and fill */
  925. struct cmdnode *LoadNodeTC() ;
  926. DEBUG((PAGRP, PALVL, "PCOND: Entered.")) ;
  927. if (GeToken(GT_NORMAL) != TEXTOKEN) /* M020 */
  928. PSError() ;
  929. n = LoadNodeTC(0) ;
  930. if (_tcsicmp(ErrStr, TokBuf) == 0) { /* ERRORLEVEL */
  931. n->type = ERRTYP ;
  932. } else
  933. if (_tcsicmp(ExsStr, TokBuf) == 0) /* EXIST */
  934. n->type = EXSTYP ;
  935. else
  936. if (fEnableExtensions && _tcsicmp(CmdExtVerStr, TokBuf) == 0) /* CMDEXTVERSION */
  937. n->type = CMDVERTYP ;
  938. else
  939. if (fEnableExtensions && _tcsicmp(DefinedStr, TokBuf) == 0) /* DEFINED */
  940. n->type = DEFTYP ;
  941. else
  942. if (_tcsicmp(NotStr, TokBuf) == 0) { /* Not */
  943. if (pcflag)
  944. PSError() ;
  945. n->type = NOTTYP ;
  946. n->argptr = (TCHAR *) ParseCond(PC_NONOTS) ;
  947. DEBUG((PAGRP, PALVL, "PCOND: Exited, type = 0x%02x", n->type)) ;
  948. return(n);
  949. } else {
  950. Lex(LX_UNGET,0);
  951. n->type = STRTYP ; /* String comparison */
  952. ParseArgEqArg(n) ;
  953. DEBUG((PAGRP, PALVL, "PCOND: Exited, type = 0x%02x", n->type)) ;
  954. return(n);
  955. } ;
  956. /* Errorlevel, Exist, CmdExtVersion or Defined */
  957. n->argptr = TokStr(GeTexTok(GT_NORMAL), NULL, TS_NOFLAGS) ;
  958. DEBUG((PAGRP, PALVL, "PCOND: Exited, type = 0x%02x", n->type)) ;
  959. return(n) ;
  960. }
  961. /*** ParseArgEqArg - parse IF statement string comparisons
  962. *
  963. * Purpose:
  964. * Parse an if statement string comparison conditional.
  965. * The comparison can be in the following formats:
  966. * s1==s2 s1== s2
  967. * s1 ==s2 s1 == s2
  968. *
  969. * ParseArgEqArg(struct cmdnode *n)
  970. *
  971. * Args:
  972. * n - ptr to the conditional structure
  973. *
  974. * *** W A R N I N G ! ***
  975. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  976. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  977. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  978. *
  979. */
  980. void ParseArgEqArg(n)
  981. struct cmdnode *n ;
  982. {
  983. //
  984. // Get LHS of test
  985. //
  986. n->cmdline = GeTexTok( GT_NORMAL );
  987. //
  988. // Get Operator
  989. //
  990. if (GeToken( GT_EQOK ) != TEXTOKEN) {
  991. PSError( );
  992. }
  993. //
  994. // If it is a double equal, the arg ptr (RHS) is the next token
  995. //
  996. if (!_tcscmp( TokBuf, EQSTR)) {
  997. n->argptr = GeTexTok( GT_NORMAL );
  998. DEBUG((PAGRP, PALVL, "PARG: s1 == s2"));
  999. }
  1000. //
  1001. // if it begins with a double equal, skip it and make the remainder
  1002. // of the string the RHS of the test
  1003. //
  1004. else if (TokLen >= 4 && TokBuf[0] == EQ && TokBuf[1] == EQ) {
  1005. n->argptr = gmkstr( (TokLen - 2) * sizeof( TCHAR ));
  1006. mystrcpy( n->argptr, TokBuf + 2 );
  1007. DEBUG((PAGRP, PALVL, "PARG: s1 ==s2"));
  1008. }
  1009. //
  1010. // We have something other than the ==. If extensions are enabled
  1011. // then we test for the extended comparisons
  1012. //
  1013. else if (fEnableExtensions) {
  1014. if (!_tcsicmp( TokBuf, TEXT( "EQU" )))
  1015. n->cmdarg = CMDNODE_ARG_IF_EQU;
  1016. else
  1017. if (!_tcsicmp( TokBuf, TEXT( "NEQ" )))
  1018. n->cmdarg = CMDNODE_ARG_IF_NEQ;
  1019. else
  1020. if (!_tcsicmp( TokBuf, TEXT( "LSS" )))
  1021. n->cmdarg = CMDNODE_ARG_IF_LSS;
  1022. else
  1023. if (!_tcsicmp( TokBuf, TEXT( "LEQ" )))
  1024. n->cmdarg = CMDNODE_ARG_IF_LEQ;
  1025. else
  1026. if (!_tcsicmp( TokBuf, TEXT( "GTR" )))
  1027. n->cmdarg = CMDNODE_ARG_IF_GTR;
  1028. else
  1029. if (!_tcsicmp( TokBuf, TEXT( "GEQ" )))
  1030. n->cmdarg = CMDNODE_ARG_IF_GEQ;
  1031. else
  1032. PSError( );
  1033. n->type = CMPTYP;
  1034. n->argptr = GeTexTok( GT_NORMAL );
  1035. } else {
  1036. PSError( );
  1037. }
  1038. DEBUG((PAGRP, PALVL, "PARG: s1 = `%ws' s2 = `%ws'", n->cmdline, n->argptr));
  1039. }
  1040. /*** ParseCmd - parse production cmd
  1041. *
  1042. * Purpose:
  1043. * Parse a command
  1044. *
  1045. * struct node *ParseCmd()
  1046. *
  1047. * Returns:
  1048. * A pointer to the production just parsed or PARSERROR.
  1049. *
  1050. * Notes:
  1051. * The code below has been completely rewritten to allow the parsing
  1052. * of redirection strings to occur anywhere within the command line.
  1053. * ParseS4() will catch redirection prior to the command name.
  1054. * Redirection between the name and argument or within the argument
  1055. * will be caught by the code below which checks for those operators
  1056. * during the building of the arguments.
  1057. * ParseS4() will then catch any occurring after the command argument.
  1058. *
  1059. * *** W A R N I N G ! ***
  1060. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  1061. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  1062. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  1063. *
  1064. * M013 - The redirection portion of this routine has been rewritten
  1065. * to conform to the new methods of parsing redirection.
  1066. *
  1067. */
  1068. struct node *ParseCmd()
  1069. {
  1070. struct cmdnode *n ; /* Ptr to node to build/fill */
  1071. TCHAR *tptr ; /* M003 - Pointer temp */
  1072. struct relem *io = NULL ; /* M013 - Redir list pointer */
  1073. struct relem **tmpio = &io ; /* M013 - ptr to ptr to redir list */
  1074. DEBUG((PAGRP, PALVL, "PCMD: Entered.")) ;
  1075. n = LoadNodeTC(CMDTYP) ;
  1076. /* M003 - The following section has been completely rewritten
  1077. */
  1078. while (IsData()) { /* M011 - Was Peek() */
  1079. if (GeToken(GT_ARGSTR) == TEXTOKEN) {
  1080. /*WARNING*/ tptr = gmkstr((mystrlen(n->argptr)+TokLen)*sizeof(TCHAR)) ;
  1081. mystrcpy(tptr, n->argptr) ;
  1082. mystrcat(tptr, TokBuf) ;
  1083. n->argptr = tptr ;
  1084. DEBUG((PAGRP, PALVL, "PCMD: args = `%ws'", n->argptr)) ;
  1085. /* M013 - If not text, the current token must be tested as possible
  1086. * redirection. Note that tmpio is a pointer to a pointer to a
  1087. * structure. It first points to the head-of-list pointer, but
  1088. * after each successful call to ParseRedir, it is advanced to
  1089. * point to the 'nxt' pointer field in the last list element.
  1090. */
  1091. } else if (ParseRedir(tmpio)) {
  1092. DEBUG((PAGRP,PALVL,"PCMD: Found redir")) ;
  1093. do {
  1094. tmpio = &(*tmpio)->nxt ;
  1095. } while (*tmpio) ;
  1096. /* M013 ends */
  1097. /* If this is neither a text token (part of the argument) or a redirection
  1098. * sequence, then this must be an operator and must be 'ungot' for the next
  1099. * parsing sequence.
  1100. */
  1101. } else {
  1102. DEBUG((PAGRP,PALVL,"PCMD: Found `%ws'", TokBuf)) ;
  1103. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken() */
  1104. break ;
  1105. }
  1106. } ;
  1107. /* M013 - Once the command is fully parsed and all mixed redirection
  1108. * identified, the list is placed in the node pointer to pass
  1109. * it back.
  1110. */
  1111. DEBUG((PAGRP,PALVL,"PCMD: Redirlist = %04x", io)) ;
  1112. n->rio = io ;
  1113. /* M003/M013 ends */
  1114. DEBUG((PAGRP, PALVL, "PCMD: Exited.")) ;
  1115. return((struct node *) n) ;
  1116. }
  1117. /*** ParseRedir - controls I/O redirection parsing
  1118. *
  1119. * Purpose:
  1120. * Parse the redir production.
  1121. *
  1122. * int ParseRedir(struct relem **io)
  1123. *
  1124. * Args:
  1125. * io - This is a pointer to the head-of-list pointer to the list
  1126. * of redirection elements being built. On entry this MUST be NULL.
  1127. *
  1128. * Returns:
  1129. * TRUE if redirection was found.
  1130. * FALSE if redirection wasn't found.
  1131. *
  1132. * Notes:
  1133. * M013 - This routine has been completely rewritten to conform to
  1134. * new structures and methods of redirection parsing. With these
  1135. * changes, redirection may now begin with ">", "<", "n>" or "n<"
  1136. * where n is a handle number. This function now loops through
  1137. * lex'd input, parsing as many redirection instructions as exist
  1138. * in sequence, building a new structure for each one and linking
  1139. * it into a list of such structures. Like XENIX we require the
  1140. * sequences 'n>' and 'n>&' to exist without separating whitespace
  1141. * although that sequence and any filename or trailing digit may be
  1142. * separated by the normal set of delimiters. The XENIX << operator
  1143. * is lex'd correctly but currently restricted from use.
  1144. * M014 - Input redirection may now exist for handles other than 0.
  1145. */
  1146. int ParseRedir(io)
  1147. struct relem **io ; /* M013 - Ptr to ptr to redir elem */
  1148. {
  1149. TCHAR rdop ; /* M013 - Type of operation */
  1150. int didflg = 0, /* M013 - Loop count */
  1151. getcnt = 0 ; /* M013 - GeToken count */
  1152. TCHAR *i ; /* M013 - General ptr temp */
  1153. DEBUG((PAGRP, PALVL, "PREDIR: Entered, token = `%ws'", TokBuf)) ;
  1154. /* M013 - If the 'while' conditional succeeds, rdop will equal the type of
  1155. * redirection being parsed. A structure is malloc'd for *io and filled
  1156. * with the redirection information. If more redirection is found, io
  1157. * becomes the address of the 'nxt' field in *io, and the cycle is repeated
  1158. * until the 1st non-redirection token is found.
  1159. * Error checking for "<<" and for handle substitution operators '&' without
  1160. * matching digits is done here, returning syntax errors when found.
  1161. */
  1162. while ((rdop = *TokBuf) == INOP || rdop == OUTOP ||
  1163. (_istdigit(*TokBuf) &&
  1164. ((rdop = *(TokBuf+1)) == INOP || rdop == OUTOP))) {
  1165. if (!(*io = (struct relem *)mkstr(sizeof(struct relem)))) {
  1166. PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS); /* M021 */
  1167. PError() ;
  1168. } ;
  1169. DEBUG((PAGRP,PALVL,"PREDIR: List element made.")) ;
  1170. ++didflg ; /* Set == found at least one */
  1171. i = TokBuf ;
  1172. (*io)->rdop = rdop ; /* M014 - New field for type */
  1173. if (_istdigit(*i)) {
  1174. (*io)->rdhndl = *i - TEXT('0');
  1175. ++i ;
  1176. DEBUG((PAGRP,PALVL,"PREDIR: Specific-handle Redir.")) ;
  1177. } else {
  1178. if (rdop == OUTOP)
  1179. (*io)->rdhndl = STDOUT;
  1180. else
  1181. (*io)->rdhndl = STDIN ;
  1182. } ;
  1183. DEBUG((PAGRP,PALVL,"PREDIR:Redir handle %d...",(*io)->rdhndl)) ;
  1184. DEBUG((PAGRP,PALVL,(rdop == INOP)? "PREDIR:...for input" :
  1185. "PREDIR:...for output")) ;
  1186. if (*i == *(i+1)) {
  1187. if (rdop == INOP) /* M013 - Disallow '<<' */
  1188. PSError() ;
  1189. (*io)->flag = 1 ;
  1190. ++i ;
  1191. } ;
  1192. ++i ;
  1193. if (*i == CSOP) {
  1194. if (mystrlen(i) == 2 && _istdigit(*(i+1)) &&
  1195. ((*io)->fname = mkstr(3*sizeof(TCHAR))))
  1196. mystrcpy((*io)->fname, i) ;
  1197. else
  1198. PSError() ;
  1199. } else
  1200. (*io)->fname = GeTexTok(GT_NORMAL) ;
  1201. DEBUG((PAGRP,PALVL,"PREDIR: RD fname = `%ws'.",(*io)->fname)) ;
  1202. if (IsData()) {
  1203. GeToken(GT_NORMAL) ;
  1204. io = &((*io)->nxt) ;
  1205. ++getcnt ;
  1206. } else
  1207. break ;
  1208. } ;
  1209. DEBUG((PAGRP, PALVL, "PREDIR: Exited current token = `%ws'", TokBuf)) ;
  1210. /* M013 - Note that this function is called with a valid token in the
  1211. * token buffer. If the routine returns FALSE, that token must
  1212. * still be valid. If it returns TRUE, there must not be a valid
  1213. * token in the buffer. This is complicated by the fact that at
  1214. * return time, there will either be a valid non-redirection token
  1215. * in the buffer, or there will be no valid token because none were
  1216. * available. To determine whether to do an unget, we keep counts
  1217. * of tokens read (minus the one passed at call time) and tokens
  1218. * used. If they are equal, we unget the last one; if not, then
  1219. * no data was available.
  1220. */
  1221. if (didflg) {
  1222. if (getcnt == didflg)
  1223. Lex(LX_UNGET,0) ;
  1224. return(TRUE) ;
  1225. } else
  1226. return(FALSE) ;
  1227. }
  1228. /*** BinaryOperator - parse binary operators
  1229. *
  1230. * Purpose:
  1231. * Parse a production which contains a binary operator. Parse the left
  1232. * side of the operator. If the next token is the operator we are looking
  1233. * for, build a node for it, call the operator's production parsing
  1234. * routine to parse the right side of the operator, put all the pieces
  1235. * together.
  1236. *
  1237. * struct node *BinaryOperator(TCHAR *opstr, int optype,
  1238. * struct node *opprodfunc(), struct node *leftprodfunc())
  1239. *
  1240. * Args:
  1241. * opstr - string representation of the operator to look for
  1242. * optype - type of operator
  1243. * opprodfunc - the function which parses this operator
  1244. * leftprodfunc - the function which parses the left side of the operator
  1245. *
  1246. * Returns:
  1247. * If the node is found, a pointer to the node for the operator.
  1248. * Otherwise, a pointer to the "left side" of the oprator.
  1249. *
  1250. */
  1251. struct node *BinaryOperator(opstr, optype, opprodfunc, leftprodfunc)
  1252. TCHAR *opstr ;
  1253. int optype ;
  1254. PPARSE_ROUTINE opprodfunc ;
  1255. PPARSE_ROUTINE leftprodfunc ;
  1256. {
  1257. struct node *n ; /* Ptr to binop node to build and fill */
  1258. struct node *leftside ; /* Ptr to the leftside of the binop */
  1259. DEBUG((PAGRP, PALVL, "BINOP: op = %ws", opstr)) ;
  1260. leftside = (*leftprodfunc)() ;
  1261. /* M011 - Ref to IsData() was ref to Peek()
  1262. */
  1263. if (IsData()) /* If data left, read token - else, return */
  1264. GeToken(GT_NORMAL) ;
  1265. else {
  1266. DEBUG((PAGRP, PALVL, "BINOP: No more data, return lh side.")) ;
  1267. return(leftside) ;
  1268. } ;
  1269. /* This conditional tests for two cases; a true occurance of the binary op
  1270. * being sought, or the occurance of a newline acting as a command seperator
  1271. * within a parenthetical statement.
  1272. */
  1273. if (_tcscmp(opstr, TokBuf) == 0 ||
  1274. (StatementType[StatementDepth-1] == PARTYP &&
  1275. _tcscmp(opstr, CSSTR) == 0 &&
  1276. *TokBuf == NLN
  1277. )
  1278. ) {
  1279. /* M004 - This functionality was moved here from ParseS0. It handles the
  1280. * stripping of newlines that occur following the first production
  1281. * in a parenthetical operation. And will eliminate the newline's
  1282. * acting as a command seperator when the right side production is
  1283. * only the terminating right paren.
  1284. * M010 - Added fix for problem of token after newline being eaten if
  1285. * inside a parenthetical statment.
  1286. */
  1287. if (*TokBuf == NLN) { /* Only TRUE if inside () */
  1288. do {
  1289. GeToken(GT_NORMAL) ;
  1290. } while (*TokBuf == NLN) ;
  1291. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken() */
  1292. if (*TokBuf == RPOP) {
  1293. DEBUG((PAGRP, PALVL, "BINOP: Ungetting right paren.")) ;
  1294. return(leftside) ; /* Return left only */
  1295. }
  1296. optype = LFTYP;
  1297. } ;
  1298. /* M004/M010 ends */
  1299. DEBUG((PAGRP, PALVL, "BINOP: Found %ws", opstr)) ;
  1300. n = mknode() ;
  1301. if (n == NULL) {
  1302. Abort();
  1303. }
  1304. n->type = optype ;
  1305. n->lhs = leftside ;
  1306. AtIsToken = 1; /* @@4 */
  1307. GeToken(GT_LPOP) ; /* M000 - Was GT_NORMAL */
  1308. AtIsToken = 0; /* @@4 */
  1309. n->rhs = (*opprodfunc)() ;
  1310. DEBUG((PAGRP, PALVL, "BINOP: Exiting op = %ws", opstr)) ;
  1311. return(n) ;
  1312. } else {
  1313. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken() */
  1314. DEBUG((PAGRP, PALVL, "BINOP: Did NOT find %ws", opstr)) ;
  1315. } ;
  1316. DEBUG((PAGRP, PALVL, "BINOP: Exiting op = %ws", opstr)) ;
  1317. return(leftside) ;
  1318. }
  1319. /*** BuildArgList - parse FOR statement argument list
  1320. *
  1321. * Purpose:
  1322. * Build a FOR statement's argument list. Collapse it into the following
  1323. * form "a0 a1 a2 a3...".
  1324. *
  1325. * TCHAR *BuildArgList()
  1326. *
  1327. * Returns:
  1328. * A pointer to the argument list.
  1329. */
  1330. TCHAR *BuildArgList()
  1331. {
  1332. TCHAR *args = NULL ; /* Ptr to squeezed FOR arg list */
  1333. int arglen = 0 ; /* Length of current arg */
  1334. int done = 0 ; /* Flag, nonzero if done */
  1335. DEBUG((PAGRP, PALVL, "BARGL: Entered.")) ;
  1336. if(GeToken(GT_LPOP) != LPOP)
  1337. PSError() ;
  1338. for ( ; !done ; ) {
  1339. switch (GeToken(GT_RPOP)) { /* M000 - Was GT_NORMAL */
  1340. case TEXTOKEN: /* Another arg was found, add it to the list */
  1341. arglen += TokLen ;
  1342. if (args) {
  1343. args = resize(args, arglen*sizeof(TCHAR)) ;
  1344. SpaceCat(args, args, TokBuf) ;
  1345. } else {
  1346. args = gmkstr(arglen*sizeof(TCHAR)) ; /*WARNING*/
  1347. mystrcpy(args, TokBuf) ;
  1348. } ;
  1349. DEBUG((PAGRP, PALVL, "BARGL: Current args = %ws", args)) ;
  1350. break ;
  1351. case NLN: /* Skip newlines */
  1352. continue ;
  1353. default: /* If anything else, we're done */
  1354. done = TRUE ;
  1355. break ;
  1356. } ;
  1357. } ;
  1358. /* When the loop exits, the current token should be a right paren */
  1359. if (TokTyp == RPOP) {
  1360. DEBUG((PAGRP, PALVL, "BARGL: Exiting, args = %ws", args)) ;
  1361. return(args) ;
  1362. } ;
  1363. PSError() ;
  1364. return NULL;
  1365. }
  1366. /*** GetCheckStr - get and check a token
  1367. *
  1368. * Purpose:
  1369. * Get a token and compare it against the string passed. If they don't
  1370. * match, call PSError().
  1371. *
  1372. * GetCheckStr(TCHAR *str)
  1373. *
  1374. * Args:
  1375. * str - the string to compare with the token
  1376. *
  1377. */
  1378. void GetCheckStr(str)
  1379. TCHAR *str ;
  1380. {
  1381. GeToken(GT_NORMAL) ;
  1382. if (_tcsicmp(str, TokBuf) != 0)
  1383. PSError() ;
  1384. DEBUG((PAGRP, PALVL, "GETCS: Exiting.")) ;
  1385. }
  1386. /*** GeTexTok - get a text token
  1387. *
  1388. * Purpose:
  1389. * Get the next text token, allocate a string for it and copy it into the
  1390. * string.
  1391. *
  1392. * TCHAR *GeTexTok(unsigned gtflag)
  1393. *
  1394. * Args:
  1395. * gtflag - flag to be passed to GeToken()
  1396. *
  1397. * Returns:
  1398. * A pointer to the string.
  1399. *
  1400. * *** W A R N I N G ! ***
  1401. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  1402. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  1403. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  1404. *
  1405. */
  1406. TCHAR *GeTexTok(gtflag)
  1407. unsigned gtflag ;
  1408. {
  1409. TCHAR *s ; /* Ptr to the text token */
  1410. if (GeToken(gtflag) != TEXTOKEN)
  1411. PSError() ;
  1412. s = gmkstr(TokLen*sizeof(TCHAR)) ; /*WARNING*/
  1413. mystrcpy(s, TokBuf) ;
  1414. DEBUG((PAGRP, PALVL, "GETT: Exiting.")) ;
  1415. return(s) ;
  1416. }
  1417. /*** GeToken - get a token
  1418. *
  1419. * Purpose:
  1420. * If there is a previous token, make it the current token. Otherwise,
  1421. * call the lexer to get another token and make it the current token.
  1422. *
  1423. * unsigned GeToken(unsigned flag)
  1424. *
  1425. * Args:
  1426. * flag - passed to lexer to tell it to get an argstring or regular token
  1427. *
  1428. * Returns:
  1429. * The type of the current token.
  1430. *
  1431. */
  1432. unsigned GeToken(flag)
  1433. unsigned flag ;
  1434. {
  1435. unsigned Lex() ;
  1436. if(PendingParens != 0)
  1437. flag = flag | GT_RPOP ;
  1438. if ((TokTyp = Lex((TCHAR *)TokBuf, (unsigned)flag)) == (unsigned )LEXERROR)
  1439. PError() ;
  1440. TokLen = mystrlen(TokBuf)+1 ;
  1441. DEBUG((PAGRP, PALVL, " GET: type = 0x%04x token = `%ws' toklen = %d", TokTyp, TokBuf, TokLen)) ;
  1442. if (fDumpTokens)
  1443. cmd_printf( TEXT("GeToken: (%x) '%s'\n"), TokTyp, TokBuf);
  1444. return(TokTyp) ;
  1445. }
  1446. /*** LoadNodeTC - make and load a command node
  1447. *
  1448. * Purpose:
  1449. * Make a command node and load its type field with the argument and
  1450. * its cmdline field with the current token.
  1451. *
  1452. * struct cmdnode *LoadNodeTC(int type)
  1453. *
  1454. * Args:
  1455. * type - the type of command
  1456. *
  1457. * Returns:
  1458. * A pointer to the node that was made.
  1459. *
  1460. * *** W A R N I N G ! ***
  1461. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  1462. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  1463. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  1464. *
  1465. */
  1466. struct cmdnode *LoadNodeTC(type)
  1467. int type ;
  1468. {
  1469. struct cmdnode *n ; /* Ptr to the cmdnode to build and fill */
  1470. n = (struct cmdnode *) mknode() ;
  1471. if (n == NULL) {
  1472. Abort();
  1473. }
  1474. n->type = type ;
  1475. n->flag = 0 ;
  1476. n->cmdline = gmkstr(TokLen*sizeof(TCHAR)) ; /*WARNING*/
  1477. mystrcpy(n->cmdline, TokBuf) ;
  1478. DEBUG((PAGRP, PALVL, "LOAD: type = %04x", type)) ;
  1479. return(n) ;
  1480. }
  1481. /*** PError - handle parser error
  1482. *
  1483. * Purpose:
  1484. * Parser via longjmp().
  1485. *
  1486. * PError()
  1487. *
  1488. * Returns:
  1489. * PARSERROR
  1490. *
  1491. */
  1492. void PError()
  1493. {
  1494. global_dfvalue = MSG_SYNERR_GENL; /* @@J1 PSError, not jump in parser*/
  1495. longjmp(CmdJBuf1, PARSERROR) ;
  1496. }
  1497. /*** PSError - print error message and handle parser error
  1498. *
  1499. * Purpose:
  1500. * Print the parser syntax error message and return to Parser via
  1501. * longjmp().
  1502. *
  1503. * PSError()
  1504. *
  1505. * Returns:
  1506. * PARSERROR
  1507. *
  1508. * Notes:
  1509. * M021 - Unfragmented Syntax error messages and revised function.
  1510. *
  1511. */
  1512. void PSError( )
  1513. {
  1514. /*@@4*/ unsigned do_jmp;
  1515. /*@@4*/ do_jmp = global_dfvalue != MSG_SYNERR_GENL;
  1516. /*@@4*/ if ( global_dfvalue == READFILE )
  1517. {
  1518. /*@@4*/ global_dfvalue = MSG_SYNERR_GENL;
  1519. }
  1520. else
  1521. {
  1522. if (*TokBuf == NLN)
  1523. {
  1524. PutStdErr(MSG_BAD_SYNTAX, NOARGS) ;
  1525. }
  1526. else
  1527. {
  1528. /*@@4*/ if (*TokBuf != NULLC) /* @@J1 if no data wrong then */
  1529. /*@@4*/ { /* @@J1 do not give message */
  1530. PutStdErr(MSG_SYNERR_GENL, ONEARG, TokBuf );
  1531. /*@@4*/ } /* @@J1 */
  1532. }
  1533. }
  1534. /*@@4*/ if ( do_jmp ) {
  1535. longjmp(CmdJBuf1, PARSERROR) ;
  1536. /*@@4*/ }
  1537. }
  1538. /*** SpaceCat - concatenate 2 strings and delimit the strings with a space
  1539. *
  1540. * Purpose:
  1541. * Copy src1 to dst. Then concatenate a space and src2 to the end of
  1542. * dst.
  1543. *
  1544. * SpaceCat(TCHAR *dst, TCHAR *src1, TCHAR *src2)
  1545. *
  1546. * Args:
  1547. * See above
  1548. *
  1549. */
  1550. void SpaceCat(dst, src1, src2)
  1551. TCHAR *dst,
  1552. *src1,
  1553. *src2 ;
  1554. {
  1555. mystrcpy(dst, src1) ;
  1556. mystrcat(dst, TEXT(" ")) ;
  1557. mystrcat(dst, src2) ;
  1558. }