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.

1782 lines
40 KiB

  1. //
  2. // Copyright (c) Microsoft Corporation 1995
  3. //
  4. // scanner.c
  5. //
  6. // This file contains the scanner functions.
  7. //
  8. // History:
  9. // 05-04-95 ScottH Created
  10. //
  11. #include "proj.h"
  12. #include "rcids.h"
  13. // This is a hack global string used by error messages.
  14. // This should be removed when Stxerr encapsulates the script
  15. // filename within itself.
  16. static char g_szScript[MAX_PATH];
  17. #define SCANNER_BUF_SIZE 1024
  18. #define IS_WHITESPACE(ch) (' ' == (ch) || '\t' == (ch) || '\n' == (ch) || '\r' == (ch))
  19. #define IS_QUOTE(ch) ('\"' == (ch))
  20. #define IS_KEYWORD_LEAD(ch) ('$' == (ch) || '_' == (ch) || IsCharAlpha(ch))
  21. #define IS_KEYWORD(ch) ('_' == (ch) || IsCharAlphaNumeric(ch))
  22. #define IS_COMMENT_LEAD(ch) (';' == (ch))
  23. #define IS_EOL(ch) ('\n' == (ch))
  24. typedef BOOL (CALLBACK * SCANEVALPROC)(char ch, LPBOOL pbEatIt, LPARAM);
  25. //
  26. // Lexical mapping
  27. //
  28. typedef struct tagLEX
  29. {
  30. LPSTR pszLexeme;
  31. SYM sym;
  32. } LEX;
  33. DECLARE_STANDARD_TYPES(LEX);
  34. #pragma data_seg(DATASEG_READONLY)
  35. // (The keywords are case-sensitive)
  36. //
  37. // This table is sorted alphabetically for binary search.
  38. //
  39. const LEX c_rglexKeywords[] = {
  40. { "FALSE", SYM_FALSE },
  41. { "TRUE", SYM_TRUE },
  42. { "and", SYM_AND },
  43. { "boolean", SYM_BOOLEAN },
  44. { "databits", SYM_DATABITS },
  45. { "delay", SYM_DELAY },
  46. { "do", SYM_DO },
  47. { "endif", SYM_ENDIF },
  48. { "endproc", SYM_ENDPROC },
  49. { "endwhile", SYM_ENDWHILE },
  50. { "even", SYM_EVEN },
  51. { "getip", SYM_GETIP },
  52. { "goto", SYM_GOTO },
  53. { "halt", SYM_HALT },
  54. { "if", SYM_IF },
  55. { "integer", SYM_INTEGER },
  56. { "ipaddr", SYM_IPADDR },
  57. { "keyboard", SYM_KEYBRD },
  58. { "mark", SYM_MARK },
  59. { "matchcase", SYM_MATCHCASE },
  60. { "none", SYM_NONE },
  61. { "odd", SYM_ODD },
  62. { "off", SYM_OFF },
  63. { "on", SYM_ON },
  64. { "or", SYM_OR },
  65. { "parity", SYM_PARITY },
  66. { "port", SYM_PORT },
  67. { "proc", SYM_PROC },
  68. { "raw", SYM_RAW },
  69. { "screen", SYM_SCREEN },
  70. { "set", SYM_SET },
  71. { "space", SYM_SPACE },
  72. { "stopbits", SYM_STOPBITS },
  73. { "string", SYM_STRING },
  74. { "then", SYM_THEN },
  75. { "transmit", SYM_TRANSMIT },
  76. { "until", SYM_UNTIL },
  77. { "waitfor", SYM_WAITFOR },
  78. { "while", SYM_WHILE },
  79. };
  80. #pragma data_seg()
  81. //
  82. // Tokens
  83. //
  84. #ifdef DEBUG
  85. #pragma data_seg(DATASEG_READONLY)
  86. struct tagSYMMAP
  87. {
  88. SYM sym;
  89. LPCSTR psz;
  90. } const c_rgsymmap[] = {
  91. DEBUG_STRING_MAP(SYM_EOF),
  92. DEBUG_STRING_MAP(SYM_IDENT),
  93. DEBUG_STRING_MAP(SYM_STRING_LITERAL),
  94. DEBUG_STRING_MAP(SYM_STRING),
  95. DEBUG_STRING_MAP(SYM_INTEGER),
  96. DEBUG_STRING_MAP(SYM_BOOLEAN),
  97. DEBUG_STRING_MAP(SYM_WAITFOR),
  98. DEBUG_STRING_MAP(SYM_WHILE),
  99. DEBUG_STRING_MAP(SYM_TRANSMIT),
  100. DEBUG_STRING_MAP(SYM_DELAY),
  101. DEBUG_STRING_MAP(SYM_THEN),
  102. DEBUG_STRING_MAP(SYM_INT_LITERAL),
  103. DEBUG_STRING_MAP(SYM_GETIP),
  104. DEBUG_STRING_MAP(SYM_IPADDR),
  105. DEBUG_STRING_MAP(SYM_ASSIGN),
  106. DEBUG_STRING_MAP(SYM_PROC),
  107. DEBUG_STRING_MAP(SYM_ENDPROC),
  108. DEBUG_STRING_MAP(SYM_HALT),
  109. DEBUG_STRING_MAP(SYM_IF),
  110. DEBUG_STRING_MAP(SYM_ENDIF),
  111. DEBUG_STRING_MAP(SYM_DO),
  112. DEBUG_STRING_MAP(SYM_RAW),
  113. DEBUG_STRING_MAP(SYM_MATCHCASE),
  114. DEBUG_STRING_MAP(SYM_SET),
  115. DEBUG_STRING_MAP(SYM_PORT),
  116. DEBUG_STRING_MAP(SYM_DATABITS),
  117. DEBUG_STRING_MAP(SYM_STOPBITS),
  118. DEBUG_STRING_MAP(SYM_PARITY),
  119. DEBUG_STRING_MAP(SYM_NONE),
  120. DEBUG_STRING_MAP(SYM_EVEN),
  121. DEBUG_STRING_MAP(SYM_MARK),
  122. DEBUG_STRING_MAP(SYM_SPACE),
  123. DEBUG_STRING_MAP(SYM_SCREEN),
  124. DEBUG_STRING_MAP(SYM_ON),
  125. DEBUG_STRING_MAP(SYM_OFF),
  126. DEBUG_STRING_MAP(SYM_NOT),
  127. DEBUG_STRING_MAP(SYM_OR),
  128. DEBUG_STRING_MAP(SYM_AND),
  129. DEBUG_STRING_MAP(SYM_LEQ),
  130. DEBUG_STRING_MAP(SYM_NEQ),
  131. DEBUG_STRING_MAP(SYM_LT),
  132. DEBUG_STRING_MAP(SYM_GT),
  133. DEBUG_STRING_MAP(SYM_GEQ),
  134. DEBUG_STRING_MAP(SYM_EQ),
  135. DEBUG_STRING_MAP(SYM_PLUS),
  136. DEBUG_STRING_MAP(SYM_MINUS),
  137. DEBUG_STRING_MAP(SYM_MULT),
  138. DEBUG_STRING_MAP(SYM_DIV),
  139. DEBUG_STRING_MAP(SYM_LPAREN),
  140. DEBUG_STRING_MAP(SYM_RPAREN),
  141. DEBUG_STRING_MAP(SYM_TRUE),
  142. DEBUG_STRING_MAP(SYM_FALSE),
  143. DEBUG_STRING_MAP(SYM_COLON),
  144. DEBUG_STRING_MAP(SYM_GOTO),
  145. DEBUG_STRING_MAP(SYM_COMMA),
  146. DEBUG_STRING_MAP(SYM_UNTIL),
  147. };
  148. #pragma data_seg()
  149. /*----------------------------------------------------------
  150. Purpose: Returns the string form of a RES value.
  151. Returns: String ptr
  152. Cond: --
  153. */
  154. LPCSTR PRIVATE Dbg_GetSym(
  155. SYM sym)
  156. {
  157. int i;
  158. for (i = 0; i < ARRAY_ELEMENTS(c_rgsymmap); i++)
  159. {
  160. if (c_rgsymmap[i].sym == sym)
  161. return c_rgsymmap[i].psz;
  162. }
  163. return "Unknown SYM";
  164. }
  165. /*----------------------------------------------------------
  166. Purpose: Dump the token
  167. Returns: --
  168. Cond: --
  169. */
  170. void PRIVATE Tok_Dump(
  171. PTOK this)
  172. {
  173. ASSERT(this);
  174. if (IsFlagSet(g_dwDumpFlags, DF_TOKEN))
  175. {
  176. switch (this->toktype)
  177. {
  178. case TT_BASE:
  179. TRACE_MSG(TF_ALWAYS, "line %ld: %s, '%s'", Tok_GetLine(this),
  180. Dbg_GetSym(Tok_GetSym(this)), Tok_GetLexeme(this));
  181. break;
  182. case TT_SZ: {
  183. PTOKSZ ptoksz = (PTOKSZ)this;
  184. TRACE_MSG(TF_ALWAYS, "line %ld: %s, {%s}", Tok_GetLine(this),
  185. Dbg_GetSym(Tok_GetSym(this)), TokSz_GetSz(ptoksz));
  186. }
  187. break;
  188. case TT_INT: {
  189. PTOKINT ptokint = (PTOKINT)this;
  190. TRACE_MSG(TF_ALWAYS, "line %ld: %s, {%d}", Tok_GetLine(this),
  191. Dbg_GetSym(Tok_GetSym(this)), TokInt_GetVal(ptokint));
  192. }
  193. break;
  194. default:
  195. ASSERT(0);
  196. break;
  197. }
  198. }
  199. }
  200. #else // DEBUG
  201. #define Dbg_GetSym(sym) ((LPSTR)"")
  202. #define Tok_Dump(ptok)
  203. #endif // DEBUG
  204. /*----------------------------------------------------------
  205. Purpose: Creates a new token with the given symbol sym.
  206. Returns: RES_OK
  207. RES_E_OUTOFMEMORY
  208. Cond: --
  209. */
  210. RES PUBLIC Tok_New(
  211. PTOK * pptok,
  212. SYM sym,
  213. LPCSTR pszLexeme,
  214. DWORD iLine)
  215. {
  216. PTOK ptok;
  217. ASSERT(pptok);
  218. ASSERT(pszLexeme);
  219. ptok = GAllocType(TOK);
  220. if (ptok)
  221. {
  222. Tok_SetSize(ptok, sizeof(*ptok));
  223. Tok_SetSym(ptok, sym);
  224. Tok_SetType(ptok, TT_BASE);
  225. Tok_SetLine(ptok, iLine);
  226. Tok_SetLexeme(ptok, pszLexeme);
  227. }
  228. *pptok = ptok;
  229. return NULL != ptok ? RES_OK : RES_E_OUTOFMEMORY;
  230. }
  231. /*----------------------------------------------------------
  232. Purpose: Destroys the given token.
  233. Returns:
  234. Cond: --
  235. */
  236. void PUBLIC Tok_Delete(
  237. PTOK this)
  238. {
  239. GFree(this);
  240. }
  241. /*----------------------------------------------------------
  242. Purpose: Duplicate the given token.
  243. Returns: RES_OK
  244. RES_E_OUTOFMEMORY
  245. Cond: --
  246. */
  247. RES PUBLIC Tok_Dup(
  248. PTOK this,
  249. PTOK * pptok)
  250. {
  251. PTOK ptok;
  252. DWORD cbSize;
  253. ASSERT(this);
  254. ASSERT(pptok);
  255. cbSize = Tok_GetSize(this);
  256. ptok = GAlloc(cbSize);
  257. if (ptok)
  258. {
  259. BltByte(ptok, this, cbSize);
  260. }
  261. *pptok = ptok;
  262. return NULL != ptok ? RES_OK : RES_E_OUTOFMEMORY;
  263. }
  264. /*----------------------------------------------------------
  265. Purpose: Creates a new string token with the given string.
  266. Returns: RES_OK
  267. RES_E_OUTOFMEMORY
  268. Cond: --
  269. */
  270. RES PUBLIC TokSz_New(
  271. PTOK * pptok,
  272. SYM sym,
  273. LPCSTR pszLexeme,
  274. DWORD iLine,
  275. LPCSTR psz)
  276. {
  277. PTOKSZ ptoksz;
  278. ASSERT(pptok);
  279. ptoksz = GAllocType(TOKSZ);
  280. if (ptoksz)
  281. {
  282. Tok_SetSize(ptoksz, sizeof(*ptoksz));
  283. Tok_SetSym(ptoksz, sym);
  284. Tok_SetType(ptoksz, TT_SZ);
  285. Tok_SetLine(ptoksz, iLine);
  286. Tok_SetLexeme(ptoksz, pszLexeme);
  287. if (psz)
  288. TokSz_SetSz(ptoksz, psz);
  289. else
  290. *ptoksz->sz = 0;
  291. }
  292. *pptok = (PTOK)ptoksz;
  293. return NULL != ptoksz ? RES_OK : RES_E_OUTOFMEMORY;
  294. }
  295. /*----------------------------------------------------------
  296. Purpose: Creates a new integer token with the given value.
  297. Returns: RES_OK
  298. RES_E_OUTOFMEMORY
  299. Cond: --
  300. */
  301. RES PUBLIC TokInt_New(
  302. PTOK * pptok,
  303. SYM sym,
  304. LPCSTR pszLexeme,
  305. DWORD iLine,
  306. int n)
  307. {
  308. PTOKINT ptokint;
  309. ASSERT(pptok);
  310. ptokint = GAllocType(TOKINT);
  311. if (ptokint)
  312. {
  313. Tok_SetSize(ptokint, sizeof(*ptokint));
  314. Tok_SetSym(ptokint, sym);
  315. Tok_SetType(ptokint, TT_INT);
  316. Tok_SetLine(ptokint, iLine);
  317. Tok_SetLexeme(ptokint, pszLexeme);
  318. TokInt_SetVal(ptokint, n);
  319. }
  320. *pptok = (PTOK)ptokint;
  321. return NULL != ptokint ? RES_OK : RES_E_OUTOFMEMORY;
  322. }
  323. /*----------------------------------------------------------
  324. Purpose: Compare two strings. This function does not take
  325. localization into account, so the comparison of two
  326. strings will be based off the English code page.
  327. This is required because the lexical keyword table
  328. is hand-sorted to the English language. Using the
  329. NLS lstrcmp would not produce the correct results.
  330. Returns: strcmp standard
  331. Cond: --
  332. */
  333. int PRIVATE strcmpraw(
  334. LPCSTR psz1,
  335. LPCSTR psz2)
  336. {
  337. for (; *psz1 == *psz2;
  338. psz1 = CharNext(psz1),
  339. psz2 = CharNext(psz2))
  340. {
  341. if (0 == *psz1)
  342. return 0;
  343. }
  344. return *psz1 - *psz2;
  345. }
  346. #ifdef DEBUG
  347. /*----------------------------------------------------------
  348. Purpose: Returns the SYM value that matches the given lexeme.
  349. If the given lexeme is not found in the list of
  350. keyword token values, then SYM_IDENT is returned.
  351. Performs a linear search.
  352. Returns: see above
  353. Cond: --
  354. */
  355. SYM PRIVATE SymFromKeywordLinear(
  356. LPCSTR pszLex)
  357. {
  358. int i;
  359. ASSERT(pszLex);
  360. for (i = 0; i < ARRAY_ELEMENTS(c_rglexKeywords); i++)
  361. {
  362. // Case-sensitive
  363. if (0 == strcmpraw(c_rglexKeywords[i].pszLexeme, pszLex))
  364. {
  365. return c_rglexKeywords[i].sym;
  366. }
  367. }
  368. return SYM_IDENT;
  369. }
  370. #endif
  371. /*----------------------------------------------------------
  372. Purpose: Returns the SYM value that matches the given lexeme.
  373. If the given lexeme is not found in the list of
  374. keyword token values, then SYM_IDENT is returned.
  375. Peforms a binary search.
  376. Returns: see above
  377. Cond: --
  378. */
  379. SYM PRIVATE SymFromKeyword(
  380. LPCSTR pszLex)
  381. {
  382. static const s_cel = ARRAY_ELEMENTS(c_rglexKeywords);
  383. SYM symRet = SYM_IDENT; // assume no match
  384. int nCmp;
  385. int iLow = 0;
  386. int iMid;
  387. int iHigh = s_cel - 1;
  388. ASSERT(pszLex);
  389. // (OK for cp == 0. Duplicate lexemes not allowed.)
  390. while (iLow <= iHigh)
  391. {
  392. iMid = (iLow + iHigh) / 2;
  393. nCmp = strcmpraw(pszLex, c_rglexKeywords[iMid].pszLexeme);
  394. if (0 > nCmp)
  395. iHigh = iMid - 1; // First is smaller
  396. else if (0 < nCmp)
  397. iLow = iMid + 1; // First is larger
  398. else
  399. {
  400. // Match
  401. symRet = c_rglexKeywords[iMid].sym;
  402. break;
  403. }
  404. }
  405. // Check if we get the same result with linear search
  406. ASSERT(SymFromKeywordLinear(pszLex) == symRet);
  407. return symRet;
  408. }
  409. //
  410. // Stxerr
  411. //
  412. /*----------------------------------------------------------
  413. Purpose: Initializes a syntax error structure
  414. Returns: --
  415. Cond: --
  416. */
  417. void PUBLIC Stxerr_Init(
  418. PSTXERR this,
  419. LPCSTR pszLex,
  420. DWORD iLine,
  421. RES res)
  422. {
  423. ASSERT(this);
  424. ASSERT(pszLex);
  425. lstrcpyn(this->szLexeme, pszLex, sizeof(this->szLexeme));
  426. this->iLine = iLine;
  427. this->res = res;
  428. }
  429. //
  430. // Scanner
  431. //
  432. /*----------------------------------------------------------
  433. Purpose: Returns TRUE if the scanner structure is valid
  434. to read a file.
  435. Returns: See above
  436. Cond: --
  437. */
  438. BOOL PRIVATE Scanner_Validate(
  439. PSCANNER this)
  440. {
  441. return (this &&
  442. (IsFlagSet(this->dwFlags, SCF_NOSCRIPT) ||
  443. INVALID_HANDLE_VALUE != this->hfile) &&
  444. this->pbBuffer &&
  445. this->psci);
  446. }
  447. /*----------------------------------------------------------
  448. Purpose: Creates a scanner.
  449. Returns: RES_OK
  450. RES_E_OUTOFMEMORY
  451. RES_E_INVALIDPARAM
  452. Cond: --
  453. */
  454. RES PUBLIC Scanner_Create(
  455. PSCANNER * ppscanner,
  456. PSESS_CONFIGURATION_INFO psci)
  457. {
  458. RES res;
  459. DBG_ENTER(Scanner_Create);
  460. ASSERT(ppscanner);
  461. ASSERT(psci);
  462. if (ppscanner)
  463. {
  464. PSCANNER pscanner;
  465. res = RES_OK; // assume success
  466. pscanner = GAllocType(SCANNER);
  467. if (!pscanner)
  468. res = RES_E_OUTOFMEMORY;
  469. else
  470. {
  471. pscanner->pbBuffer = GAlloc(SCANNER_BUF_SIZE);
  472. if (!pscanner->pbBuffer)
  473. res = RES_E_OUTOFMEMORY;
  474. else
  475. {
  476. if ( !SACreate(&pscanner->hsaStxerr, sizeof(STXERR), 8) )
  477. res = RES_E_OUTOFMEMORY;
  478. else
  479. {
  480. pscanner->hfile = INVALID_HANDLE_VALUE;
  481. pscanner->psci = psci;
  482. SetFlag(pscanner->dwFlags, SCF_NOSCRIPT);
  483. }
  484. }
  485. }
  486. if (RFAILED(res))
  487. {
  488. Scanner_Destroy(pscanner);
  489. pscanner = NULL;
  490. }
  491. *ppscanner = pscanner;
  492. }
  493. else
  494. res = RES_E_INVALIDPARAM;
  495. DBG_EXIT_RES(Scanner_Create, res);
  496. return res;
  497. }
  498. /*----------------------------------------------------------
  499. Purpose: Destroys a scanner.
  500. Returns: RES_OK
  501. RES_E_INVALIDPARAM
  502. Cond: --
  503. */
  504. RES PUBLIC Scanner_Destroy(
  505. PSCANNER this)
  506. {
  507. RES res;
  508. DBG_ENTER(Scanner_Destroy);
  509. if (this)
  510. {
  511. if (INVALID_HANDLE_VALUE != this->hfile)
  512. {
  513. TRACE_MSG(TF_GENERAL, "Closing script");
  514. CloseHandle(this->hfile);
  515. }
  516. if (this->pbBuffer)
  517. {
  518. GFree(this->pbBuffer);
  519. }
  520. if (this->hsaStxerr)
  521. {
  522. SADestroy(this->hsaStxerr);
  523. }
  524. GFree(this);
  525. res = RES_OK;
  526. }
  527. else
  528. res = RES_E_INVALIDPARAM;
  529. DBG_EXIT_RES(Scanner_Destroy, res);
  530. return res;
  531. }
  532. /*----------------------------------------------------------
  533. Purpose: Opens a script file and associates it with this scanner.
  534. Returns: RES_OK
  535. RES_E_FAIL (script cannot be opened)
  536. RES_E_INVALIDPARAM
  537. Cond: --
  538. */
  539. RES PUBLIC Scanner_OpenScript(
  540. PSCANNER this,
  541. LPCSTR pszPath)
  542. {
  543. RES res;
  544. DBG_ENTER_SZ(Scanner_OpenScript, pszPath);
  545. if (this && pszPath)
  546. {
  547. DEBUG_BREAK(BF_ONOPEN);
  548. // (shouldn't have a file open already)
  549. ASSERT(INVALID_HANDLE_VALUE == this->hfile);
  550. // Open script
  551. this->hfile = CreateFile(pszPath, GENERIC_READ, FILE_SHARE_READ,
  552. NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
  553. if (INVALID_HANDLE_VALUE == this->hfile)
  554. {
  555. TRACE_MSG(TF_GENERAL, "Failed to open script \"%s\"", pszPath);
  556. res = RES_E_FAIL;
  557. }
  558. //
  559. // .Net bug# 522307 Specifying the dialup script file as the COM
  560. // Port of the Modem will cause explorer to AV.
  561. //
  562. else if (GetFileType(this->hfile) != FILE_TYPE_DISK)
  563. {
  564. res = RES_E_FAIL;
  565. }
  566. else
  567. {
  568. // Reset buffer fields
  569. TRACE_MSG(TF_GENERAL, "Opened script \"%s\"", pszPath);
  570. lstrcpyn(this->szScript, pszPath, sizeof(this->szScript));
  571. lstrcpyn(g_szScript, pszPath, sizeof(g_szScript));
  572. ClearFlag(this->dwFlags, SCF_NOSCRIPT);
  573. this->pbCur = this->pbBuffer;
  574. this->cbUnread = 0;
  575. this->chUnget = 0;
  576. this->chTailByte = 0;
  577. this->iLine = 1;
  578. res = RES_OK;
  579. }
  580. }
  581. else
  582. res = RES_E_INVALIDPARAM;
  583. DBG_EXIT_RES(Scanner_OpenScript, res);
  584. return res;
  585. }
  586. /*----------------------------------------------------------
  587. Purpose: Reads enough bytes from the file to fill the buffer.
  588. Returns: RES_OK
  589. RES_E_FAIL (if ReadFile failed)
  590. RES_E_EOF
  591. Cond: --
  592. */
  593. RES PRIVATE Scanner_Read(
  594. PSCANNER this)
  595. {
  596. RES res;
  597. BOOL bResult;
  598. LPBYTE pb;
  599. DWORD cb;
  600. DWORD cbUnread;
  601. DBG_ENTER(Scanner_Read);
  602. ASSERT(Scanner_Validate(this));
  603. // Move the unread bytes to the front of the buffer before reading
  604. // more bytes. This function may get called when there are still
  605. // some unread bytes in the buffer. We do not want to lose those
  606. // bytes.
  607. // I'm too lazy to make this a circular buffer.
  608. BltByte(this->pbBuffer, this->pbCur, this->cbUnread);
  609. this->pbCur = this->pbBuffer;
  610. pb = this->pbBuffer + this->cbUnread;
  611. cb = (DWORD)(SCANNER_BUF_SIZE - (pb - this->pbBuffer));
  612. bResult = ReadFile(this->hfile, pb, cb, &cbUnread, NULL);
  613. if (!bResult)
  614. {
  615. res = RES_E_FAIL;
  616. }
  617. else
  618. {
  619. // End of file?
  620. if (0 == cbUnread)
  621. {
  622. // Yes
  623. res = RES_E_EOF;
  624. }
  625. else
  626. {
  627. // No
  628. this->cbUnread += cbUnread;
  629. res = RES_OK;
  630. }
  631. }
  632. DBG_EXIT_RES(Scanner_Read, res);
  633. return res;
  634. }
  635. /*----------------------------------------------------------
  636. Purpose: Gets the next character in the file (buffer). This
  637. function will scan the file buffer using CharNext,
  638. and store the current byte in chCur.
  639. Note for DBCS characters this means that only the
  640. lead byte will be stored in chCur. If chCur is a
  641. lead byte, the trailing byte will be stored in
  642. chTailByte.
  643. Returns: RES_OK
  644. RES_E_EOF
  645. Cond: --
  646. */
  647. RES PRIVATE Scanner_GetChar(
  648. PSCANNER this)
  649. {
  650. RES res = RES_OK; // assume success
  651. ASSERT(Scanner_Validate(this));
  652. if (0 != this->chUnget)
  653. {
  654. this->chCur = this->chUnget;
  655. this->chUnget = 0;
  656. }
  657. else
  658. {
  659. // Time to read more into the buffer?
  660. if (0 == this->cbUnread)
  661. {
  662. // Yes
  663. res = Scanner_Read(this);
  664. }
  665. if (RSUCCEEDED(res))
  666. {
  667. LPBYTE pbCur = this->pbCur;
  668. LPBYTE pbNext = CharNext(pbCur);
  669. DWORD cb;
  670. BOOL bIsLeadByte;
  671. this->chCur = *pbCur;
  672. bIsLeadByte = IsDBCSLeadByte(this->chCur);
  673. // We might be at the end of the unread characters, where
  674. // a DBCS character is cut in half (ie, the trailing byte
  675. // is missing). Are we in this case?
  676. if (bIsLeadByte && 1 == this->cbUnread)
  677. {
  678. // Yes; read more into the buffer, we don't care about
  679. // the return value
  680. Scanner_Read(this);
  681. // this->pbCur might have changed
  682. pbCur = this->pbCur;
  683. pbNext = CharNext(pbCur);
  684. }
  685. cb = (DWORD)(pbNext - pbCur);
  686. this->cbUnread -= cb;
  687. this->pbCur = pbNext;
  688. // Do we need to save away the whole DBCS character?
  689. if (bIsLeadByte)
  690. {
  691. // Yes
  692. ASSERT(2 == cb); // We don't support MBCS
  693. this->chTailByte = pbCur[1];
  694. }
  695. if (IS_EOL(this->chCur))
  696. {
  697. this->iLine++;
  698. };
  699. }
  700. else
  701. this->chCur = 0;
  702. }
  703. return res;
  704. }
  705. /*----------------------------------------------------------
  706. Purpose: Ungets the current character back to the buffer.
  707. Returns: RES_OK
  708. RES_E_FAIL (if a character was already ungotten since the last get)
  709. Cond: --
  710. */
  711. RES PRIVATE Scanner_UngetChar(
  712. PSCANNER this)
  713. {
  714. RES res;
  715. ASSERT(Scanner_Validate(this));
  716. if (0 != this->chUnget)
  717. {
  718. res = RES_E_FAIL;
  719. }
  720. else
  721. {
  722. this->chUnget = this->chCur;
  723. this->chCur = 0;
  724. res = RES_OK;
  725. }
  726. return res;
  727. }
  728. /*----------------------------------------------------------
  729. Purpose: Skips white space
  730. Returns: --
  731. Cond: --
  732. */
  733. void PRIVATE Scanner_SkipBlanks(
  734. PSCANNER this)
  735. {
  736. ASSERT(Scanner_Validate(this));
  737. while (IS_WHITESPACE(this->chCur))
  738. {
  739. Scanner_GetChar(this);
  740. }
  741. }
  742. /*----------------------------------------------------------
  743. Purpose: Skips commented line
  744. Returns: --
  745. Cond: --
  746. */
  747. void PRIVATE Scanner_SkipComment(
  748. PSCANNER this)
  749. {
  750. RES res;
  751. char chSav = this->chCur;
  752. ASSERT(Scanner_Validate(this));
  753. ASSERT(IS_COMMENT_LEAD(this->chCur));
  754. // Scan to end of line
  755. do
  756. {
  757. res = Scanner_GetChar(this);
  758. } while (RES_OK == res && !IS_EOL(this->chCur));
  759. if (IS_EOL(this->chCur))
  760. Scanner_GetChar(this);
  761. }
  762. /*----------------------------------------------------------
  763. Purpose: Skips white space and comments
  764. Returns: --
  765. Cond: --
  766. */
  767. void PRIVATE Scanner_SkipBadlands(
  768. PSCANNER this)
  769. {
  770. ASSERT(Scanner_Validate(this));
  771. Scanner_GetChar(this);
  772. Scanner_SkipBlanks(this);
  773. while (IS_COMMENT_LEAD(this->chCur))
  774. {
  775. Scanner_SkipComment(this);
  776. Scanner_SkipBlanks(this);
  777. }
  778. }
  779. /*----------------------------------------------------------
  780. Purpose: This function scans and copies the characters that are
  781. scanned into pszBuf until the provided callback says to stop.
  782. Returns: RES_OK
  783. RES_E_OUTOFMEMORY
  784. Cond: --
  785. */
  786. RES PRIVATE Scanner_ScanForCharacters(
  787. PSCANNER this,
  788. LPSTR pszBuf,
  789. UINT cbBuf,
  790. SCANEVALPROC pfnEval,
  791. LPARAM lParam)
  792. {
  793. RES res = RES_E_MOREDATA;
  794. ASSERT(this);
  795. ASSERT(pszBuf);
  796. ASSERT(pfnEval);
  797. // Don't use CharNext because we are iterating on a single-byte
  798. // basis.
  799. for (; 0 < cbBuf; cbBuf--, pszBuf++)
  800. {
  801. res = Scanner_GetChar(this);
  802. if (RES_OK == res)
  803. {
  804. // Delimiter?
  805. BOOL bEatIt = FALSE;
  806. if (pfnEval(this->chCur, &bEatIt, lParam))
  807. {
  808. if (!bEatIt)
  809. Scanner_UngetChar(this);
  810. break; // done
  811. }
  812. // Save the whole DBCS character?
  813. if (IsDBCSLeadByte(this->chCur))
  814. {
  815. // Yes; is there enough room?
  816. if (2 <= cbBuf)
  817. {
  818. // Yes
  819. *pszBuf = this->chCur;
  820. pszBuf++; // Increment by single byte
  821. cbBuf--;
  822. *pszBuf = this->chTailByte;
  823. }
  824. else
  825. {
  826. // No; stop iterating
  827. break;
  828. }
  829. }
  830. else
  831. {
  832. // No; this is just a single byte
  833. *pszBuf = this->chCur;
  834. }
  835. }
  836. else
  837. break;
  838. }
  839. *pszBuf = 0; // add terminator
  840. return res;
  841. }
  842. /*----------------------------------------------------------
  843. Purpose: Determines if the given character is a delimiter
  844. for a keyword.
  845. Returns: TRUE (if the character is a delimiter)
  846. FALSE (otherwise)
  847. Cond: --
  848. */
  849. BOOL CALLBACK EvalKeywordChar(
  850. char ch, // Always the first byte of a DBCS character
  851. LPBOOL pbEatIt, // Default is FALSE on entry
  852. LPARAM lparam)
  853. {
  854. return !IS_KEYWORD(ch);
  855. }
  856. /*----------------------------------------------------------
  857. Purpose: Scans for the keyword. Returns a new token.
  858. Returns: RES_OK
  859. RES_E_OUTOFMEMORY
  860. Cond: --
  861. */
  862. RES PRIVATE Scanner_GetKeywordTok(
  863. PSCANNER this,
  864. PTOK * pptok)
  865. {
  866. char sz[MAX_BUF_KEYWORD];
  867. UINT cbBuf;
  868. SYM sym;
  869. ASSERT(this);
  870. ASSERT(pptok);
  871. *sz = this->chCur;
  872. cbBuf = sizeof(sz) - 1 - 1; // reserve place for terminator
  873. Scanner_ScanForCharacters(this, &sz[1], cbBuf, EvalKeywordChar, 0);
  874. sym = SymFromKeyword(sz);
  875. return Tok_New(pptok, sym, sz, this->iLine);
  876. }
  877. /*----------------------------------------------------------
  878. Purpose: Determines if the given character is a delimiter
  879. for a string constant.
  880. *pbEatIt is set to TRUE if the character must be
  881. eaten (not copied to the buffer). Only used if
  882. this function returns TRUE.
  883. Returns: TRUE (if the character is a delimiter)
  884. FALSE (otherwise)
  885. Cond: --
  886. */
  887. BOOL CALLBACK EvalStringChar(
  888. char ch, // Always the first byte of a DBCS character
  889. LPBOOL pbEatIt, // Default is FALSE on entry
  890. LPARAM lparam)
  891. {
  892. BOOL bRet;
  893. PBOOL pbEncounteredBS = (PBOOL)lparam;
  894. BOOL bBS = *pbEncounteredBS;
  895. *pbEncounteredBS = FALSE;
  896. if (IS_QUOTE(ch))
  897. {
  898. // Is this after
  899. if (bBS)
  900. bRet = FALSE;
  901. else
  902. {
  903. *pbEatIt = TRUE;
  904. bRet = TRUE;
  905. }
  906. }
  907. else if (IS_BACKSLASH(ch))
  908. {
  909. if (!bBS)
  910. *pbEncounteredBS = TRUE;
  911. bRet = FALSE;
  912. }
  913. else
  914. bRet = FALSE;
  915. return bRet;
  916. }
  917. /*----------------------------------------------------------
  918. Purpose: Scans for the string constant. Returns a new token.
  919. Returns: RES_OK
  920. RES_E_OUTOFMEMORY
  921. Cond: --
  922. */
  923. RES PRIVATE Scanner_GetStringTok(
  924. PSCANNER this,
  925. PTOK * pptok)
  926. {
  927. char sz[MAX_BUF];
  928. UINT cbBuf;
  929. BOOL bBS;
  930. ASSERT(this);
  931. ASSERT(pptok);
  932. *sz = 0;
  933. cbBuf = sizeof(sz) - 1; // reserve place for terminator
  934. bBS = FALSE;
  935. Scanner_ScanForCharacters(this, sz, cbBuf, EvalStringChar, (LPARAM)&bBS);
  936. return TokSz_New(pptok, SYM_STRING_LITERAL, "\"", this->iLine, sz);
  937. }
  938. /*----------------------------------------------------------
  939. Purpose: Determines if the given character is a delimiter
  940. for a keyword.
  941. Returns: TRUE (if the character is a delimiter)
  942. FALSE (otherwise)
  943. Cond: --
  944. */
  945. BOOL CALLBACK EvalNumberChar(
  946. char ch, // Always the first byte of a DBCS character
  947. LPBOOL pbEatIt, // Default is FALSE on entry
  948. LPARAM lparam)
  949. {
  950. return !IS_DIGIT(ch);
  951. }
  952. /*----------------------------------------------------------
  953. Purpose: Scans for the number constant. Returns a new token.
  954. Returns: RES_OK
  955. RES_E_OUTOFMEMORY
  956. Cond: --
  957. */
  958. RES PRIVATE Scanner_GetNumberTok(
  959. PSCANNER this,
  960. PTOK * pptok)
  961. {
  962. char sz[MAX_BUF];
  963. UINT cbBuf;
  964. int n;
  965. ASSERT(this);
  966. ASSERT(pptok);
  967. *sz = this->chCur;
  968. cbBuf = sizeof(sz) - 1 - 1; // reserve place for terminator
  969. Scanner_ScanForCharacters(this, &sz[1], cbBuf, EvalNumberChar, 0);
  970. n = AnsiToInt(sz);
  971. return TokInt_New(pptok, SYM_INT_LITERAL, sz, this->iLine, n);
  972. }
  973. /*----------------------------------------------------------
  974. Purpose: Scans for the punctuation. Returns a new token.
  975. Returns: RES_OK
  976. RES_E_OUTOFMEMORY
  977. Cond: --
  978. */
  979. RES PRIVATE Scanner_GetPuncTok(
  980. PSCANNER this,
  981. PTOK * pptok)
  982. {
  983. SYM sym = SYM_UNKNOWN;
  984. char rgch[3];
  985. char chT;
  986. ASSERT(this);
  987. ASSERT(pptok);
  988. chT = this->chCur;
  989. *rgch = this->chCur;
  990. rgch[1] = 0;
  991. switch (chT)
  992. {
  993. case '=':
  994. case '<':
  995. case '>':
  996. Scanner_GetChar(this);
  997. if ('=' == this->chCur)
  998. {
  999. switch (chT)
  1000. {
  1001. case '=':
  1002. sym = SYM_EQ;
  1003. break;
  1004. case '<':
  1005. sym = SYM_LEQ;
  1006. break;
  1007. case '>':
  1008. sym = SYM_GEQ;
  1009. break;
  1010. default:
  1011. // Should never get here
  1012. ASSERT(0);
  1013. break;
  1014. }
  1015. rgch[1] = this->chCur;
  1016. rgch[2] = 0;
  1017. }
  1018. else
  1019. {
  1020. switch (chT)
  1021. {
  1022. case '=':
  1023. sym = SYM_ASSIGN;
  1024. break;
  1025. case '<':
  1026. sym = SYM_LT;
  1027. break;
  1028. case '>':
  1029. sym = SYM_GT;
  1030. break;
  1031. default:
  1032. // Should never get here
  1033. ASSERT(0);
  1034. break;
  1035. }
  1036. Scanner_UngetChar(this);
  1037. }
  1038. break;
  1039. case '!':
  1040. Scanner_GetChar(this);
  1041. if ('=' == this->chCur)
  1042. {
  1043. sym = SYM_NEQ;
  1044. rgch[1] = this->chCur;
  1045. rgch[2] = 0;
  1046. }
  1047. else
  1048. {
  1049. sym = SYM_NOT;
  1050. Scanner_UngetChar(this);
  1051. }
  1052. break;
  1053. case '+':
  1054. sym = SYM_PLUS;
  1055. break;
  1056. case '-':
  1057. sym = SYM_MINUS;
  1058. break;
  1059. case '*':
  1060. sym = SYM_MULT;
  1061. break;
  1062. case '/':
  1063. sym = SYM_DIV;
  1064. break;
  1065. case '(':
  1066. sym = SYM_LPAREN;
  1067. break;
  1068. case ')':
  1069. sym = SYM_RPAREN;
  1070. break;
  1071. case ':':
  1072. sym = SYM_COLON;
  1073. break;
  1074. case ',':
  1075. sym = SYM_COMMA;
  1076. break;
  1077. default:
  1078. if (0 == this->chCur)
  1079. {
  1080. *rgch = 0;
  1081. sym = SYM_EOF;
  1082. }
  1083. else
  1084. {
  1085. sym = SYM_UNKNOWN;
  1086. }
  1087. break;
  1088. }
  1089. return Tok_New(pptok, sym, rgch, this->iLine);
  1090. }
  1091. /*----------------------------------------------------------
  1092. Purpose: Scans for the next token. The next token is created
  1093. and returned in *pptok.
  1094. Returns: RES_OK
  1095. RES_E_FAIL (unexpected character)
  1096. Cond: --
  1097. */
  1098. RES PUBLIC Scanner_GetToken(
  1099. PSCANNER this,
  1100. PTOK * pptok)
  1101. {
  1102. RES res;
  1103. DBG_ENTER(Scanner_GetToken);
  1104. ASSERT(Scanner_Validate(this));
  1105. ASSERT(pptok);
  1106. if (this->ptokUnget)
  1107. {
  1108. this->ptokCur = this->ptokUnget;
  1109. *pptok = this->ptokCur;
  1110. this->ptokUnget = NULL;
  1111. res = RES_OK;
  1112. }
  1113. else
  1114. {
  1115. Scanner_SkipBadlands(this);
  1116. // Is this a keyword?
  1117. if (IS_KEYWORD_LEAD(this->chCur))
  1118. {
  1119. // Yes; or maybe an identifier
  1120. res = Scanner_GetKeywordTok(this, pptok);
  1121. }
  1122. // Is this a string constant?
  1123. else if (IS_QUOTE(this->chCur))
  1124. {
  1125. // Yes
  1126. res = Scanner_GetStringTok(this, pptok);
  1127. }
  1128. // Is this a number?
  1129. else if (IS_DIGIT(this->chCur))
  1130. {
  1131. // Yes
  1132. res = Scanner_GetNumberTok(this, pptok);
  1133. }
  1134. // Is this punctuation or something else?
  1135. else
  1136. {
  1137. res = Scanner_GetPuncTok(this, pptok);
  1138. }
  1139. this->ptokCur = *pptok;
  1140. #ifdef DEBUG
  1141. if (RSUCCEEDED(res))
  1142. {
  1143. Tok_Dump(*pptok);
  1144. }
  1145. #endif
  1146. }
  1147. DBG_EXIT_RES(Scanner_GetToken, res);
  1148. return res;
  1149. }
  1150. /*----------------------------------------------------------
  1151. Purpose: Ungets the current token.
  1152. Returns: RES_OK
  1153. RES_E_FAIL (if a token was already ungotten since the
  1154. last get)
  1155. Cond: --
  1156. */
  1157. RES PUBLIC Scanner_UngetToken(
  1158. PSCANNER this)
  1159. {
  1160. RES res;
  1161. ASSERT(Scanner_Validate(this));
  1162. if (this->ptokUnget)
  1163. {
  1164. ASSERT(0);
  1165. res = RES_E_FAIL;
  1166. }
  1167. else
  1168. {
  1169. this->ptokUnget = this->ptokCur;
  1170. this->ptokCur = NULL;
  1171. res = RES_OK;
  1172. }
  1173. return res;
  1174. }
  1175. /*----------------------------------------------------------
  1176. Purpose: Returns the line of the currently read token.
  1177. Returns: see above
  1178. Cond: --
  1179. */
  1180. DWORD PUBLIC Scanner_GetLine(
  1181. PSCANNER this)
  1182. {
  1183. DWORD iLine;
  1184. ASSERT(this);
  1185. if (this->ptokUnget)
  1186. {
  1187. iLine = Tok_GetLine(this->ptokUnget);
  1188. }
  1189. else
  1190. {
  1191. iLine = this->iLine;
  1192. }
  1193. return iLine;
  1194. }
  1195. /*----------------------------------------------------------
  1196. Purpose: This function peeks at the next token and returns
  1197. the sym type.
  1198. Returns: RES_OK
  1199. RES_E_FAIL
  1200. RES_E_INVALIDPARAM
  1201. Cond: --
  1202. */
  1203. RES PUBLIC Scanner_Peek(
  1204. PSCANNER this,
  1205. PSYM psym)
  1206. {
  1207. RES res;
  1208. PTOK ptok;
  1209. ASSERT(this);
  1210. ASSERT(psym);
  1211. DBG_ENTER(Scanner_Peek);
  1212. res = Scanner_GetToken(this, &ptok);
  1213. if (RSUCCEEDED(res))
  1214. {
  1215. *psym = Tok_GetSym(ptok);
  1216. Scanner_UngetToken(this);
  1217. res = RES_OK;
  1218. }
  1219. DBG_EXIT_RES(Scanner_Peek, res);
  1220. return res;
  1221. }
  1222. /*----------------------------------------------------------
  1223. Purpose: This function expects that the next token that will
  1224. be read from the scanner is of the given sym type.
  1225. If the next token is of the expected type, the function
  1226. eats the token and returns RES_OK. Otherwise, the
  1227. function fails.
  1228. Returns: RES_OK
  1229. RES_E_FAIL
  1230. RES_E_INVALIDPARAM
  1231. Cond: --
  1232. */
  1233. RES PUBLIC Scanner_ReadToken(
  1234. PSCANNER this,
  1235. SYM sym)
  1236. {
  1237. RES res;
  1238. PTOK ptok;
  1239. DBG_ENTER(Scanner_ReadToken);
  1240. res = Scanner_GetToken(this, &ptok);
  1241. if (RSUCCEEDED(res))
  1242. {
  1243. if (Tok_GetSym(ptok) == sym)
  1244. {
  1245. // Eat the token
  1246. Tok_Delete(ptok);
  1247. res = RES_OK;
  1248. }
  1249. else
  1250. {
  1251. Scanner_UngetToken(this);
  1252. res = RES_E_FAIL;
  1253. }
  1254. }
  1255. DBG_EXIT_RES(Scanner_ReadToken, res);
  1256. return res;
  1257. }
  1258. /*----------------------------------------------------------
  1259. Purpose: This function reads the next token only if it is of
  1260. the given type.
  1261. If the next token is of the expected type, the function
  1262. eats the token and returns RES_OK. Otherwise, the
  1263. token is retained for the next read, and RES_FALSE is
  1264. returned.
  1265. If pptok is non-NULL and RES_OK is returned, the
  1266. retrieved token is returned in *pptok.
  1267. Returns: RES_OK
  1268. RES_FALSE (if the next token is not of the expected type)
  1269. RES_E_FAIL
  1270. RES_E_INVALIDPARAM
  1271. Cond: --
  1272. */
  1273. RES PUBLIC Scanner_CondReadToken(
  1274. PSCANNER this,
  1275. SYM symExpect,
  1276. PTOK * pptok) // May be NULL
  1277. {
  1278. RES res;
  1279. PTOK ptok;
  1280. DBG_ENTER(Scanner_CondReadToken);
  1281. res = Scanner_GetToken(this, &ptok);
  1282. if (RSUCCEEDED(res))
  1283. {
  1284. if (Tok_GetSym(ptok) == symExpect)
  1285. {
  1286. // Eat the token
  1287. if (pptok)
  1288. *pptok = ptok;
  1289. else
  1290. Tok_Delete(ptok);
  1291. res = RES_OK;
  1292. }
  1293. else
  1294. {
  1295. if (pptok)
  1296. *pptok = NULL;
  1297. Scanner_UngetToken(this);
  1298. res = RES_FALSE; // not a failure
  1299. }
  1300. }
  1301. DBG_EXIT_RES(Scanner_CondReadToken, res);
  1302. return res;
  1303. }
  1304. /*----------------------------------------------------------
  1305. Purpose: Wrapper to add an error for the scanner.
  1306. Returns: resErr
  1307. Cond: --
  1308. */
  1309. RES PUBLIC Scanner_AddError(
  1310. PSCANNER this,
  1311. PTOK ptok, // May be NULL
  1312. RES resErr)
  1313. {
  1314. STXERR stxerr;
  1315. ASSERT(this);
  1316. ASSERT(this->hsaStxerr);
  1317. // Initialize the structure
  1318. if (NULL == ptok)
  1319. {
  1320. if (RSUCCEEDED(Scanner_GetToken(this, &ptok)))
  1321. {
  1322. Stxerr_Init(&stxerr, Tok_GetLexeme(ptok), Tok_GetLine(ptok), resErr);
  1323. Tok_Delete(ptok);
  1324. }
  1325. else
  1326. {
  1327. Stxerr_Init(&stxerr, "", Scanner_GetLine(this), resErr);
  1328. }
  1329. }
  1330. else
  1331. {
  1332. Stxerr_Init(&stxerr, Tok_GetLexeme(ptok), Tok_GetLine(ptok), resErr);
  1333. }
  1334. // Add to the list of errors
  1335. SAInsertItem(this->hsaStxerr, SA_APPEND, &stxerr);
  1336. return resErr;
  1337. }
  1338. /*----------------------------------------------------------
  1339. Purpose: Adds an error to the list.
  1340. Returns: resErr
  1341. Cond: --
  1342. */
  1343. RES PUBLIC Stxerr_Add(
  1344. HSA hsaStxerr,
  1345. LPCSTR pszLexeme,
  1346. DWORD iLine,
  1347. RES resErr)
  1348. {
  1349. STXERR stxerr;
  1350. LPCSTR psz;
  1351. ASSERT(hsaStxerr);
  1352. if (pszLexeme)
  1353. psz = pszLexeme;
  1354. else
  1355. psz = "";
  1356. // Add to the list of errors
  1357. Stxerr_Init(&stxerr, psz, iLine, resErr);
  1358. SAInsertItem(hsaStxerr, SA_APPEND, &stxerr);
  1359. return resErr;
  1360. }
  1361. /*----------------------------------------------------------
  1362. Purpose: Adds an error to the list.
  1363. Returns: resErr
  1364. Cond: --
  1365. */
  1366. RES PUBLIC Stxerr_AddTok(
  1367. HSA hsaStxerr,
  1368. PTOK ptok,
  1369. RES resErr)
  1370. {
  1371. LPCSTR pszLexeme;
  1372. DWORD iLine;
  1373. ASSERT(hsaStxerr);
  1374. if (ptok)
  1375. {
  1376. pszLexeme = Tok_GetLexeme(ptok);
  1377. iLine = Tok_GetLine(ptok);
  1378. }
  1379. else
  1380. {
  1381. pszLexeme = NULL;
  1382. iLine = 0;
  1383. }
  1384. return Stxerr_Add(hsaStxerr, pszLexeme, iLine, resErr);
  1385. }
  1386. /*----------------------------------------------------------
  1387. Purpose: Shows a series of message boxes of all the errors
  1388. found in the script.
  1389. Returns: RES_OK
  1390. Cond: --
  1391. */
  1392. RES PUBLIC Stxerr_ShowErrors(
  1393. HSA hsaStxerr,
  1394. HWND hwndOwner)
  1395. {
  1396. DWORD cel;
  1397. DWORD i;
  1398. STXERR stxerr;
  1399. #ifndef WINNT_RAS
  1400. //
  1401. // On Win95, syntax-errors are reported using a series of message-boxes.
  1402. // On NT, syntax-error information is written to a file
  1403. // named %windir%\system32\ras\script.log.
  1404. //
  1405. cel = SAGetCount(hsaStxerr);
  1406. for (i = 0; i < cel; i++)
  1407. {
  1408. BOOL bRet = SAGetItem(hsaStxerr, i, &stxerr);
  1409. ASSERT(bRet);
  1410. if (bRet)
  1411. {
  1412. UINT ids = IdsFromRes(Stxerr_GetRes(&stxerr));
  1413. if (0 != ids)
  1414. {
  1415. MsgBox(g_hinst,
  1416. hwndOwner,
  1417. MAKEINTRESOURCE(ids),
  1418. MAKEINTRESOURCE(IDS_CAP_Script),
  1419. NULL,
  1420. MB_ERROR,
  1421. g_szScript,
  1422. Stxerr_GetLine(&stxerr),
  1423. Stxerr_GetLexeme(&stxerr));
  1424. }
  1425. }
  1426. }
  1427. #else // !WINNT_RAS
  1428. RxLogErrors(((SCRIPTDATA*)hwndOwner)->hscript, (VOID*)hsaStxerr);
  1429. #endif // !WINNT_RAS
  1430. return RES_OK;
  1431. }