Leaked source code of windows server 2003
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.

1920 lines
54 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[] ;
  78. int NulNode = FALSE ; /* M018 */
  79. unsigned global_dfvalue;
  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;
  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;
  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;
  287. GeToken(GT_LPOP) ; /* M000 - Was GT_NORMAL */
  288. AtIsToken = 0;
  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 );
  300. Abort();
  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) ;
  444. SpaceCat(n->cmdline, n->cmdline, TokBuf) ;
  445. n->arglist = BuildArgList() ;
  446. GetCheckStr(DoStr) ;
  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. return NULL;
  867. }
  868. if (*TokBuf == LPOP) { /* M015 */
  869. n->type = PARTYP ;
  870. /* M004 - Strip leading newlines from the current paren group
  871. */
  872. do {
  873. GeToken(GT_NORMAL|GT_RPOP) ;
  874. } while (*TokBuf == NLN) ;
  875. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken */
  876. /* M004 ends */
  877. n->lhs = ParseStatement(PARTYP) ;
  878. } else { /* M015 */
  879. n->type = SILTYP ; /* M015 */
  880. DEBUG((PAGRP,PALVL,"PS5: Silent mode starts")) ;
  881. n->lhs = ParseStatement(0) ;
  882. } ;
  883. DEBUG((PAGRP, PALVL, "PS5: ParseStatement has returned.")) ;
  884. if (n->type == SILTYP) /* M015 */
  885. return(n) ; /* M015 */
  886. /* M015 ends */
  887. if (GeToken(GT_RPOP) == RPOP) { /* M000 - Was GT_NORMAL */
  888. return(n) ;
  889. };
  890. };
  891. DEBUG((PAGRP, PALVL, "PS5: Error, no right paren. Token = `%ws'", TokBuf)) ;
  892. PSError() ;
  893. return NULL;
  894. }
  895. /*** ParseCond - parse the condition production
  896. *
  897. * Purpose:
  898. * Parse a CONDITION production.
  899. *
  900. * struct cmdnode *ParseCond(unsigned pcflag)
  901. *
  902. * Args:
  903. * pcflag - nonzero if "NOT"s are not allowed because one has been found
  904. * already
  905. *
  906. * Returns:
  907. * A pointer to a parsed COND production or PARSERROR.
  908. *
  909. * Notes:
  910. * The token after "errorlevel" is checked to make sure it is a number.
  911. * If it isn't, a syntax error is generated.
  912. *
  913. * If a valid "NOT" is found, ParseCond() is called recursively to parse
  914. * the rest of the condition. A pointer to the node is put in the
  915. * argptr field of the node.
  916. *
  917. * M020 - "ERRORLEVEL=x" not being parsed correctly due to entire
  918. * string being lexed as single token. Now Lex first as GT_NORMAL
  919. * and if not ERRORLEVEL, re-Lex as GT_EQOK.
  920. *
  921. */
  922. struct cmdnode *ParseCond(pcflag)
  923. unsigned pcflag ;
  924. {
  925. struct cmdnode *n ; /* Ptr to cond node to build and fill */
  926. struct cmdnode *LoadNodeTC() ;
  927. DEBUG((PAGRP, PALVL, "PCOND: Entered.")) ;
  928. if (GeToken(GT_NORMAL) != TEXTOKEN) /* M020 */
  929. PSError() ;
  930. n = LoadNodeTC(0) ;
  931. if (_tcsicmp(ErrStr, TokBuf) == 0) { /* ERRORLEVEL */
  932. n->type = ERRTYP ;
  933. } else
  934. if (_tcsicmp(ExsStr, TokBuf) == 0) /* EXIST */
  935. n->type = EXSTYP ;
  936. else
  937. if (fEnableExtensions && _tcsicmp(CmdExtVerStr, TokBuf) == 0) /* CMDEXTVERSION */
  938. n->type = CMDVERTYP ;
  939. else
  940. if (fEnableExtensions && _tcsicmp(DefinedStr, TokBuf) == 0) /* DEFINED */
  941. n->type = DEFTYP ;
  942. else
  943. if (_tcsicmp(NotStr, TokBuf) == 0) { /* Not */
  944. if (pcflag)
  945. PSError() ;
  946. n->type = NOTTYP ;
  947. n->argptr = (TCHAR *) ParseCond(PC_NONOTS) ;
  948. DEBUG((PAGRP, PALVL, "PCOND: Exited, type = 0x%02x", n->type)) ;
  949. return(n);
  950. } else {
  951. Lex(LX_UNGET,0);
  952. n->type = STRTYP ; /* String comparison */
  953. ParseArgEqArg(n) ;
  954. DEBUG((PAGRP, PALVL, "PCOND: Exited, type = 0x%02x", n->type)) ;
  955. return(n);
  956. } ;
  957. /* Errorlevel, Exist, CmdExtVersion or Defined */
  958. n->argptr = TokStr(GeTexTok(GT_NORMAL), NULL, TS_NOFLAGS) ;
  959. DEBUG((PAGRP, PALVL, "PCOND: Exited, type = 0x%02x", n->type)) ;
  960. return(n) ;
  961. }
  962. /*** ParseArgEqArg - parse IF statement string comparisons
  963. *
  964. * Purpose:
  965. * Parse an if statement string comparison conditional.
  966. * The comparison can be in the following formats:
  967. * s1==s2 s1== s2
  968. * s1 ==s2 s1 == s2
  969. *
  970. * ParseArgEqArg(struct cmdnode *n)
  971. *
  972. * Args:
  973. * n - ptr to the conditional structure
  974. *
  975. * *** W A R N I N G ! ***
  976. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  977. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  978. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  979. *
  980. */
  981. void ParseArgEqArg(n)
  982. struct cmdnode *n ;
  983. {
  984. //
  985. // Get LHS of test
  986. //
  987. n->cmdline = GeTexTok( GT_NORMAL );
  988. //
  989. // Get Operator
  990. //
  991. if (GeToken( GT_EQOK ) != TEXTOKEN) {
  992. PSError( );
  993. }
  994. //
  995. // If it is a double equal, the arg ptr (RHS) is the next token
  996. //
  997. if (!_tcscmp( TokBuf, EQSTR)) {
  998. n->argptr = GeTexTok( GT_NORMAL );
  999. DEBUG((PAGRP, PALVL, "PARG: s1 == s2"));
  1000. }
  1001. //
  1002. // if it begins with a double equal, skip it and make the remainder
  1003. // of the string the RHS of the test
  1004. //
  1005. else if (TokLen >= 4 && TokBuf[0] == EQ && TokBuf[1] == EQ) {
  1006. n->argptr = gmkstr( (TokLen - 2) * sizeof( TCHAR ));
  1007. mystrcpy( n->argptr, TokBuf + 2 );
  1008. DEBUG((PAGRP, PALVL, "PARG: s1 ==s2"));
  1009. }
  1010. //
  1011. // We have something other than the ==. If extensions are enabled
  1012. // then we test for the extended comparisons
  1013. //
  1014. else if (fEnableExtensions) {
  1015. if (!_tcsicmp( TokBuf, TEXT( "EQU" )))
  1016. n->cmdarg = CMDNODE_ARG_IF_EQU;
  1017. else
  1018. if (!_tcsicmp( TokBuf, TEXT( "NEQ" )))
  1019. n->cmdarg = CMDNODE_ARG_IF_NEQ;
  1020. else
  1021. if (!_tcsicmp( TokBuf, TEXT( "LSS" )))
  1022. n->cmdarg = CMDNODE_ARG_IF_LSS;
  1023. else
  1024. if (!_tcsicmp( TokBuf, TEXT( "LEQ" )))
  1025. n->cmdarg = CMDNODE_ARG_IF_LEQ;
  1026. else
  1027. if (!_tcsicmp( TokBuf, TEXT( "GTR" )))
  1028. n->cmdarg = CMDNODE_ARG_IF_GTR;
  1029. else
  1030. if (!_tcsicmp( TokBuf, TEXT( "GEQ" )))
  1031. n->cmdarg = CMDNODE_ARG_IF_GEQ;
  1032. else
  1033. PSError( );
  1034. n->type = CMPTYP;
  1035. n->argptr = GeTexTok( GT_NORMAL );
  1036. } else {
  1037. PSError( );
  1038. }
  1039. DEBUG((PAGRP, PALVL, "PARG: s1 = `%ws' s2 = `%ws'", n->cmdline, n->argptr));
  1040. }
  1041. /*** ParseCmd - parse production cmd
  1042. *
  1043. * Purpose:
  1044. * Parse a command
  1045. *
  1046. * struct node *ParseCmd()
  1047. *
  1048. * Returns:
  1049. * A pointer to the production just parsed or PARSERROR.
  1050. *
  1051. * Notes:
  1052. * The code below has been completely rewritten to allow the parsing
  1053. * of redirection strings to occur anywhere within the command line.
  1054. * ParseS4() will catch redirection prior to the command name.
  1055. * Redirection between the name and argument or within the argument
  1056. * will be caught by the code below which checks for those operators
  1057. * during the building of the arguments.
  1058. * ParseS4() will then catch any occurring after the command argument.
  1059. *
  1060. * *** W A R N I N G ! ***
  1061. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  1062. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  1063. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  1064. *
  1065. * M013 - The redirection portion of this routine has been rewritten
  1066. * to conform to the new methods of parsing redirection.
  1067. *
  1068. */
  1069. struct node *ParseCmd()
  1070. {
  1071. struct cmdnode *n ; /* Ptr to node to build/fill */
  1072. TCHAR *tptr ; /* M003 - Pointer temp */
  1073. struct relem *io = NULL ; /* M013 - Redir list pointer */
  1074. struct relem **tmpio = &io ; /* M013 - ptr to ptr to redir list */
  1075. DEBUG((PAGRP, PALVL, "PCMD: Entered.")) ;
  1076. n = LoadNodeTC(CMDTYP) ;
  1077. /* M003 - The following section has been completely rewritten
  1078. */
  1079. while (IsData()) { /* M011 - Was Peek() */
  1080. if (GeToken(GT_ARGSTR) == TEXTOKEN) {
  1081. /*WARNING*/
  1082. tptr = gmkstr((mystrlen(n->argptr)+TokLen)*sizeof(TCHAR)) ;
  1083. mystrcpy(tptr, n->argptr) ;
  1084. mystrcat(tptr, TokBuf) ;
  1085. n->argptr = tptr ;
  1086. DEBUG((PAGRP, PALVL, "PCMD: args = `%ws'", n->argptr)) ;
  1087. /* M013 - If not text, the current token must be tested as possible
  1088. * redirection. Note that tmpio is a pointer to a pointer to a
  1089. * structure. It first points to the head-of-list pointer, but
  1090. * after each successful call to ParseRedir, it is advanced to
  1091. * point to the 'nxt' pointer field in the last list element.
  1092. */
  1093. } else if (ParseRedir(tmpio)) {
  1094. DEBUG((PAGRP,PALVL,"PCMD: Found redir")) ;
  1095. do {
  1096. tmpio = &(*tmpio)->nxt ;
  1097. } while (*tmpio) ;
  1098. /* M013 ends */
  1099. /* If this is neither a text token (part of the argument) or a redirection
  1100. * sequence, then this must be an operator and must be 'ungot' for the next
  1101. * parsing sequence.
  1102. */
  1103. } else {
  1104. DEBUG((PAGRP,PALVL,"PCMD: Found `%ws'", TokBuf)) ;
  1105. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken() */
  1106. break ;
  1107. }
  1108. } ;
  1109. /* M013 - Once the command is fully parsed and all mixed redirection
  1110. * identified, the list is placed in the node pointer to pass
  1111. * it back.
  1112. */
  1113. DEBUG((PAGRP,PALVL,"PCMD: Redirlist = %04x", io)) ;
  1114. n->rio = io ;
  1115. /* M003/M013 ends */
  1116. DEBUG((PAGRP, PALVL, "PCMD: Exited.")) ;
  1117. return((struct node *) n) ;
  1118. }
  1119. /*** ParseRedir - controls I/O redirection parsing
  1120. *
  1121. * Purpose:
  1122. * Parse the redir production.
  1123. *
  1124. * int ParseRedir(struct relem **io)
  1125. *
  1126. * Args:
  1127. * io - This is a pointer to the head-of-list pointer to the list
  1128. * of redirection elements being built. On entry this MUST be NULL.
  1129. *
  1130. * Returns:
  1131. * TRUE if redirection was found.
  1132. * FALSE if redirection wasn't found.
  1133. *
  1134. * Notes:
  1135. * M013 - This routine has been completely rewritten to conform to
  1136. * new structures and methods of redirection parsing. With these
  1137. * changes, redirection may now begin with ">", "<", "n>" or "n<"
  1138. * where n is a handle number. This function now loops through
  1139. * lex'd input, parsing as many redirection instructions as exist
  1140. * in sequence, building a new structure for each one and linking
  1141. * it into a list of such structures. Like XENIX we require the
  1142. * sequences 'n>' and 'n>&' to exist without separating whitespace
  1143. * although that sequence and any filename or trailing digit may be
  1144. * separated by the normal set of delimiters. The XENIX << operator
  1145. * is lex'd correctly but currently restricted from use.
  1146. * M014 - Input redirection may now exist for handles other than 0.
  1147. */
  1148. int ParseRedir(io)
  1149. struct relem **io ; /* M013 - Ptr to ptr to redir elem */
  1150. {
  1151. TCHAR rdop ; /* M013 - Type of operation */
  1152. int didflg = 0, /* M013 - Loop count */
  1153. getcnt = 0 ; /* M013 - GeToken count */
  1154. TCHAR *i ; /* M013 - General ptr temp */
  1155. DEBUG((PAGRP, PALVL, "PREDIR: Entered, token = `%ws'", TokBuf)) ;
  1156. /* M013 - If the 'while' conditional succeeds, rdop will equal the type of
  1157. * redirection being parsed. A structure is malloc'd for *io and filled
  1158. * with the redirection information. If more redirection is found, io
  1159. * becomes the address of the 'nxt' field in *io, and the cycle is repeated
  1160. * until the 1st non-redirection token is found.
  1161. * Error checking for "<<" and for handle substitution operators '&' without
  1162. * matching digits is done here, returning syntax errors when found.
  1163. */
  1164. while ((rdop = *TokBuf) == INOP || rdop == OUTOP ||
  1165. (_istdigit(*TokBuf) &&
  1166. ((rdop = *(TokBuf+1)) == INOP || rdop == OUTOP))) {
  1167. if (!(*io = (struct relem *)mkstr(sizeof(struct relem)))) {
  1168. PutStdErr(ERROR_NOT_ENOUGH_MEMORY, NOARGS); /* M021 */
  1169. PError() ;
  1170. };
  1171. DEBUG((PAGRP,PALVL,"PREDIR: List element made.")) ;
  1172. ++didflg ; /* Set == found at least one */
  1173. i = TokBuf ;
  1174. (*io)->rdop = rdop ; /* M014 - New field for type */
  1175. if (_istdigit(*i)) {
  1176. (*io)->rdhndl = *i - TEXT('0');
  1177. ++i ;
  1178. DEBUG((PAGRP,PALVL,"PREDIR: Specific-handle Redir.")) ;
  1179. } else {
  1180. if (rdop == OUTOP)
  1181. (*io)->rdhndl = STDOUT;
  1182. else
  1183. (*io)->rdhndl = STDIN ;
  1184. } ;
  1185. DEBUG((PAGRP,PALVL,"PREDIR:Redir handle %d...",(*io)->rdhndl)) ;
  1186. DEBUG((PAGRP,PALVL,(rdop == INOP)? "PREDIR:...for input" :
  1187. "PREDIR:...for output")) ;
  1188. if (*i == *(i+1)) {
  1189. if (rdop == INOP) /* M013 - Disallow '<<' */
  1190. PSError() ;
  1191. (*io)->flag = 1 ;
  1192. ++i ;
  1193. };
  1194. ++i ;
  1195. if (*i == CSOP) {
  1196. if (mystrlen(i) == 2 && _istdigit(*(i+1)) &&
  1197. ((*io)->fname = mkstr(3*sizeof(TCHAR))))
  1198. mystrcpy((*io)->fname, i) ;
  1199. else
  1200. PSError() ;
  1201. } else
  1202. (*io)->fname = GeTexTok(GT_NORMAL) ;
  1203. DEBUG((PAGRP,PALVL,"PREDIR: RD fname = `%ws'.",(*io)->fname)) ;
  1204. if (IsData()) {
  1205. GeToken(GT_NORMAL) ;
  1206. io = &((*io)->nxt) ;
  1207. ++getcnt ;
  1208. } else
  1209. break ;
  1210. } ;
  1211. DEBUG((PAGRP, PALVL, "PREDIR: Exited current token = `%ws'", TokBuf)) ;
  1212. /* M013 - Note that this function is called with a valid token in the
  1213. * token buffer. If the routine returns FALSE, that token must
  1214. * still be valid. If it returns TRUE, there must not be a valid
  1215. * token in the buffer. This is complicated by the fact that at
  1216. * return time, there will either be a valid non-redirection token
  1217. * in the buffer, or there will be no valid token because none were
  1218. * available. To determine whether to do an unget, we keep counts
  1219. * of tokens read (minus the one passed at call time) and tokens
  1220. * used. If they are equal, we unget the last one; if not, then
  1221. * no data was available.
  1222. */
  1223. if (didflg) {
  1224. if (getcnt == didflg)
  1225. Lex(LX_UNGET,0) ;
  1226. return(TRUE) ;
  1227. } else
  1228. return(FALSE) ;
  1229. }
  1230. /*** BinaryOperator - parse binary operators
  1231. *
  1232. * Purpose:
  1233. * Parse a production which contains a binary operator. Parse the left
  1234. * side of the operator. If the next token is the operator we are looking
  1235. * for, build a node for it, call the operator's production parsing
  1236. * routine to parse the right side of the operator, put all the pieces
  1237. * together.
  1238. *
  1239. * struct node *BinaryOperator(TCHAR *opstr, int optype,
  1240. * struct node *opprodfunc(), struct node *leftprodfunc())
  1241. *
  1242. * Args:
  1243. * opstr - string representation of the operator to look for
  1244. * optype - type of operator
  1245. * opprodfunc - the function which parses this operator
  1246. * leftprodfunc - the function which parses the left side of the operator
  1247. *
  1248. * Returns:
  1249. * If the node is found, a pointer to the node for the operator.
  1250. * Otherwise, a pointer to the "left side" of the oprator.
  1251. *
  1252. */
  1253. struct node *BinaryOperator(opstr, optype, opprodfunc, leftprodfunc)
  1254. TCHAR *opstr ;
  1255. int optype ;
  1256. PPARSE_ROUTINE opprodfunc ;
  1257. PPARSE_ROUTINE leftprodfunc ;
  1258. {
  1259. struct node *n ; /* Ptr to binop node to build and fill */
  1260. struct node *leftside ; /* Ptr to the leftside of the binop */
  1261. DEBUG((PAGRP, PALVL, "BINOP: op = %ws", opstr)) ;
  1262. leftside = (*leftprodfunc)() ;
  1263. /* M011 - Ref to IsData() was ref to Peek()
  1264. */
  1265. if (IsData()) /* If data left, read token - else, return */
  1266. GeToken(GT_NORMAL) ;
  1267. else {
  1268. DEBUG((PAGRP, PALVL, "BINOP: No more data, return lh side.")) ;
  1269. return(leftside) ;
  1270. } ;
  1271. /* This conditional tests for two cases; a true occurance of the binary op
  1272. * being sought, or the occurance of a newline acting as a command seperator
  1273. * within a parenthetical statement.
  1274. */
  1275. if (_tcscmp(opstr, TokBuf) == 0 ||
  1276. (StatementType[StatementDepth-1] == PARTYP &&
  1277. _tcscmp(opstr, CSSTR) == 0 &&
  1278. *TokBuf == NLN
  1279. )
  1280. ) {
  1281. /* M004 - This functionality was moved here from ParseS0. It handles the
  1282. * stripping of newlines that occur following the first production
  1283. * in a parenthetical operation. And will eliminate the newline's
  1284. * acting as a command seperator when the right side production is
  1285. * only the terminating right paren.
  1286. * M010 - Added fix for problem of token after newline being eaten if
  1287. * inside a parenthetical statment.
  1288. */
  1289. if (*TokBuf == NLN) { /* Only TRUE if inside () */
  1290. do {
  1291. GeToken(GT_NORMAL) ;
  1292. } while (*TokBuf == NLN) ;
  1293. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken() */
  1294. if (*TokBuf == RPOP) {
  1295. DEBUG((PAGRP, PALVL, "BINOP: Ungetting right paren.")) ;
  1296. return(leftside) ; /* Return left only */
  1297. }
  1298. optype = LFTYP;
  1299. };
  1300. /* M004/M010 ends */
  1301. DEBUG((PAGRP, PALVL, "BINOP: Found %ws", opstr)) ;
  1302. n = mknode() ;
  1303. if (n == NULL) {
  1304. Abort();
  1305. }
  1306. n->type = optype ;
  1307. n->lhs = leftside ;
  1308. AtIsToken = 1;
  1309. GeToken(GT_LPOP) ; /* M000 - Was GT_NORMAL */
  1310. AtIsToken = 0;
  1311. n->rhs = (*opprodfunc)() ;
  1312. DEBUG((PAGRP, PALVL, "BINOP: Exiting op = %ws", opstr)) ;
  1313. return(n) ;
  1314. } else {
  1315. Lex(LX_UNGET,0) ; /* M011 - Was UnGeToken() */
  1316. DEBUG((PAGRP, PALVL, "BINOP: Did NOT find %ws", opstr)) ;
  1317. } ;
  1318. DEBUG((PAGRP, PALVL, "BINOP: Exiting op = %ws", opstr)) ;
  1319. return(leftside) ;
  1320. }
  1321. /*** BuildArgList - parse FOR statement argument list
  1322. *
  1323. * Purpose:
  1324. * Build a FOR statement's argument list. Collapse it into the following
  1325. * form "a0 a1 a2 a3...".
  1326. *
  1327. * TCHAR *BuildArgList()
  1328. *
  1329. * Returns:
  1330. * A pointer to the argument list.
  1331. */
  1332. TCHAR *BuildArgList()
  1333. {
  1334. TCHAR *args = NULL ; /* Ptr to squeezed FOR arg list */
  1335. int arglen = 0 ; /* Length of current arg */
  1336. int done = 0 ; /* Flag, nonzero if done */
  1337. DEBUG((PAGRP, PALVL, "BARGL: Entered.")) ;
  1338. if (GeToken(GT_LPOP) != LPOP)
  1339. PSError() ;
  1340. for ( ; !done ; ) {
  1341. switch (GeToken(GT_RPOP)) { /* M000 - Was GT_NORMAL */
  1342. case TEXTOKEN: /* Another arg was found, add it to the list */
  1343. arglen += TokLen ;
  1344. if (args) {
  1345. args = resize(args, arglen*sizeof(TCHAR)) ;
  1346. SpaceCat(args, args, TokBuf) ;
  1347. } else {
  1348. args = gmkstr(arglen*sizeof(TCHAR)) ; /*WARNING*/
  1349. mystrcpy(args, TokBuf) ;
  1350. } ;
  1351. DEBUG((PAGRP, PALVL, "BARGL: Current args = %ws", args)) ;
  1352. break ;
  1353. case NLN: /* Skip newlines */
  1354. continue ;
  1355. default: /* If anything else, we're done */
  1356. done = TRUE ;
  1357. break ;
  1358. } ;
  1359. } ;
  1360. /* When the loop exits, the current token should be a right paren */
  1361. if (TokTyp == RPOP) {
  1362. DEBUG((PAGRP, PALVL, "BARGL: Exiting, args = %ws", args)) ;
  1363. return(args) ;
  1364. };
  1365. PSError() ;
  1366. return NULL;
  1367. }
  1368. /*** GetCheckStr - get and check a token
  1369. *
  1370. * Purpose:
  1371. * Get a token and compare it against the string passed. If they don't
  1372. * match, call PSError().
  1373. *
  1374. * GetCheckStr(TCHAR *str)
  1375. *
  1376. * Args:
  1377. * str - the string to compare with the token
  1378. *
  1379. */
  1380. void GetCheckStr(str)
  1381. TCHAR *str ;
  1382. {
  1383. GeToken(GT_NORMAL) ;
  1384. if (_tcsicmp(str, TokBuf) != 0)
  1385. PSError() ;
  1386. DEBUG((PAGRP, PALVL, "GETCS: Exiting.")) ;
  1387. }
  1388. /*** GeTexTok - get a text token
  1389. *
  1390. * Purpose:
  1391. * Get the next text token, allocate a string for it and copy it into the
  1392. * string.
  1393. *
  1394. * TCHAR *GeTexTok(unsigned gtflag)
  1395. *
  1396. * Args:
  1397. * gtflag - flag to be passed to GeToken()
  1398. *
  1399. * Returns:
  1400. * A pointer to the string.
  1401. *
  1402. * *** W A R N I N G ! ***
  1403. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  1404. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  1405. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  1406. *
  1407. */
  1408. TCHAR *GeTexTok(gtflag)
  1409. unsigned gtflag ;
  1410. {
  1411. TCHAR *s ; /* Ptr to the text token */
  1412. if (GeToken(gtflag) != TEXTOKEN)
  1413. PSError() ;
  1414. s = gmkstr(TokLen*sizeof(TCHAR)) ; /*WARNING*/
  1415. mystrcpy(s, TokBuf) ;
  1416. DEBUG((PAGRP, PALVL, "GETT: Exiting.")) ;
  1417. return(s) ;
  1418. }
  1419. /*** GeToken - get a token
  1420. *
  1421. * Purpose:
  1422. * If there is a previous token, make it the current token. Otherwise,
  1423. * call the lexer to get another token and make it the current token.
  1424. *
  1425. * unsigned GeToken(unsigned flag)
  1426. *
  1427. * Args:
  1428. * flag - passed to lexer to tell it to get an argstring or regular token
  1429. *
  1430. * Returns:
  1431. * The type of the current token.
  1432. *
  1433. */
  1434. unsigned GeToken(flag)
  1435. unsigned flag ;
  1436. {
  1437. unsigned Lex() ;
  1438. if (PendingParens != 0)
  1439. flag = flag | GT_RPOP ;
  1440. if ((TokTyp = Lex((TCHAR *)TokBuf, (unsigned)flag)) == (unsigned )LEXERROR)
  1441. PError() ;
  1442. TokLen = mystrlen(TokBuf)+1 ;
  1443. DEBUG((PAGRP, PALVL, " GET: type = 0x%04x token = `%ws' toklen = %d", TokTyp, TokBuf, TokLen)) ;
  1444. if (fDumpTokens)
  1445. cmd_printf( TEXT("GeToken: (%x) '%s'\n"), TokTyp, TokBuf);
  1446. return(TokTyp) ;
  1447. }
  1448. /*** LoadNodeTC - make and load a command node
  1449. *
  1450. * Purpose:
  1451. * Make a command node and load its type field with the argument and
  1452. * its cmdline field with the current token.
  1453. *
  1454. * struct cmdnode *LoadNodeTC(int type)
  1455. *
  1456. * Args:
  1457. * type - the type of command
  1458. *
  1459. * Returns:
  1460. * A pointer to the node that was made.
  1461. *
  1462. * *** W A R N I N G ! ***
  1463. * THIS ROUTINE WILL CAUSE AN ABORT IF MEMORY CANNOT BE ALLOCATED
  1464. * THIS ROUTINE MUST NOT BE CALLED DURING A SIGNAL
  1465. * CRITICAL SECTION OR DURING RECOVERY FROM AN ABORT
  1466. *
  1467. */
  1468. struct cmdnode *LoadNodeTC(type)
  1469. int type ;
  1470. {
  1471. struct cmdnode *n ; /* Ptr to the cmdnode to build and fill */
  1472. n = (struct cmdnode *) mknode() ;
  1473. if (n == NULL) {
  1474. Abort();
  1475. return NULL;
  1476. }
  1477. n->type = type ;
  1478. n->flag = 0 ;
  1479. n->cmdline = gmkstr(TokLen*sizeof(TCHAR)) ; /*WARNING*/
  1480. mystrcpy(n->cmdline, TokBuf) ;
  1481. DEBUG((PAGRP, PALVL, "LOAD: type = %04x", type)) ;
  1482. return(n) ;
  1483. }
  1484. /*** PError - handle parser error
  1485. *
  1486. * Purpose:
  1487. * Parser via longjmp().
  1488. *
  1489. * PError()
  1490. *
  1491. * Returns:
  1492. * PARSERROR
  1493. *
  1494. */
  1495. void PError()
  1496. {
  1497. global_dfvalue = MSG_SYNERR_GENL; /* @@J1 PSError, not jump in parser*/
  1498. longjmp(CmdJBuf1, PARSERROR) ;
  1499. }
  1500. /*** PSError - print error message and handle parser error
  1501. *
  1502. * Purpose:
  1503. * Print the parser syntax error message and return to Parser via
  1504. * longjmp().
  1505. *
  1506. * PSError()
  1507. *
  1508. * Returns:
  1509. * PARSERROR
  1510. *
  1511. * Notes:
  1512. * M021 - Unfragmented Syntax error messages and revised function.
  1513. *
  1514. */
  1515. void PSError( )
  1516. {
  1517. unsigned do_jmp;
  1518. do_jmp = global_dfvalue != MSG_SYNERR_GENL;
  1519. if ( global_dfvalue == READFILE ) {
  1520. global_dfvalue = MSG_SYNERR_GENL;
  1521. } else {
  1522. if (*TokBuf == NLN) {
  1523. PutStdErr(MSG_BAD_SYNTAX, NOARGS) ;
  1524. } else {
  1525. if (*TokBuf != NULLC) {
  1526. PutStdErr(MSG_SYNERR_GENL, ONEARG, TokBuf );
  1527. }
  1528. }
  1529. }
  1530. if ( do_jmp ) {
  1531. longjmp(CmdJBuf1, PARSERROR) ;
  1532. }
  1533. }
  1534. /*** SpaceCat - concatenate 2 strings and delimit the strings with a space
  1535. *
  1536. * Purpose:
  1537. * Copy src1 to dst. Then concatenate a space and src2 to the end of
  1538. * dst.
  1539. *
  1540. * SpaceCat(TCHAR *dst, TCHAR *src1, TCHAR *src2)
  1541. *
  1542. * Args:
  1543. * See above
  1544. *
  1545. */
  1546. void SpaceCat(dst, src1, src2)
  1547. TCHAR *dst,
  1548. *src1,
  1549. *src2 ;
  1550. {
  1551. mystrcpy(dst, src1) ;
  1552. mystrcat(dst, TEXT(" ")) ;
  1553. mystrcat(dst, src2) ;
  1554. }