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

1798 lines
65 KiB

4 years ago
  1. #include "cmd.h"
  2. extern unsigned int DosErr;
  3. extern jmp_buf CmdJBuf2 ; /* Used for error handling */
  4. extern TCHAR DTNums[] ;
  5. extern TCHAR MsgBuf[];
  6. extern unsigned msglen; /* @@@@@@@@ */
  7. extern TCHAR VerMsg[] ; /* dgs - print out cmd version too */
  8. int Necho = 0; /* No echo option */
  9. extern int KeysFlag; /* @@5 */
  10. extern int EditLine(
  11. CRTHANDLE DataPtr,
  12. TCHAR *Buffer,
  13. int MaxLength,
  14. int *ReturnLength); /* @@5 */
  15. static unsigned DataFlag ; /* Tells FillBuf where to get its input */
  16. static CRTHANDLE DataPtr ; /* File handle/string ptr FillBuf... */
  17. int Ctrlc = 0; /* flag - if set print a ctrl/c before next prompt */
  18. int ExtCtrlc = 0; /* @@4 flag, if set print msg */
  19. int AtIsToken; /* @@4 flag, true if @ is a token */
  20. /***
  21. * The lex buffer is called LexBuf. It holds characters as they are
  22. * retrieved one by one by GetByte. With the advent of double byte
  23. * characters, UnGetByte may be sometimes called upon to put back
  24. * up to two characters. To facilitate this, LexBuf is really an
  25. * alias for &LexBuffer[1]. This gives an extra byte in front of the
  26. * buffer for character push back. Every time fillbuf is called, it
  27. * copies the last character of the previous buffer into the byte
  28. * preceeding the normal buffer. Thus, when UnGetByte does a
  29. * LexBufPtr-- the pointer will correctly point at the preceeding character.
  30. */
  31. TCHAR LexBuffer[LBUFLEN+3]; /* @@4 */
  32. /* ...reads from Lexer input buffer *M011*/
  33. /* LBUFLEN characters + newline + null + */
  34. /* an extra byte for UnGetByte */
  35. #define LexBuf (&LexBuffer[1])
  36. static TCHAR *LexBufPtr ; /* Ptr to next byte in Lex's input buffer */
  37. static TCHAR *PrevLexPtr ; /* M013 - New previous token pointer */
  38. static TCHAR FrsBuf[LBUFLEN+1] ;
  39. extern int LastRetCode ;
  40. TCHAR LastRetCodeStr[32];
  41. extern CHAR AnsiBuf[];
  42. extern TCHAR Fmt27[], Fmt14[] ;
  43. extern struct batdata *CurBat ; /* M026 - Batch file struct ptr */
  44. extern TCHAR CrLf[] ;
  45. extern TCHAR ErrStr[] ;
  46. extern int NulNode ;
  47. extern TCHAR Fmt19[] ;
  48. extern TCHAR DBkSpc[] ;
  49. #if defined(DBCS) // DDBkSpc[]
  50. extern TCHAR DDBkSpc[] ;
  51. #endif // defined(DBCS)
  52. extern unsigned global_dfvalue; /* @@4 */
  53. extern int EchoFlag ;
  54. extern TCHAR PromptStr[], CurDrvDir[], Delimiters[] ;
  55. extern unsigned flgwd ;
  56. extern BOOL CtrlCSeen;
  57. VOID SetCtrlC();
  58. VOID ResetCtrlC();
  59. //
  60. // Prompt string special characters and associated print character/flag.
  61. //
  62. // These are the flags which may be placed in the flag field of the
  63. // prompt_table structure to control PrintPrompt
  64. //
  65. #define PNULLFLAG 0
  66. #define PTIMFLAG 1
  67. #define PDATFLAG 2
  68. #define PPATFLAG 3
  69. #define PVERFLAG 4
  70. #define PBAKFLAG 5 // destructive backspace flag
  71. #define PNLNFLAG 6 // newline prompt flag
  72. #define PDRVFLAG 7
  73. #define PLITFLAG 8 // Print character in SpecialChar field
  74. #define PDPTFLAG 9 // Print depth of pushd stack
  75. #define PNETFLAG 10 // Print \\server\share or local for current drive
  76. //
  77. // Esc character used to mark a special prompt char. in prompt string
  78. //
  79. #define PROMPTESC DOLLAR
  80. //
  81. // Table of prompts for user.
  82. // BUGBUG This should be in a message file!!!!!!
  83. //
  84. typedef struct {
  85. TCHAR Char; // Used to match esc. char. in user prompt
  86. TCHAR Format; // Used to print some string that has to be computed
  87. TCHAR Literal; // When Format == PLITFLAG this is printed in prompt
  88. } PROMPT_ENTRY;
  89. PROMPT_ENTRY PromptTable[] = {
  90. { TEXT('P'),PPATFLAG, NULLC },
  91. { TEXT('E'),PLITFLAG,'\033' },
  92. { TEXT('D'),PDATFLAG, NULLC },
  93. { TEXT('T'),PTIMFLAG, NULLC },
  94. { TEXT('B'),PLITFLAG, PIPOP },
  95. { TEXT('G'),PLITFLAG, OUTOP },
  96. { TEXT('H'),PBAKFLAG, NULLC },
  97. { TEXT('L'),PLITFLAG, INOP },
  98. { TEXT('N'),PDRVFLAG, NULLC },
  99. { TEXT('S'),PLITFLAG, SPACE },
  100. { TEXT('Q'),PLITFLAG, EQ },
  101. { TEXT('V'),PVERFLAG, NULLC },
  102. { TEXT('_'),PNLNFLAG, NULLC },
  103. { DOLLAR, PLITFLAG, DOLLAR },
  104. { TEXT('A'),PLITFLAG, ANDOP },
  105. { TEXT('C'),PLITFLAG, LPOP },
  106. { TEXT('F'),PLITFLAG, RPOP },
  107. { TEXT('+'),PDPTFLAG, NULLC },
  108. { TEXT('M'),PNETFLAG, NULLC },
  109. { NULLC,PNULLFLAG, NULLC}};
  110. /*** InitLex - initialize the lexer's global variables
  111. *
  112. * Purpose:
  113. * Initialize DataFlag, DataPtr, LexBuf, and LexBufPtr.
  114. *
  115. * InitLex(unsigned dfvalue, int dpvalue)
  116. *
  117. * Args:
  118. * dfvalue - the value to be assigned to DataFlag
  119. * dpvalue - the value to be assigned to DataPtr
  120. *
  121. */
  122. void InitLex(dfvalue, dpvalue)
  123. unsigned dfvalue ;
  124. int dpvalue ;
  125. {
  126. DataFlag = dfvalue ;
  127. DataPtr = dpvalue ;
  128. *LexBuf = NULLC ;
  129. PrevLexPtr = LexBufPtr = LexBuf ; /* M013 - Init new ptr */
  130. DEBUG((PAGRP, LXLVL, "INITLEX: Dataflag = %04x DataPtr = %04x", DataFlag, DataPtr)) ;
  131. }
  132. /*** Lex - controls data input and token lexing
  133. *
  134. * Purpose:
  135. * Read in the next token or argstring and put it in tokbuf.
  136. *
  137. * unsigned Lex(TCHAR *tokbuf, unsigned lflag)
  138. *
  139. * Args:
  140. * tokbuf - buffer used by lex to store the next token or
  141. * - M013 if zero, indicates unget last token.
  142. * lflag - bit 0 on if lex is to return an argument string, ie white space
  143. * other than NLN is not considered a token delimiter
  144. *
  145. * Returns:
  146. * If the token is an operator, EOS, or NLN ret the 1st byte of the token.
  147. * If the token is a command, REM arg or argstring, return TEXTOKEN.
  148. * If the token is longer than MAXTOKLEN or the token is illegal, LEXERROR
  149. * is returned.
  150. *
  151. * Notes:
  152. * The parser depends on the fact that the only values returned by
  153. * Lex that are greater than 0xff are TEXTOKEN and LEXERROR.
  154. *
  155. */
  156. unsigned Lex(tokbuf, lflag)
  157. TCHAR *tokbuf ;
  158. unsigned lflag ;
  159. {
  160. int i ; /* Length of text token */
  161. TCHAR c, /* Current character */
  162. *tbcpy; /* Copy of tokbuf */
  163. if(setjmp(CmdJBuf2)) { /* M026 - Now msg printed prior */
  164. return((unsigned)LEXERROR) ; /* ...to arrival here */
  165. } ;
  166. /* M013 - This code detects request to unget last token and if so, performs
  167. that function. If not, it sets the previous token pointer to
  168. to equal the current token pointer.
  169. */
  170. if (tokbuf == LX_UNGET) { /* Unget last token? */
  171. DEBUG((PAGRP, LXLVL, "LEX: Ungetting last token.")) ;
  172. LexBufPtr = PrevLexPtr ; /* If so, reset ptr... */
  173. return(LX_UNGET) ; /* ...and return */
  174. } else { /* If not, set previous... */
  175. PrevLexPtr = LexBufPtr ; /* ...ptr to current... */
  176. DEBUG((PAGRP, LXLVL, "LEX: lflag = %d", lflag)) ;
  177. } ; /* ...ptr and continue */
  178. /* M013 ends */
  179. tbcpy = tokbuf ;
  180. /* M005 - Altered conditional below to also fail if the LX_REM bit
  181. * is set making it "If !(arg | rem), eat whtspc & delims".
  182. */
  183. if (!(lflag & (LX_ARG|LX_REM))) {
  184. DEBUG((PAGRP, LXLVL, "LEX: Trashing white space.")) ;
  185. while (TRUE) {
  186. c = GetByte();
  187. if (((_istspace(c) && c != NLN)
  188. || (mystrchr(((lflag & LX_EQOK) ? &Delimiters[1] : Delimiters), c) && c)))
  189. ;
  190. else
  191. break;
  192. } ;
  193. UnGetByte() ;
  194. } ;
  195. /* As of M016, operators of more than 2 characters can be lexed. For now,
  196. * these are assumed to be specific-handle redirection operators of the form
  197. * 'n>>' or 'n<<' and always begin with a digit. TextCheck will not return
  198. * a digit as an operator unless it is preceeded by whitespace and followed
  199. * by '>' or '<'. To simplify matters, handle substitution (ie., '...&n')
  200. * is now lexed as part of a special five character operator, instead of
  201. * looking at the '&n' as an argument. ASCII filename arguments, however,
  202. * are still lexed as separate tokens via another call to Lex.
  203. */
  204. if (TextCheck(&c, &lflag) == LX_DELOP) {
  205. *tokbuf++ = c ; /* The token is an operator */
  206. if (_istdigit(c)) { /* Next is '<' or '>'... */
  207. DEBUG((PAGRP, LXLVL, "LEX: Found digit operator.")) ;
  208. c = GetByte() ; /* ...by definition or ... */
  209. *tokbuf++ = c ; /* ...we wouldn't be here */
  210. } ;
  211. if (c == PIPOP || c == ANDOP || c == OUTOP || c == INOP) {
  212. if ((c = GetByte()) == *(tokbuf-1)) {
  213. *tokbuf++ = c ;
  214. c = GetByte() ;
  215. } ;
  216. if (*(tokbuf-1) == OUTOP || *(tokbuf-1) == INOP) {
  217. DEBUG((PAGRP,LXLVL, "LEX: Found redir.")) ;
  218. if (c == CSOP) {
  219. DEBUG((PAGRP,LXLVL, "LEX: Found >&")) ;
  220. *tokbuf++ = c ;
  221. do {
  222. c = GetByte() ;
  223. } while (_istspace(c) ||
  224. mystrchr(Delimiters,c)) ;
  225. if (_istdigit(c)) {
  226. *tokbuf++ = c ;
  227. c = GetByte() ;
  228. } ;
  229. } ;
  230. /* M016 ends */
  231. } ;
  232. UnGetByte() ;
  233. } ;
  234. *tokbuf = NULLC ;
  235. DEBUG((PAGRP, LXLVL, "LEX: Returning op = `%ws'", tbcpy)) ;
  236. return(*tbcpy) ;
  237. } ;
  238. DEBUG((PAGRP, LXLVL, "LEX: Found text token %04x, Getting more.", c)) ;
  239. *tokbuf++ = c ; /* Found text token, read the rest */
  240. lflag |= LX_DBLOK;
  241. AtIsToken = 0; /* @@4, treat @ as text now */
  242. for (i = 1 ; TextCheck(&c, &lflag) != LX_DELOP && i < MAXTOKLEN ; i++)
  243. *tokbuf++ = c ;
  244. lflag &= ~LX_DBLOK;
  245. *tokbuf = NULLC ;
  246. if (i < MAXTOKLEN)
  247. UnGetByte() ;
  248. if (i >= MAXTOKLEN && c != (TCHAR) -1) { /* Token too long, complain */
  249. /* M025 */ PutStdErr(MSG_TOKEN_TOO_LONG, ONEARG, argstr1(TEXT("%s"), (unsigned long)((int)tokbuf)));
  250. return((unsigned)LEXERROR) ;
  251. } ;
  252. DEBUG((PAGRP, LXLVL, "LEX: Return text = `%ws' type = %04x", tbcpy, TEXTOKEN)) ;
  253. return(TEXTOKEN) ;
  254. }
  255. /*** TextCheck - get the next character and determine its type
  256. *
  257. * Purpose:
  258. * Store the next character in LexBuf in *c. If that character is a
  259. * valid text token character, return it. Otherwise return LX_DELOP.
  260. *
  261. * int TextCheck(TCHAR *c, unsigned &lflag)
  262. *
  263. * Args:
  264. * c - the next character in the lexer input buffer is stored here.
  265. * lflag - Bit 0 = On if lex is to return an argument string, ie.,
  266. * white space other than NLN is not a token delimiter.
  267. * Bit 1 = On if a quoted string is being read, ie., only NLN
  268. * or a closing quote are delimiters.
  269. * Bit 2 = On if equalsigns are NOT to be considered delimiters.
  270. * Bit 3 = On if left parens are to be considered operators.
  271. * Bit 4 = On if right parens are to be considered operators.
  272. * Bit 5 = On if only NLN is to be a delimiter.
  273. * Bit 6 = On iff the caller is willing to accept the second
  274. * half of a double byte character
  275. *
  276. * Returns:
  277. * Next character or LX_DELOP if a delimeter/operator character is found.
  278. *
  279. */
  280. int TextCheck(c, lflag)
  281. TCHAR *c ;
  282. unsigned *lflag ;
  283. {
  284. TCHAR i ; /* M016 - Temp byte holder */
  285. static int saw_dbcs_lead = 0; /* remember if we're in the middle
  286. of a double byte character */
  287. *c = GetByte() ;
  288. if (saw_dbcs_lead) {
  289. saw_dbcs_lead = 0;
  290. if (*lflag & LX_DBLOK) /* If second half of double byte is */
  291. return(*c); /* ok, return it, otherwise. . . */
  292. else
  293. *c = GetByte(); /* go on to next character */
  294. }
  295. DEBUG((PAGRP, BYLVL, "TXTCHK: c = %04x lflag = %04x", *c, *lflag)) ;
  296. switch (*c) {
  297. case SILOP: /* M017 - New unary operator */
  298. /* ...binds like left paren */
  299. if ((*lflag & (LX_QUOTE|LX_REM))) /* M005 */
  300. break ;
  301. if( !AtIsToken ) /* If @ is not to be treated */
  302. { /* as token, then indicate */
  303. return( *c ); /* such @@4 */
  304. } ;
  305. case LPOP: /* M002 - Moved these two cases */
  306. if ((*lflag & (LX_QUOTE|LX_REM))) /* M005 */
  307. break ;
  308. if(!(*lflag & GT_LPOP)) /* ...up and break if */
  309. break ; /* ...they are not to */
  310. case RPOP: /* ...be treated as ops */
  311. if ((*lflag & (LX_QUOTE|LX_REM))) /* M005 */
  312. break ;
  313. if((!(*lflag & GT_RPOP)) && *c == RPOP)
  314. break ; /* M002 ends */
  315. case NLN: /* M005 - NLN turns off QUOTE/REM flags */
  316. case EOS: /* @@5a - treat like NLN */
  317. *lflag &= (~LX_QUOTE & ~LX_REM) ; /* M005 */
  318. case CSOP:
  319. case INOP: /* M005 - Note below that LX_DELOP... */
  320. case PIPOP: /* ...QUOTE mode or REM mode is in... */
  321. case OUTOP: /* ...in effect at the time */
  322. if (!(*lflag & (LX_QUOTE|LX_REM))) /* M005 */
  323. return(LX_DELOP) ;
  324. } ;
  325. /* M003 - If the character is '^', and the QUOTE mode flag is off,
  326. * discard the current character, get the next one and return
  327. * it as text.
  328. * M005 - Extended this conditional to insure that both QUOTE and
  329. * REM flags must be off for "escape" to occur.
  330. */
  331. if (*c == ESCHAR && !(*lflag & (LX_QUOTE|LX_REM))) {
  332. *c = GetByte() ;
  333. if (*c == NLN)
  334. *c = GetByte() ;
  335. return(*c) ;
  336. } ;
  337. /* M003/M005 end */
  338. if (*c == QUOTE) /* Flip quote mode flag bit */
  339. *lflag ^= LX_QUOTE ;
  340. /* M005 - Altered conditional below to also insure that REM flag was
  341. * off before checking for any delimiters
  342. */
  343. if (!(*lflag & (LX_ARG|LX_QUOTE|LX_REM)) &&
  344. (_istspace(*c) ||
  345. mystrchr(((*lflag & LX_EQOK) ? &Delimiters[1] : Delimiters), *c)))
  346. return(LX_DELOP) ;
  347. /* As of M016, before accepting this character as text, it is now tested
  348. * for being a digit followed by one of the redirection operators and;
  349. * 1) is the first character on a line, 2) is preceeded by whitespace or
  350. * 3) is preceeded by a delimiter (including Command's operators). If it
  351. * meets these conditions, it is a special, specific-handle redirection
  352. * operator and TextCheck must return LX_DELOP so that Lex can build the
  353. * remainder. NOTE: LexBufPtr is advanced during GetByte, so that the
  354. * current byte is *(LexBufPtr-1).
  355. */
  356. if (_istdigit(*c)) {
  357. DEBUG((PAGRP,BYLVL,"TXTCHK: Found digit character.")) ;
  358. if ((LexBufPtr-LexBuf) < 2 ||
  359. _istspace(i = *(LexBufPtr-2)) ||
  360. mystrchr(TEXT("()|&=,;\""), i)) {
  361. DEBUG((PAGRP,BYLVL,"TXTCHK: Digit follows delim.")) ;
  362. if (*LexBufPtr == INOP || *LexBufPtr == OUTOP) {
  363. DEBUG((PAGRP,BYLVL,"TXTCHK: Found hdl redir")) ;
  364. if (!(*lflag & (LX_QUOTE|LX_REM))) /* M005 */
  365. return(LX_DELOP) ;
  366. } ;
  367. } ;
  368. } ;
  369. /* M016 ends */
  370. return(*c) ;
  371. }
  372. /*** GetByte - return the next byte in the buffer
  373. *
  374. * Purpose:
  375. * Get the next byte from the lexer's input buffer. If the buffer is
  376. * empty, fill it first. Update the buffer pointer.
  377. *
  378. * TCHAR GetByte()
  379. *
  380. * Return:
  381. * The next character in the buffer or EOF.
  382. *
  383. * Notes:
  384. * All three types of input STDIN, FILE and STRING are treated
  385. * the same now when it comes to dealing with CR/LF combinations.
  386. * Keyboard input is massaged to look like file input.
  387. * Invalid double byte characters are thrown away and not returned
  388. * to the caller.
  389. *
  390. */
  391. TCHAR GetByte()
  392. {
  393. static int saw_dbcs_lead = 0; /* remember if we're in the middle
  394. of a double byte character */
  395. TCHAR lead; /* variables for remember parts of
  396. double byte characters */
  397. if (!*LexBufPtr)
  398. FillBuf() ;
  399. DEBUG((PAGRP, BYLVL, "GTTCHAR: byte = %04x", *LexBufPtr)) ;
  400. if (*LexBufPtr == CR && !saw_dbcs_lead) {
  401. /* M000 - removed file-only test */
  402. LexBufPtr++ ;
  403. return(GetByte()) ;
  404. } ;
  405. /* if this is a double byte character, look ahead to the next character
  406. and check for legality */
  407. if (saw_dbcs_lead) {
  408. saw_dbcs_lead = 0;
  409. return(*LexBufPtr++) ;
  410. }
  411. else {
  412. lead = *LexBufPtr++;
  413. return(lead);
  414. }
  415. }
  416. /*** UnGetByte - rewind the lexer buffer pointer 1 byte
  417. *
  418. * Purpose:
  419. * Back up the lexer's buffer pointer. If the pointer already points
  420. * to the beginning of the buffer, do nothing.
  421. *
  422. * UnGetByte()
  423. *
  424. * Return:
  425. * Nothing.
  426. *
  427. */
  428. void UnGetByte()
  429. {
  430. if (!(LexBufPtr == LexBuffer))
  431. LexBufPtr-- ;
  432. }
  433. /*** FillBuf - read data to fill the lexer's buffer
  434. *
  435. * Purpose:
  436. * To fill the lexer's buffer with data from whatever source is indicated
  437. * by the global variables DataFlag and DataPtr. If reading from
  438. * stdin, prompt for data.
  439. *
  440. * FillBuf()
  441. *
  442. * Notes:
  443. * - Algorithm after M021 is as follows:
  444. * copy last char of current buffer into LexBuffer[0] (which preceeds
  445. * LexBuf by one byte) so the UnGetByte can unget two bytes
  446. * If READSTDIN or READFILE
  447. * If input is STDIN
  448. * Print correct prompt
  449. * Use DOSREAD to fill primary buffer
  450. * Copy to Lexer buffer so that primary buffer is usable by F3 key.
  451. * Null terminate total input.
  452. * Scan buffer for NLN || ^Z
  453. * If none found
  454. * Error out
  455. * Else
  456. * Terminate statement at NLN or ^Z (exclude ^Z iteself)
  457. * If read was from file
  458. * Rewind to end of first statement
  459. * If file handle is STDIN
  460. * Echo statement to STDOUT
  461. * Else
  462. * Read first statement from string and reset pointer
  463. * Reset Lexer Buffer Pointer to start of buffer
  464. * Substitute for batch and environment variables (M026)
  465. * Reset Previous Lexer Buffer Pointer to start of buffer
  466. *
  467. */
  468. BOOL ReadFromStdInOkay = FALSE;
  469. void FillBuf()
  470. {
  471. long l ; /* M004 - Data count in buffer */
  472. TCHAR *sptr ; /* Copy of DataPtr */
  473. size_t i ; /* Work variable */
  474. DWORD cnt ; /* Count of input bytes */
  475. BOOL flag;
  476. //
  477. // clear this flag in case it was hit from previous command
  478. // if it is true we would not execute the next command
  479. //
  480. ResetCtrlC();
  481. LexBuffer[0] = *(LexBufPtr - 1);
  482. switch (DataFlag & FIRSTIME) {
  483. case READFILE:
  484. case READSTDIN:
  485. if ((DataFlag & FIRSTIME) == READSTDIN ||
  486. DataPtr == STDIN) {
  487. if (DataFlag & NOTFIRSTIME) {
  488. /* M025 */ PutStdOut(MSG_MS_MORE, NOARGS);
  489. } else {
  490. PrintPrompt() ;
  491. DataFlag |= NOTFIRSTIME ;
  492. DEBUG((PAGRP, LFLVL, "FLBF: Reading stdin")) ;
  493. } ;
  494. } ;
  495. //
  496. // clear in case ^c seen while printing prompt
  497. //
  498. ResetCtrlC();
  499. DEBUG((PAGRP, LFLVL, "FLBF: Reading handle %d", DataPtr)) ;
  500. //
  501. // If input is STDIN and piped or input is from a
  502. // device but not console input (flgwd == 1)
  503. //
  504. if ( ( DataPtr == STDIN ) && ( FileIsPipe( STDIN ) ||
  505. ( FileIsDevice( STDIN ) && (!(flgwd & 1)) ) ) ) {
  506. cnt = 0;
  507. while (
  508. ( cnt < LBUFLEN) && /* ##1 */
  509. ( (ReadBufFromFile(CRTTONT(DataPtr),
  510. &FrsBuf[cnt], 1, (LPDWORD)&i)) != 0 ||
  511. GetLastError() == ERROR_MORE_DATA) &&
  512. ( i != 0 )
  513. ) {
  514. cnt++;
  515. if ( FrsBuf[cnt-1] == NLN ){
  516. break;
  517. } /* endif */
  518. }
  519. } else if ( ( DataPtr == STDIN ) &&
  520. FileIsDevice( STDIN ) &&
  521. (flgwd & 1) ) {
  522. //
  523. // Are reading from stdin and it is a device
  524. // (not a file) and it is console input
  525. //
  526. if ( KeysFlag ) {
  527. i = EditLine( DataPtr, FrsBuf, LBUFLEN, &cnt );
  528. }
  529. else {
  530. ResetCtrlC();
  531. if (ReadBufFromConsole(
  532. CRTTONT(DataPtr),
  533. FrsBuf,
  534. LBUFLEN,
  535. &cnt) ) {
  536. //
  537. // Check that ^c's on the current line.
  538. // Could be the case where ^c thread
  539. // came in from a previous line
  540. //
  541. //
  542. // also if cnt is 0 then outpt crlf to
  543. // prevent two prompts on command line
  544. //
  545. if (cnt == 0) {
  546. if (GetLastError() == ERROR_OPERATION_ABORTED) {
  547. cmd_printf(CrLf);
  548. longjmp(CmdJBuf2, -1);
  549. }
  550. cmd_printf(CrLf);
  551. }
  552. i = 0;
  553. DEBUG((PAGRP, LFLVL, "FLBF: ReadFile %d bytes", cnt)) ;
  554. } else {
  555. cnt = 0;
  556. i = GetLastError();
  557. DEBUG((PAGRP, LFLVL, "FLBF: ReadFile %d bytes and error %d", cnt, i)) ;
  558. }
  559. }
  560. }
  561. else {
  562. flag = ReadBufFromFile(
  563. CRTTONT(DataPtr),
  564. FrsBuf, LBUFLEN, (LPDWORD)&cnt) ;
  565. DEBUG((PAGRP, LFLVL, "FLBF: Read %d bytes", cnt)) ;
  566. if (CtrlCSeen) {
  567. ResetCtrlC();
  568. longjmp(CmdJBuf2, -1);
  569. // Abort();
  570. }
  571. if (flag == 0 || (int)cnt <= 0) {
  572. cnt = 0;
  573. i = GetLastError();
  574. }
  575. else {
  576. i = 0;
  577. }
  578. }
  579. DEBUG((PAGRP, LFLVL, "FLBF: I made it here alive")) ;
  580. if (!cnt && DataPtr == STDIN) {
  581. DEBUG((PAGRP,LFLVL,"FLBF: ^Z from STDIN!")) ;
  582. DEBUG((PAGRP,LFLVL," READFILE retd %d",i)) ;
  583. if (FileIsDevice(STDIN) && ReadFromStdInOkay) {
  584. DEBUG((PAGRP,LFLVL,"FLBF: Is device, fixing up buffer")) ;
  585. FrsBuf[0] = NLN ;
  586. ++cnt ;
  587. } else {
  588. DEBUG((PAGRP,LFLVL,"FLBF: Is file, aborting!!!")) ;
  589. ExitAbort(EXIT_EOF) ;
  590. } ;
  591. } else if (!ReadFromStdInOkay && cnt && DataPtr == STDIN) {
  592. ReadFromStdInOkay = TRUE;
  593. }
  594. cnt = LexCopy(LexBuf, FrsBuf, cnt);
  595. DEBUG((PAGRP, LFLVL, "FLBF: Received %d characters.", cnt)) ;
  596. *(LexBuf+cnt) = NULLC ; /* Term with NULL */
  597. /* Have total bytes read. Now scan for NLN or ^Z. Either means end of
  598. * input statement, neither in 128 bytes means buffer overflow error.
  599. */
  600. if((i = mystrcspn(LexBuf, TEXT("\n\032"))) < mystrlen(LexBuf)
  601. || cnt == 0) { /*M029*/
  602. DEBUG((PAGRP, LFLVL, "FLBF: Scan found %04x", *(LexBuf+i))) ;
  603. DEBUG((PAGRP, LFLVL, "FLBF: At position %d", i)) ;
  604. sptr = LexBuf+i ; /* Set pointer */
  605. if(*sptr == CTRLZ) {
  606. *sptr = NLN;
  607. }
  608. if(*sptr == NLN) { /* If \n, inc... */
  609. ++sptr ; /* ...ptr & sub */
  610. l = cnt - ++i ; /* ....whats used */
  611. /* M014 ends */ i = FILE_CURRENT ;
  612. } else { /* If ^Z, go EOF */
  613. l = 0 ;
  614. i = FILE_END ;
  615. } ;
  616. DEBUG((PAGRP,LFLVL,"FLBF: Changing %04x to NULLC",*(sptr-1))) ;
  617. *sptr = NULLC ; /* Term valid input */
  618. if (!FileIsDevice(DataPtr)) {
  619. SetFilePointer(CRTTONT(DataPtr), -l, NULL, i) ;
  620. DEBUG((PAGRP, LFLVL, "FLBF: Rewound %ld", l)) ;
  621. if ((DataPtr == STDIN) && (!Necho)) {
  622. cmd_printf(Fmt14, LexBuf) ;
  623. } ;
  624. } ;
  625. } else if(i >= LBUFLEN) { /*M029*/
  626. /* @@4 */ if ( global_dfvalue == READFILE )
  627. /* @@4 */ {
  628. /* @@4 */ if ( EchoFlag == E_ON )
  629. /* @@4 */ {
  630. /* @@4 */ DEBUG((PAGRP, LXLVL,
  631. /* @@4 */ "BLOOP: Displaying Statement.")) ;
  632. /* @@4 */
  633. /* @@4 */ PrintPrompt() ;
  634. /* @@4 */ cmd_printf(&LexBuffer[1]) ;
  635. /* @@4 */ cmd_printf(CrLf) ;
  636. /* @@4 */ } ;
  637. /* @@4 */ PutStdErr(MSG_LINES_TOO_LONG, NOARGS) ;
  638. /* @@4 */ Abort() ;
  639. /* @@4 */ } ;
  640. PutStdErr(MSG_LINES_TOO_LONG, NOARGS) ;
  641. /* M028 */ if(DataPtr == STDIN && FileIsDevice(DataPtr))
  642. FlushKB() ;
  643. longjmp(CmdJBuf2,-1) ;
  644. } ;
  645. break ;
  646. case READSTRING:
  647. DEBUG((PAGRP, LFLVL, "FLBF: Reading string.")) ;
  648. *(LexBuf+LBUFLEN) = NULLC ; /* Term max string */
  649. _tcsncpy(LexBuf, (TCHAR *) DataPtr, LBUFLEN) ;
  650. DataPtr += mystrlen(LexBuf)*sizeof(TCHAR) ; /* Update DataPtr */
  651. DEBUG((PAGRP, LFLVL, "FLBF: New DataPtr = %ws", DataPtr)) ;
  652. break ;
  653. } ;
  654. LexBufPtr = LexBuf ; /* M004 - Reset pointer */
  655. SubVar() ; /* Sub env & batch vars (M026) */
  656. DEBUG((PAGRP, LFLVL, "FLBF: Buffer contents: `%ws'", LexBufPtr)) ;
  657. /* Insure that when buffer is refilled, the previous token pointer is
  658. * reset to the start of the lexer buffer
  659. */
  660. PrevLexPtr = LexBufPtr ;
  661. }
  662. /*** LexCopy - copy the lex buffer
  663. *
  664. * Purpose:
  665. * To copy the contents read into the dos buffer into LexBuf,
  666. * translating double byte spaces into regular spaces in the
  667. * process.
  668. * Input:
  669. * A to and from pointer and a byte count.
  670. * Returned:
  671. * A new byte count which might be smaller than that passed in.
  672. */
  673. int LexCopy(to, from, count)
  674. TCHAR *to, *from;
  675. int count;
  676. {
  677. int new_count;
  678. _tcsncpy( to, from, count );
  679. return count;
  680. }
  681. BOOLEAN PromptValid;
  682. TCHAR PromptVariableBuffer[ 256 ];
  683. TCHAR PromptBuffer[ 256 ];
  684. void
  685. PrintPrompt()
  686. /*++
  687. Routine Description:
  688. To print Command's main input prompt and to interpret the special
  689. characters in it (see MSDOS manual for list of special prompt
  690. characters).
  691. An array of PROMPT_ENTRY structures called PromptTable is searched for
  692. the special characters. If a match is found , then either print out
  693. the special character if the format field is PLITFLAG or do some
  694. sort of special processing to print out the prompt string such as
  695. get time of day etc.
  696. Arguments:
  697. Return Value:
  698. --*/
  699. {
  700. TCHAR *pszPrompt ;
  701. TCHAR *s;
  702. int nLeft, nUsed;
  703. ULONG idx;
  704. ULONG vrs ;
  705. #if defined(JAPAN) && defined(UNICODE)
  706. // This local variable is used for determine the last
  707. // character is full width character (=DBCS) or not.
  708. WCHAR chLast = NULLC;
  709. #endif /* defined(JAPAN) && defined(UNICODE) */
  710. #if 0
  711. //
  712. // used in pipe if a ^c see on dispatch of left side
  713. //
  714. if (ExtCtrlc) {
  715. if (ExtCtrlc == 1)
  716. PutStdOut(MSG_CMD_KILLED, NOARGS);
  717. ExtCtrlc = 0;
  718. }
  719. if (Ctrlc) {
  720. PutStdOut(MSG_C, NOARGS);
  721. Ctrlc = 0;
  722. }
  723. #endif
  724. if (CtrlCSeen) {
  725. PutStdOut(MSG_C, NOARGS);
  726. ResetCtrlC();
  727. // Abort();
  728. }
  729. //
  730. // The newline which must preceed the prompt is tied to prompt rather than to
  731. // command completion in Dispatch.
  732. //
  733. // Also return without newline or prompt if echo status is "echo off".
  734. //
  735. if (EchoFlag == E_OFF) {
  736. return ;
  737. }
  738. if (!NulNode) {
  739. cmd_printf(CrLf) ;
  740. }
  741. if ( PromptValid ) {
  742. pszPrompt = PromptVariableBuffer;
  743. }
  744. else {
  745. //
  746. // Fetch user prompt string from environment (should be PROMPT)
  747. //
  748. pszPrompt = GetEnvVar(PromptStr) ;
  749. if ( pszPrompt ) {
  750. mystrcpy( PromptVariableBuffer, pszPrompt);
  751. pszPrompt = PromptVariableBuffer;
  752. PromptValid = TRUE;
  753. }
  754. }
  755. //
  756. // refetch the current directory, since we may have lost the
  757. // drive letter due to net disconnect
  758. //
  759. GetDir(CurDrvDir, GD_DEFAULT) ;
  760. DEBUG((PAGRP, LFLVL, "PRINTPROMPT: pszPrompt = `%ws'", pszPrompt)) ;
  761. s = PromptBuffer;
  762. *s = NULLC;
  763. nLeft = sizeof(PromptBuffer) / sizeof(TCHAR);
  764. //
  765. // Check if there was a prompt string.
  766. // If there is not prompt string then just print current drive
  767. //
  768. if (!pszPrompt || !*pszPrompt) {
  769. nUsed = _sntprintf( s, nLeft, Fmt27, CurDrvDir) ;
  770. s += nUsed;
  771. nLeft -= nUsed;
  772. } else {
  773. //
  774. // Loop through interpreting prompt string
  775. //
  776. for ( ; *pszPrompt ; pszPrompt++) {
  777. //
  778. // Look for the escape character in prompt for special
  779. // processing
  780. //
  781. if (*pszPrompt != PROMPTESC) {
  782. nUsed = _sntprintf( s, nLeft, Fmt19, *pszPrompt) ;
  783. s += nUsed;
  784. nLeft -= nUsed;
  785. #if defined(JAPAN) && defined(UNICODE)
  786. // If character is full width character, mark it.
  787. if (IsFullWidth(*pszPrompt))
  788. chLast = pszPrompt;
  789. else
  790. chLast = NULLC;
  791. #endif /* defined(JAPAN) && defined(UNICODE) */
  792. } else {
  793. //
  794. // There is an escape character in prompt string.
  795. // Try to find a match for next character after escape
  796. // character from prompt table.
  797. //
  798. pszPrompt++;
  799. for (idx = 0 ; PromptTable[idx].Char != NULLC ; idx++)
  800. if (_totupper(*pszPrompt) == PromptTable[idx].Char) {
  801. break ;
  802. }
  803. if (PromptTable[idx].Char == NULLC) {
  804. //
  805. // Could find no match for escape. Return without finishing
  806. // printing
  807. //
  808. //
  809. // If ^c seen while printing prompt blow it away
  810. //
  811. if (CtrlCSeen) {
  812. ResetCtrlC();
  813. }
  814. return ;
  815. } else {
  816. if (PromptTable[idx].Format == PLITFLAG) {
  817. nUsed = _sntprintf( s, nLeft, Fmt19, PromptTable[idx].Literal) ;
  818. s += nUsed;
  819. nLeft -= nUsed;
  820. } else {
  821. switch (PromptTable[idx].Format) {
  822. case PTIMFLAG:
  823. nUsed = PrintTime(NULL, PT_TIME, s, nLeft) ;
  824. s += nUsed;
  825. nLeft -= nUsed;
  826. break ;
  827. case PDATFLAG:
  828. nUsed = PrintDate(NULL, PD_DATE, s, nLeft) ;
  829. s += nUsed;
  830. nLeft -= nUsed;
  831. break ;
  832. case PPATFLAG:
  833. nUsed = _sntprintf( s, nLeft, Fmt14, CurDrvDir) ;
  834. s += nUsed;
  835. nLeft -= nUsed;
  836. break ;
  837. case PVERFLAG:
  838. vrs = GetVersion();
  839. GetMsg( MSG_MS_DOS_VERSION,
  840. THREEARGS,
  841. VerMsg,
  842. argstr1( TEXT("%d"), (unsigned long)(vrs & 0xFF)),
  843. argstr2( TEXT("%-2d"), (unsigned long)((vrs >> 8)& 0xFF))
  844. );
  845. nUsed = _sntprintf( s, nLeft, Fmt14, MsgBuf );
  846. s += nUsed;
  847. nLeft -= nUsed;
  848. break ;
  849. case PBAKFLAG:
  850. #if defined(DBCS) // PrintPrompt()
  851. // if the last character is full width character.
  852. // we should delete 2 bytes.
  853. if (chLast != NULLC)
  854. nUsed = _sntprintf( s, nLeft, DDBkSpc);
  855. else
  856. nUsed = _sntprintf( s, nLeft, DBkSpc) ;
  857. #else
  858. nUsed = _sntprintf( s, nLeft, DBkSpc) ;
  859. #endif // defined(DBCS)
  860. s += nUsed;
  861. nLeft -= nUsed;
  862. break ;
  863. case PNLNFLAG:
  864. nUsed = _sntprintf( s, nLeft, CrLf) ;
  865. s += nUsed;
  866. nLeft -= nUsed;
  867. break ;
  868. case PDPTFLAG:
  869. case PNETFLAG:
  870. //
  871. // If extensions are enabled, then two new prompt characters
  872. //
  873. // $+ generates from zero to N plus characters, depending upon
  874. // the depth of the PUSHD directory stack.
  875. //
  876. // $m generates the empty string if the current drive is not a
  877. // network drive. If it is, then $m generates the \\server\share
  878. // name with a trailing space.
  879. //
  880. if (fEnableExtensions) {
  881. if (PromptTable[idx].Format == PDPTFLAG) {
  882. nUsed = _sntprintf( s, nLeft, TEXT("%.*s"), GetDirStackDepth(), TEXT("+++++++++++++++++++++++++++++++++"));
  883. s += nUsed;
  884. nLeft -= nUsed;
  885. }
  886. else {
  887. TCHAR CurDrive[4];
  888. TCHAR NetPath[MAX_PATH];
  889. DWORD n;
  890. _tcsncpy(CurDrive, CurDrvDir, 2);
  891. CurDrive[2] = NULLC;
  892. n = MAX_PATH;
  893. switch (WNetGetConnection(CurDrive,
  894. NetPath,
  895. &n
  896. )
  897. ) {
  898. case NO_ERROR:
  899. NetPath[n] = NULLC;
  900. nUsed = _sntprintf( s, nLeft, TEXT("%s "), NetPath);
  901. s += nUsed;
  902. nLeft -= nUsed;
  903. break;
  904. case ERROR_NOT_CONNECTED:
  905. break;
  906. default:
  907. nUsed = _sntprintf( s, nLeft, TEXT("Unknown"));
  908. s += nUsed;
  909. nLeft -= nUsed;
  910. break;
  911. }
  912. }
  913. }
  914. break;
  915. default:
  916. nUsed = _sntprintf( s, nLeft, Fmt19, CurDrvDir[0]) ;
  917. s += nUsed;
  918. nLeft -= nUsed;
  919. }
  920. }
  921. }
  922. }
  923. } // for
  924. } // else
  925. *s = NULLC;
  926. cmd_printf(TEXT("%s"), PromptBuffer);
  927. //
  928. // If ^c seen while printing prompt blow it away
  929. //
  930. if (CtrlCSeen) {
  931. ResetCtrlC();
  932. }
  933. }
  934. /*** IsData - check the input buffer
  935. *
  936. * Purpose:
  937. * Check the lexer's input buffer to see if there is data in it.
  938. *
  939. * int IsData()
  940. *
  941. * Returns:
  942. * TRUE if the buffer has data in it.
  943. * FALSE if the buffer is empty.
  944. *
  945. */
  946. int IsData()
  947. {
  948. DEBUG((PAGRP, LXLVL, "ISDATA: *LexBufPtr = %04x", *LexBufPtr)) ;
  949. if (*LexBufPtr)
  950. return(TRUE) ;
  951. return(FALSE) ;
  952. }
  953. /*** SubVar - Substitute for environment variables. (M004)
  954. *
  955. * Purpose:
  956. * This function scans the lexer input buffer looking for percent
  957. * signs and substituting batch variables and environment variables
  958. * as they are found.
  959. *
  960. * void SubVar()
  961. *
  962. * NOTES:
  963. * - This function does not return if expansion causes length to exceed
  964. * maximum line length (LBUFLEN = 2048).
  965. * - M026 caused complete rewrite to perform batch variable substitution
  966. * at the lexer stage rather than in batch processing. Note that the
  967. * printing of error messages can now be either line too long or token
  968. * too long, so error printing occurs before the longjmp() call.
  969. */
  970. void SubVar()
  971. {
  972. TCHAR lxtmp[LBUFLEN+1] ; /* Temporary holding buffer */
  973. int dlen ; /* Temps & counters */
  974. int j, slen ;
  975. TCHAR *srcp = lxtmp ; /* Src byte pointer */
  976. TCHAR *substr = NULL ; /* Possible Env Var pointer */
  977. TCHAR c ; /* Temp byte holder */
  978. mystrcpy(srcp,LexBufPtr) ; /* Make a copy of the input */
  979. DEBUG((PAGRP, LXLVL, "SBENV: Copy = %ws", srcp)) ;
  980. dlen = j = slen = 0 ; /* Zero the counters */
  981. while((c = *srcp++) && dlen <= LBUFLEN + 1) {
  982. if(c != PERCENT) {
  983. *LexBufPtr++ = c ;
  984. ++dlen ;
  985. if(c == NLN) /* Stop subst. if statement end */
  986. break ;
  987. continue ;
  988. } ;
  989. DEBUG((PAGRP,LXLVL,"SBVAR: Found `%%' in input")) ;
  990. DEBUG((PAGRP,LXLVL,"SBVAR: Current pair is `%c%c'",c,*srcp)) ;
  991. if (CurBat && *srcp == PERCENT) {
  992. DEBUG((PAGRP,LXLVL,"SBVAR: Found `%%%%' in batch file")) ;
  993. *LexBufPtr++ = *srcp++ ;
  994. ++dlen ;
  995. continue ;
  996. } ;
  997. //
  998. // If inside a command script and extensions are enabled,
  999. // expand %* into all the arguments (%1 through %n).
  1000. //
  1001. if (CurBat && fEnableExtensions && *srcp == STAR) {
  1002. ++srcp ; /* Kick past star */
  1003. slen = mystrlen(CurBat->orgargs);
  1004. substr = CurBat->orgargs;
  1005. DEBUG((PAGRP,LXLVL,"SBVAR: Found batch var %*")) ;
  1006. DEBUG((PAGRP,LXLVL,"SBVAR: - len = %d", slen)) ;
  1007. DEBUG((PAGRP,LXLVL,"SBVAR: - var = %ws", substr)) ;
  1008. if (slen > 0) {
  1009. if (dlen+slen > MAXTOKLEN) {
  1010. DEBUG((PAGRP,LXLVL,"SBVAR: Too Long!"));
  1011. _tcsncpy(LexBufPtr,substr,MAXTOKLEN - dlen) ;
  1012. LexBuf[MAXTOKLEN] = NULLC ;
  1013. PutStdErr(MSG_TOKEN_TOO_LONG, ONEARG,LexBuf) ;
  1014. longjmp(CmdJBuf2,-1) ;
  1015. } ;
  1016. mystrcpy(LexBufPtr, substr) ;
  1017. dlen += slen ;
  1018. LexBufPtr += slen ;
  1019. DEBUG((PAGRP,LXLVL,"SBVAR: Subst complete; dest = `%ws'", LexBuf)) ;
  1020. } else {
  1021. DEBUG((PAGRP,LXLVL,"SBVAR: Var %* undefined")) ;
  1022. } ;
  1023. continue ;
  1024. } ;
  1025. //
  1026. // If inside a command script attempt to expand variable references
  1027. // of the form %n where n is a digit from 0 to 9
  1028. //
  1029. // If not in a command script or not a variable reference see if
  1030. // this is an environment variable expansion request.
  1031. //
  1032. if((CurBat &&
  1033. (substr = MSCmdVar(&CmdJBuf2,srcp,&j,TEXT("0123456789"),CurBat->aptrs))
  1034. ) ||
  1035. (substr = MSEnvVar(&CmdJBuf2,srcp,&j, PERCENT)) != NULL
  1036. ) {
  1037. DEBUG((PAGRP,LXLVL,"SBVAR: Found var %ws", substr)) ;
  1038. //
  1039. // Either variable reference or environment variable reference.
  1040. // Copy the result to the input buffer
  1041. //
  1042. if((dlen += (slen = mystrlen(substr))) > LBUFLEN+1) {
  1043. PutStdErr(MSG_LINES_TOO_LONG, NOARGS);
  1044. longjmp(CmdJBuf2,-1) ;
  1045. } ;
  1046. mystrcpy(LexBufPtr,substr) ;
  1047. LexBufPtr += slen ;
  1048. srcp += j ; /* M027 'j+1' --> 'j' */
  1049. } else {
  1050. DEBUG((PAGRP,LXLVL,"SBVAR: No var found")) ;
  1051. //
  1052. // Variable not found. If inside of command script, toss
  1053. // the variable reference in the bit bucket. If not in a
  1054. // command script pass the characters that make up the reference
  1055. // into the input buffer. User will see their mistake shortly.
  1056. //
  1057. if (CurBat) {
  1058. srcp += j ;
  1059. } else {
  1060. *LexBufPtr++ = c ;
  1061. dlen++ ;
  1062. } ;
  1063. } ;
  1064. } ;
  1065. *LexBufPtr = NULLC ; /* Terminate Statement */
  1066. LexBufPtr = LexBuf ; /* Reset Pointer to start */
  1067. if(dlen > LBUFLEN+1) { /* Statement too long?? */
  1068. *LexBufPtr = NULLC ; /* If so, kill line, print err */
  1069. PutStdErr(MSG_LINES_TOO_LONG, NOARGS);
  1070. longjmp(CmdJBuf2,-1) ;
  1071. } ;
  1072. }
  1073. /*** MSEnvVar - Does environment variable substitution
  1074. *
  1075. * Purpose:
  1076. * When percent signs are found in the newly filled lexer buffer,
  1077. * this function is called to determine if there is an environment
  1078. * variable substitution possible.
  1079. *
  1080. * TCHAR *MSEnvVar(TCHAR *str, int *supdate, TCHAR delim)
  1081. *
  1082. * Args:
  1083. * errjmp - optional pointer to jmp_buf for errors
  1084. * str - pointer to a possible environment variable name
  1085. * supdate - location to place env variable name length in
  1086. * delim - delimiter character to look for (e.g. PERCENT)
  1087. *
  1088. * Returns:
  1089. * If there is no ending delim,
  1090. * set supdate to 0
  1091. * return NULL
  1092. * else
  1093. * set supdate to the enclosed string length
  1094. * if the string is not an environment variable
  1095. * return NULL
  1096. * else
  1097. * return a pointer to the replacement string
  1098. *
  1099. * Notes:
  1100. * - M026 changed the way this function works so that supdate will
  1101. * contain the string length if any string was found. This allows
  1102. * the string to be deleted if within a batch file.
  1103. *
  1104. */
  1105. TCHAR *MSEnvVar(errjmp, str, supdate, delim)
  1106. jmp_buf *errjmp ;
  1107. TCHAR *str ;
  1108. int *supdate ;
  1109. TCHAR delim ;
  1110. {
  1111. TCHAR *w0 ; /* Points to ending delim */
  1112. TCHAR *w1 ; /* Will hold ptr to env var value */
  1113. TCHAR *w2 ;
  1114. TCHAR *wSrch ; /* Will hold ptr to search string */
  1115. TCHAR *wRepl ; /* Will hold ptr to replace string */
  1116. TCHAR savec ;
  1117. int noff, nlen, nsrchlen, nrepllen, ndeltalen, fPrefixMatch;
  1118. *supdate = 0 ; /* M026 - Init to "Not found" */
  1119. for (w0 = str ; *w0 ; w0++) /* Search for ending delim */
  1120. if (fEnableExtensions) {
  1121. if (*w0 == delim ||
  1122. (*w0 == COLON && w0[1] != delim)
  1123. // || !_istalnum(*w0)
  1124. )
  1125. break;
  1126. }
  1127. else
  1128. if (*w0 == delim)
  1129. break ;
  1130. DEBUG((PAGRP, LFLVL, "MSENVVAR: *w0 = %04x", *w0)) ;
  1131. /* M026 - Now check for two together and terminate here to avoid an
  1132. * environment search operation
  1133. */
  1134. if (!*w0 || (w0 - str) == 0) /* If none or two together "%%" */
  1135. return(NULL) ; /* Say, "Not found" */
  1136. savec = *w0;
  1137. *w0 = NULLC ; /* Null term any Env Var name */
  1138. *supdate = mystrlen(str) ; /* M026 - supdate = source len */
  1139. if (savec == delim)
  1140. *supdate += 1 ; /* Kick it past ending delim */
  1141. DEBUG((PAGRP, LFLVL, "MSENVVAR: Possible env var = `%ws'", str)) ;
  1142. w1 = GetEnvVar(str) ; /* w1 == NULL or env variable */
  1143. if (fEnableExtensions && w1 == NULL) {
  1144. if (!_tcsicmp(str, ErrStr))
  1145. wsprintf( w1 = LastRetCodeStr, TEXT("%d"), LastRetCode );
  1146. else
  1147. if (!_tcsicmp(str, TEXT("CMDCMDLINE")))
  1148. w1 = GetCommandLine();
  1149. }
  1150. *w0 = savec ; /* Restore str and... */
  1151. if (savec == delim || savec == COLON)
  1152. w0 += 1;
  1153. //
  1154. // If Command Extensions are enabled, then we support munging the
  1155. // output of environment variable expansion. Here is the supported
  1156. // syntax, all keyed off a trailing COLON character at the end of
  1157. // the environment variable name. Note, that %FOO:% is treated
  1158. // as it was before.
  1159. //
  1160. // Environment variable substitution has been enhanced as follows:
  1161. //
  1162. // %PATH:str1=str2%
  1163. //
  1164. // would expand the PATH environment variable, substituting each
  1165. // occurrence of "str1" in the expanded result with "str2". "str2" can
  1166. // be the empty string to effectively delete all occurrences of "str1"
  1167. // from the expanded output. Additionally:
  1168. //
  1169. // %PATH:~10,5%
  1170. //
  1171. // would expand the PATH environment variable, and then use only the 5
  1172. // characters that begin at the 11th character of the expanded result.
  1173. // If the ,5 is left off, it will take the entire remainder of the
  1174. // expanded result.
  1175. //
  1176. if (fEnableExtensions && savec == COLON && w1 != NULL) {
  1177. wSrch = w0;
  1178. if (*w0 == EQI) {
  1179. //
  1180. // %PATH:~10,5%
  1181. w0 += 1;
  1182. noff = _tcstol(w0, &w0, 0);
  1183. if (*w0 == COMMA) {
  1184. w0 += 1;
  1185. nlen = _tcstol(w0, &w0, 0);
  1186. if (nlen>0) {
  1187. //
  1188. // If length specified, use it
  1189. //
  1190. _tcsncpy(w1, w1+noff, nlen);
  1191. w1[nlen] = NULLC;
  1192. }
  1193. } else
  1194. //
  1195. // Otherwise just copy from offset to end of string.
  1196. //
  1197. _tcscpy(w1, w1+noff);
  1198. if (*w0 == delim)
  1199. w0 += 1;
  1200. } else {
  1201. //
  1202. // Not extracting a string, so must be search and replacing
  1203. //
  1204. // %PATH:str1=str2%
  1205. //
  1206. //
  1207. while (*w0 && *w0 != EQ) {
  1208. if (*w0 == delim)
  1209. break;
  1210. w0 += 1;
  1211. }
  1212. if (*w0 == EQ) {
  1213. *w0++ = NULLC;
  1214. nsrchlen = _tcslen(wSrch);
  1215. wRepl = w0;
  1216. while (*w0 && *w0 != delim)
  1217. w0 += 1;
  1218. if (*w0) {
  1219. *w0 = NULLC;
  1220. nrepllen = _tcslen(wRepl);
  1221. w2 = w1;
  1222. if (*wSrch == STAR) {
  1223. fPrefixMatch = TRUE;
  1224. wSrch += 1;
  1225. nsrchlen -= 1;
  1226. } else
  1227. fPrefixMatch = FALSE;
  1228. while (*w2) {
  1229. if (!_tcsnicmp(w2, wSrch, nsrchlen)) {
  1230. if (fPrefixMatch) {
  1231. nsrchlen = (w2 - w1) + nsrchlen;
  1232. w2 = w1;
  1233. }
  1234. ndeltalen = nsrchlen-nrepllen;
  1235. if (ndeltalen < 0)
  1236. memmove(w2-ndeltalen, w2, (_tcslen(w2)+1)*sizeof(TCHAR));
  1237. else
  1238. if (ndeltalen > 0)
  1239. _tcscpy(w2, w2+ndeltalen);
  1240. memmove(w2, wRepl, nrepllen*sizeof(TCHAR));
  1241. if (fPrefixMatch) {
  1242. wSrch -= 1;
  1243. break;
  1244. }
  1245. w2 += nrepllen;
  1246. } else {
  1247. w2 += 1;
  1248. }
  1249. }
  1250. *w0++ = delim;
  1251. }
  1252. *--wRepl = EQ;
  1253. }
  1254. else
  1255. if (*w0) {
  1256. if (errjmp != NULL) {
  1257. PutStdErr(MSG_SYNERR_GENL, ONEARG, wSrch);
  1258. longjmp(*errjmp,-1) ;
  1259. } else
  1260. return NULL;
  1261. }
  1262. }
  1263. *supdate += w0 - wSrch + 1;
  1264. }
  1265. return(w1) ; /* ...return what was found */
  1266. }
  1267. /*** MSCmdVar - Does command variable substitution
  1268. *
  1269. * Purpose:
  1270. * When percent signs are found in the newly filled lexer buffer,
  1271. * this function is called to determine if there is a command processor
  1272. * variable substitution possible.
  1273. *
  1274. * TCHAR *MSCmdVar(TCHAR *srcp, int *supdate, TCHAR *vars, TCHAR *subs[])
  1275. *
  1276. * Args:
  1277. * errjmp - optional pointer to jmp_buf for errors
  1278. * srcp - pointer to a possible variable name
  1279. * supdate - location to place variable name length in
  1280. * vars - array of character variable names to look for
  1281. * subs - array of substitution strings for each variable name.
  1282. *
  1283. * Returns:
  1284. * If there is no ending delimiter
  1285. * set supdate to 0
  1286. * return NULL
  1287. * else
  1288. * set supdate to the enclosed string length
  1289. * if the string is not a variable
  1290. * return NULL
  1291. * else
  1292. * return a pointer to the replacement string
  1293. */
  1294. TCHAR *MSCmdVar(errjmp, srcp, supdate, vars, subs)
  1295. jmp_buf *errjmp ;
  1296. TCHAR *srcp ;
  1297. int *supdate ;
  1298. TCHAR *vars ;
  1299. TCHAR *subs[] ;
  1300. {
  1301. TCHAR *substr;
  1302. TCHAR *s1;
  1303. int j;
  1304. substr = NULL;
  1305. *supdate = 0;
  1306. //
  1307. // If extensions are enabled, we support the following syntax for expanding
  1308. // variable values:
  1309. // %~fi - expands %i to a fully qualified path name
  1310. // %~di - expands %i to a drive letter only
  1311. // %~pi - expands %i to a path only
  1312. // %~ni - expands %i to a file name only
  1313. // %~xi - expands %i to a file extension only
  1314. // %~si - changes the meaning of n and x options to
  1315. // reference the short name instead
  1316. // %~$PATH:i - searches the directories listed in the PATH
  1317. // environment variable and expands %i to the
  1318. // fully qualified name of the first one found.
  1319. // If the environment variable name is not
  1320. // defined or the file is not found by the
  1321. // search, then this modifier expands to the
  1322. // empty string
  1323. //
  1324. // The modifiers can be combined to get compound results:
  1325. //
  1326. // %~dpi - expands %i to a drive letter and path only
  1327. // %~nxi - expands %i to a file name and extension only
  1328. // %~dp$PATH:i - searches the directories listed in the PATH
  1329. // environment variable for %i and expands to the
  1330. // drive letter and path of the first one found.
  1331. //
  1332. //
  1333. // See if new syntax is being specified
  1334. //
  1335. if (fEnableExtensions && *srcp == EQI) {
  1336. BOOL fWantFullPath = FALSE;
  1337. BOOL fWantDrive = FALSE;
  1338. BOOL fWantPath = FALSE;
  1339. BOOL fWantName = FALSE;
  1340. BOOL fWantExtension = FALSE;
  1341. BOOL fWantShortName = FALSE;
  1342. WIN32_FIND_DATA FindBuf;
  1343. HANDLE FindHandle;
  1344. TCHAR c;
  1345. TCHAR ArgStr[MAX_PATH];
  1346. TCHAR FullPath[MAX_PATH], NullExt;
  1347. TCHAR *FilePart, *Extension, *StartPath, *VarName, *SearchVar, *StartBuf;
  1348. DWORD FullPathLength;
  1349. //
  1350. // New syntax. Scan the modifiers after the ~ until we see
  1351. // a single letter variable name that matches one the passed in
  1352. // array of variable letters.
  1353. //
  1354. FullPathLength = 0;
  1355. SearchVar = NULL;
  1356. StartBuf = srcp-1;
  1357. s1 = NULL;
  1358. while (!s1 && *++srcp) {
  1359. switch (_totlower(*srcp)) {
  1360. case TEXT('f'): fWantFullPath = TRUE; break;
  1361. case TEXT('d'): fWantDrive = TRUE; break;
  1362. case TEXT('p'): fWantPath = TRUE; break;
  1363. case TEXT('n'): fWantName = TRUE; break;
  1364. case TEXT('x'): fWantExtension = TRUE; break;
  1365. case TEXT('s'): fWantShortName = TRUE; break;
  1366. case TEXT('$'): VarName = ++srcp;
  1367. while (*srcp && *srcp != COLON)
  1368. srcp++;
  1369. if (!*srcp) {
  1370. if (errjmp != NULL) {
  1371. PutStdErr(MSG_PATH_OPERATOR_INVALID, ONEARG, StartBuf) ;
  1372. longjmp(*errjmp,-1) ;
  1373. } else
  1374. return NULL;
  1375. }
  1376. *srcp = NULLC;
  1377. SearchVar = MyGetEnvVarPtr(VarName);
  1378. if (SearchVar == NULL) {
  1379. SearchVar = (TCHAR *)-1;
  1380. }
  1381. *srcp = COLON;
  1382. break;
  1383. default:
  1384. if ((s1 = _tcsrchr(vars, *srcp)) == NULL) {
  1385. if (errjmp != NULL) {
  1386. PutStdErr(MSG_PATH_OPERATOR_INVALID, ONEARG, StartBuf) ;
  1387. longjmp(*errjmp,-1) ;
  1388. } else
  1389. return NULL;
  1390. }
  1391. }
  1392. }
  1393. //
  1394. // If we get here without a variable name, then bogus input.
  1395. // Bail with an error message.
  1396. //
  1397. if (s1 == NULL) {
  1398. if (errjmp != NULL) {
  1399. PutStdErr(MSG_PATH_OPERATOR_INVALID, ONEARG, StartBuf) ;
  1400. longjmp(*errjmp,-1) ;
  1401. } else
  1402. return NULL;
  1403. }
  1404. //
  1405. // Get current value of variable
  1406. //
  1407. substr = subs[s1 - vars];
  1408. if (substr != NULL && *substr == QUOTE) {
  1409. _tcscpy(ArgStr, substr+1);
  1410. s1 = lastc(ArgStr);
  1411. if (*s1 == QUOTE)
  1412. *s1 = NULLC;
  1413. substr = ArgStr;
  1414. }
  1415. else
  1416. if (substr != NULL &&
  1417. *srcp == TEXT('0') &&
  1418. CurBat != NULL &&
  1419. CurBat->orgaptr0 == substr &&
  1420. SearchVar == NULL &&
  1421. (fWantFullPath || fWantDrive || fWantPath || fWantName ||
  1422. fWantExtension || fWantShortName)
  1423. ) {
  1424. substr = CurBat->filespec;
  1425. }
  1426. // Skip past the variable name letter and tell caller how much of the
  1427. // source string we consumed.
  1428. //
  1429. ++srcp ;
  1430. *supdate = (srcp - StartBuf) - 1;
  1431. //
  1432. // If the variable has a value, then apply the modifiers to the
  1433. // value.
  1434. //
  1435. if (substr && *substr) {
  1436. //
  1437. // If requested searching an environment variable path, do that.
  1438. //
  1439. FullPath[0] = NULLC;
  1440. if (SearchVar != NULL) {
  1441. if (SearchVar != (TCHAR *)-1) {
  1442. FullPathLength = SearchPath(SearchVar,substr,NULL,MAX_PATH,FullPath,&FilePart);
  1443. if (FullPathLength == 0)
  1444. SearchVar = (TCHAR *)-1;
  1445. else
  1446. if (!fWantFullPath && !fWantDrive &&
  1447. !fWantPath && !fWantName && !fWantExtension)
  1448. fWantFullPath = TRUE;
  1449. }
  1450. }
  1451. if (SearchVar == NULL) {
  1452. //
  1453. // If not searching environment variable path, start with full path.
  1454. FullPathLength = GetFullPathName(substr,MAX_PATH,FullPath,&FilePart);
  1455. if (FilePart == NULL)
  1456. FilePart = _tcschr( FullPath, NULLC );
  1457. }
  1458. else
  1459. if (SearchVar == (TCHAR *)-1) {
  1460. //
  1461. // If search of environment variable path failed, result is empty string
  1462. //
  1463. substr = NULL;
  1464. }
  1465. //
  1466. // Fixup the path to have same case as on disk, substituting short
  1467. // names if requested.
  1468. //
  1469. FixupPath(FullPath, fWantShortName);
  1470. //
  1471. // If we have a full path, the result gets the portions requested by
  1472. // the user, unless they wanted the full path, in which case there is
  1473. // nothing more to do.
  1474. //
  1475. if (FullPathLength != 0) {
  1476. if (!fWantFullPath) {
  1477. StartPath = FullPath + 2;
  1478. if (!fWantDrive) {
  1479. StartPath = _tcscpy(FullPath, StartPath);
  1480. FilePart -= 2;
  1481. }
  1482. if (!fWantPath)
  1483. FilePart = _tcscpy(StartPath, FilePart);
  1484. Extension = _tcsrchr(FilePart, DOT);
  1485. if (Extension == NULL) {
  1486. NullExt = NULLC;
  1487. Extension = &NullExt;
  1488. }
  1489. if (!fWantExtension)
  1490. *Extension = NULLC;
  1491. if (!fWantName)
  1492. _tcscpy(FilePart, Extension);
  1493. }
  1494. substr = FullPath;
  1495. }
  1496. }
  1497. }
  1498. else
  1499. if (*srcp && (s1 = _tcsrchr(vars, *srcp))) {
  1500. //
  1501. // Old syntax. Result is value of variable
  1502. //
  1503. substr = subs[s1 - vars]; /* Found variable*/
  1504. //
  1505. // Skip past the variable name letter and tell caller how much of the
  1506. // source string we consumed.
  1507. //
  1508. ++srcp ; /* Kick past name*/
  1509. *supdate += 1;
  1510. } ;
  1511. //
  1512. // If result was empty, then return the null string. Otherwise return the result
  1513. //
  1514. if (!substr && *supdate != 0)
  1515. return TEXT("");
  1516. else
  1517. return substr;
  1518. }