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

1774 lines
38 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. else
  559. {
  560. // Reset buffer fields
  561. TRACE_MSG(TF_GENERAL, "Opened script \"%s\"", pszPath);
  562. lstrcpyn(this->szScript, pszPath, sizeof(this->szScript));
  563. lstrcpyn(g_szScript, pszPath, sizeof(g_szScript));
  564. ClearFlag(this->dwFlags, SCF_NOSCRIPT);
  565. this->pbCur = this->pbBuffer;
  566. this->cbUnread = 0;
  567. this->chUnget = 0;
  568. this->chTailByte = 0;
  569. this->iLine = 1;
  570. res = RES_OK;
  571. }
  572. }
  573. else
  574. res = RES_E_INVALIDPARAM;
  575. DBG_EXIT_RES(Scanner_OpenScript, res);
  576. return res;
  577. }
  578. /*----------------------------------------------------------
  579. Purpose: Reads enough bytes from the file to fill the buffer.
  580. Returns: RES_OK
  581. RES_E_FAIL (if ReadFile failed)
  582. RES_E_EOF
  583. Cond: --
  584. */
  585. RES PRIVATE Scanner_Read(
  586. PSCANNER this)
  587. {
  588. RES res;
  589. BOOL bResult;
  590. LPBYTE pb;
  591. DWORD cb;
  592. DWORD cbUnread;
  593. DBG_ENTER(Scanner_Read);
  594. ASSERT(Scanner_Validate(this));
  595. // Move the unread bytes to the front of the buffer before reading
  596. // more bytes. This function may get called when there are still
  597. // some unread bytes in the buffer. We do not want to lose those
  598. // bytes.
  599. // I'm too lazy to make this a circular buffer.
  600. BltByte(this->pbBuffer, this->pbCur, this->cbUnread);
  601. this->pbCur = this->pbBuffer;
  602. pb = this->pbBuffer + this->cbUnread;
  603. cb = (DWORD)(SCANNER_BUF_SIZE - (pb - this->pbBuffer));
  604. bResult = ReadFile(this->hfile, pb, cb, &cbUnread, NULL);
  605. if (!bResult)
  606. {
  607. res = RES_E_FAIL;
  608. }
  609. else
  610. {
  611. // End of file?
  612. if (0 == cbUnread)
  613. {
  614. // Yes
  615. res = RES_E_EOF;
  616. }
  617. else
  618. {
  619. // No
  620. this->cbUnread += cbUnread;
  621. res = RES_OK;
  622. }
  623. }
  624. DBG_EXIT_RES(Scanner_Read, res);
  625. return res;
  626. }
  627. /*----------------------------------------------------------
  628. Purpose: Gets the next character in the file (buffer). This
  629. function will scan the file buffer using CharNext,
  630. and store the current byte in chCur.
  631. Note for DBCS characters this means that only the
  632. lead byte will be stored in chCur. If chCur is a
  633. lead byte, the trailing byte will be stored in
  634. chTailByte.
  635. Returns: RES_OK
  636. RES_E_EOF
  637. Cond: --
  638. */
  639. RES PRIVATE Scanner_GetChar(
  640. PSCANNER this)
  641. {
  642. RES res = RES_OK; // assume success
  643. ASSERT(Scanner_Validate(this));
  644. if (0 != this->chUnget)
  645. {
  646. this->chCur = this->chUnget;
  647. this->chUnget = 0;
  648. }
  649. else
  650. {
  651. // Time to read more into the buffer?
  652. if (0 == this->cbUnread)
  653. {
  654. // Yes
  655. res = Scanner_Read(this);
  656. }
  657. if (RSUCCEEDED(res))
  658. {
  659. LPBYTE pbCur = this->pbCur;
  660. LPBYTE pbNext = CharNext(pbCur);
  661. DWORD cb;
  662. BOOL bIsLeadByte;
  663. this->chCur = *pbCur;
  664. bIsLeadByte = IsDBCSLeadByte(this->chCur);
  665. // We might be at the end of the unread characters, where
  666. // a DBCS character is cut in half (ie, the trailing byte
  667. // is missing). Are we in this case?
  668. if (bIsLeadByte && 1 == this->cbUnread)
  669. {
  670. // Yes; read more into the buffer, we don't care about
  671. // the return value
  672. Scanner_Read(this);
  673. // this->pbCur might have changed
  674. pbCur = this->pbCur;
  675. pbNext = CharNext(pbCur);
  676. }
  677. cb = (DWORD)(pbNext - pbCur);
  678. this->cbUnread -= cb;
  679. this->pbCur = pbNext;
  680. // Do we need to save away the whole DBCS character?
  681. if (bIsLeadByte)
  682. {
  683. // Yes
  684. ASSERT(2 == cb); // We don't support MBCS
  685. this->chTailByte = pbCur[1];
  686. }
  687. if (IS_EOL(this->chCur))
  688. {
  689. this->iLine++;
  690. };
  691. }
  692. else
  693. this->chCur = 0;
  694. }
  695. return res;
  696. }
  697. /*----------------------------------------------------------
  698. Purpose: Ungets the current character back to the buffer.
  699. Returns: RES_OK
  700. RES_E_FAIL (if a character was already ungotten since the last get)
  701. Cond: --
  702. */
  703. RES PRIVATE Scanner_UngetChar(
  704. PSCANNER this)
  705. {
  706. RES res;
  707. ASSERT(Scanner_Validate(this));
  708. if (0 != this->chUnget)
  709. {
  710. res = RES_E_FAIL;
  711. }
  712. else
  713. {
  714. this->chUnget = this->chCur;
  715. this->chCur = 0;
  716. res = RES_OK;
  717. }
  718. return res;
  719. }
  720. /*----------------------------------------------------------
  721. Purpose: Skips white space
  722. Returns: --
  723. Cond: --
  724. */
  725. void PRIVATE Scanner_SkipBlanks(
  726. PSCANNER this)
  727. {
  728. ASSERT(Scanner_Validate(this));
  729. while (IS_WHITESPACE(this->chCur))
  730. {
  731. Scanner_GetChar(this);
  732. }
  733. }
  734. /*----------------------------------------------------------
  735. Purpose: Skips commented line
  736. Returns: --
  737. Cond: --
  738. */
  739. void PRIVATE Scanner_SkipComment(
  740. PSCANNER this)
  741. {
  742. RES res;
  743. char chSav = this->chCur;
  744. ASSERT(Scanner_Validate(this));
  745. ASSERT(IS_COMMENT_LEAD(this->chCur));
  746. // Scan to end of line
  747. do
  748. {
  749. res = Scanner_GetChar(this);
  750. } while (RES_OK == res && !IS_EOL(this->chCur));
  751. if (IS_EOL(this->chCur))
  752. Scanner_GetChar(this);
  753. }
  754. /*----------------------------------------------------------
  755. Purpose: Skips white space and comments
  756. Returns: --
  757. Cond: --
  758. */
  759. void PRIVATE Scanner_SkipBadlands(
  760. PSCANNER this)
  761. {
  762. ASSERT(Scanner_Validate(this));
  763. Scanner_GetChar(this);
  764. Scanner_SkipBlanks(this);
  765. while (IS_COMMENT_LEAD(this->chCur))
  766. {
  767. Scanner_SkipComment(this);
  768. Scanner_SkipBlanks(this);
  769. }
  770. }
  771. /*----------------------------------------------------------
  772. Purpose: This function scans and copies the characters that are
  773. scanned into pszBuf until the provided callback says to stop.
  774. Returns: RES_OK
  775. RES_E_OUTOFMEMORY
  776. Cond: --
  777. */
  778. RES PRIVATE Scanner_ScanForCharacters(
  779. PSCANNER this,
  780. LPSTR pszBuf,
  781. UINT cbBuf,
  782. SCANEVALPROC pfnEval,
  783. LPARAM lParam)
  784. {
  785. RES res = RES_E_MOREDATA;
  786. ASSERT(this);
  787. ASSERT(pszBuf);
  788. ASSERT(pfnEval);
  789. // Don't use CharNext because we are iterating on a single-byte
  790. // basis.
  791. for (; 0 < cbBuf; cbBuf--, pszBuf++)
  792. {
  793. res = Scanner_GetChar(this);
  794. if (RES_OK == res)
  795. {
  796. // Delimiter?
  797. BOOL bEatIt = FALSE;
  798. if (pfnEval(this->chCur, &bEatIt, lParam))
  799. {
  800. if (!bEatIt)
  801. Scanner_UngetChar(this);
  802. break; // done
  803. }
  804. // Save the whole DBCS character?
  805. if (IsDBCSLeadByte(this->chCur))
  806. {
  807. // Yes; is there enough room?
  808. if (2 <= cbBuf)
  809. {
  810. // Yes
  811. *pszBuf = this->chCur;
  812. pszBuf++; // Increment by single byte
  813. cbBuf--;
  814. *pszBuf = this->chTailByte;
  815. }
  816. else
  817. {
  818. // No; stop iterating
  819. break;
  820. }
  821. }
  822. else
  823. {
  824. // No; this is just a single byte
  825. *pszBuf = this->chCur;
  826. }
  827. }
  828. else
  829. break;
  830. }
  831. *pszBuf = 0; // add terminator
  832. return res;
  833. }
  834. /*----------------------------------------------------------
  835. Purpose: Determines if the given character is a delimiter
  836. for a keyword.
  837. Returns: TRUE (if the character is a delimiter)
  838. FALSE (otherwise)
  839. Cond: --
  840. */
  841. BOOL CALLBACK EvalKeywordChar(
  842. char ch, // Always the first byte of a DBCS character
  843. LPBOOL pbEatIt, // Default is FALSE on entry
  844. LPARAM lparam)
  845. {
  846. return !IS_KEYWORD(ch);
  847. }
  848. /*----------------------------------------------------------
  849. Purpose: Scans for the keyword. Returns a new token.
  850. Returns: RES_OK
  851. RES_E_OUTOFMEMORY
  852. Cond: --
  853. */
  854. RES PRIVATE Scanner_GetKeywordTok(
  855. PSCANNER this,
  856. PTOK * pptok)
  857. {
  858. char sz[MAX_BUF_KEYWORD];
  859. UINT cbBuf;
  860. SYM sym;
  861. ASSERT(this);
  862. ASSERT(pptok);
  863. *sz = this->chCur;
  864. cbBuf = sizeof(sz) - 1 - 1; // reserve place for terminator
  865. Scanner_ScanForCharacters(this, &sz[1], cbBuf, EvalKeywordChar, 0);
  866. sym = SymFromKeyword(sz);
  867. return Tok_New(pptok, sym, sz, this->iLine);
  868. }
  869. /*----------------------------------------------------------
  870. Purpose: Determines if the given character is a delimiter
  871. for a string constant.
  872. *pbEatIt is set to TRUE if the character must be
  873. eaten (not copied to the buffer). Only used if
  874. this function returns TRUE.
  875. Returns: TRUE (if the character is a delimiter)
  876. FALSE (otherwise)
  877. Cond: --
  878. */
  879. BOOL CALLBACK EvalStringChar(
  880. char ch, // Always the first byte of a DBCS character
  881. LPBOOL pbEatIt, // Default is FALSE on entry
  882. LPARAM lparam)
  883. {
  884. BOOL bRet;
  885. PBOOL pbEncounteredBS = (PBOOL)lparam;
  886. BOOL bBS = *pbEncounteredBS;
  887. *pbEncounteredBS = FALSE;
  888. if (IS_QUOTE(ch))
  889. {
  890. // Is this after
  891. if (bBS)
  892. bRet = FALSE;
  893. else
  894. {
  895. *pbEatIt = TRUE;
  896. bRet = TRUE;
  897. }
  898. }
  899. else if (IS_BACKSLASH(ch))
  900. {
  901. if (!bBS)
  902. *pbEncounteredBS = TRUE;
  903. bRet = FALSE;
  904. }
  905. else
  906. bRet = FALSE;
  907. return bRet;
  908. }
  909. /*----------------------------------------------------------
  910. Purpose: Scans for the string constant. Returns a new token.
  911. Returns: RES_OK
  912. RES_E_OUTOFMEMORY
  913. Cond: --
  914. */
  915. RES PRIVATE Scanner_GetStringTok(
  916. PSCANNER this,
  917. PTOK * pptok)
  918. {
  919. char sz[MAX_BUF];
  920. UINT cbBuf;
  921. BOOL bBS;
  922. ASSERT(this);
  923. ASSERT(pptok);
  924. *sz = 0;
  925. cbBuf = sizeof(sz) - 1; // reserve place for terminator
  926. bBS = FALSE;
  927. Scanner_ScanForCharacters(this, sz, cbBuf, EvalStringChar, (LPARAM)&bBS);
  928. return TokSz_New(pptok, SYM_STRING_LITERAL, "\"", this->iLine, sz);
  929. }
  930. /*----------------------------------------------------------
  931. Purpose: Determines if the given character is a delimiter
  932. for a keyword.
  933. Returns: TRUE (if the character is a delimiter)
  934. FALSE (otherwise)
  935. Cond: --
  936. */
  937. BOOL CALLBACK EvalNumberChar(
  938. char ch, // Always the first byte of a DBCS character
  939. LPBOOL pbEatIt, // Default is FALSE on entry
  940. LPARAM lparam)
  941. {
  942. return !IS_DIGIT(ch);
  943. }
  944. /*----------------------------------------------------------
  945. Purpose: Scans for the number constant. Returns a new token.
  946. Returns: RES_OK
  947. RES_E_OUTOFMEMORY
  948. Cond: --
  949. */
  950. RES PRIVATE Scanner_GetNumberTok(
  951. PSCANNER this,
  952. PTOK * pptok)
  953. {
  954. char sz[MAX_BUF];
  955. UINT cbBuf;
  956. int n;
  957. ASSERT(this);
  958. ASSERT(pptok);
  959. *sz = this->chCur;
  960. cbBuf = sizeof(sz) - 1 - 1; // reserve place for terminator
  961. Scanner_ScanForCharacters(this, &sz[1], cbBuf, EvalNumberChar, 0);
  962. n = AnsiToInt(sz);
  963. return TokInt_New(pptok, SYM_INT_LITERAL, sz, this->iLine, n);
  964. }
  965. /*----------------------------------------------------------
  966. Purpose: Scans for the punctuation. Returns a new token.
  967. Returns: RES_OK
  968. RES_E_OUTOFMEMORY
  969. Cond: --
  970. */
  971. RES PRIVATE Scanner_GetPuncTok(
  972. PSCANNER this,
  973. PTOK * pptok)
  974. {
  975. SYM sym;
  976. char rgch[3];
  977. char chT;
  978. ASSERT(this);
  979. ASSERT(pptok);
  980. chT = this->chCur;
  981. *rgch = this->chCur;
  982. rgch[1] = 0;
  983. switch (chT)
  984. {
  985. case '=':
  986. case '<':
  987. case '>':
  988. Scanner_GetChar(this);
  989. if ('=' == this->chCur)
  990. {
  991. switch (chT)
  992. {
  993. case '=':
  994. sym = SYM_EQ;
  995. break;
  996. case '<':
  997. sym = SYM_LEQ;
  998. break;
  999. case '>':
  1000. sym = SYM_GEQ;
  1001. break;
  1002. default:
  1003. // Should never get here
  1004. ASSERT(0);
  1005. break;
  1006. }
  1007. rgch[1] = this->chCur;
  1008. rgch[2] = 0;
  1009. }
  1010. else
  1011. {
  1012. switch (chT)
  1013. {
  1014. case '=':
  1015. sym = SYM_ASSIGN;
  1016. break;
  1017. case '<':
  1018. sym = SYM_LT;
  1019. break;
  1020. case '>':
  1021. sym = SYM_GT;
  1022. break;
  1023. default:
  1024. // Should never get here
  1025. ASSERT(0);
  1026. break;
  1027. }
  1028. Scanner_UngetChar(this);
  1029. }
  1030. break;
  1031. case '!':
  1032. Scanner_GetChar(this);
  1033. if ('=' == this->chCur)
  1034. {
  1035. sym = SYM_NEQ;
  1036. rgch[1] = this->chCur;
  1037. rgch[2] = 0;
  1038. }
  1039. else
  1040. {
  1041. sym = SYM_NOT;
  1042. Scanner_UngetChar(this);
  1043. }
  1044. break;
  1045. case '+':
  1046. sym = SYM_PLUS;
  1047. break;
  1048. case '-':
  1049. sym = SYM_MINUS;
  1050. break;
  1051. case '*':
  1052. sym = SYM_MULT;
  1053. break;
  1054. case '/':
  1055. sym = SYM_DIV;
  1056. break;
  1057. case '(':
  1058. sym = SYM_LPAREN;
  1059. break;
  1060. case ')':
  1061. sym = SYM_RPAREN;
  1062. break;
  1063. case ':':
  1064. sym = SYM_COLON;
  1065. break;
  1066. case ',':
  1067. sym = SYM_COMMA;
  1068. break;
  1069. default:
  1070. if (0 == this->chCur)
  1071. {
  1072. *rgch = 0;
  1073. sym = SYM_EOF;
  1074. }
  1075. else
  1076. {
  1077. sym = SYM_UNKNOWN;
  1078. }
  1079. break;
  1080. }
  1081. return Tok_New(pptok, sym, rgch, this->iLine);
  1082. }
  1083. /*----------------------------------------------------------
  1084. Purpose: Scans for the next token. The next token is created
  1085. and returned in *pptok.
  1086. Returns: RES_OK
  1087. RES_E_FAIL (unexpected character)
  1088. Cond: --
  1089. */
  1090. RES PUBLIC Scanner_GetToken(
  1091. PSCANNER this,
  1092. PTOK * pptok)
  1093. {
  1094. RES res;
  1095. DBG_ENTER(Scanner_GetToken);
  1096. ASSERT(Scanner_Validate(this));
  1097. ASSERT(pptok);
  1098. if (this->ptokUnget)
  1099. {
  1100. this->ptokCur = this->ptokUnget;
  1101. *pptok = this->ptokCur;
  1102. this->ptokUnget = NULL;
  1103. res = RES_OK;
  1104. }
  1105. else
  1106. {
  1107. Scanner_SkipBadlands(this);
  1108. // Is this a keyword?
  1109. if (IS_KEYWORD_LEAD(this->chCur))
  1110. {
  1111. // Yes; or maybe an identifier
  1112. res = Scanner_GetKeywordTok(this, pptok);
  1113. }
  1114. // Is this a string constant?
  1115. else if (IS_QUOTE(this->chCur))
  1116. {
  1117. // Yes
  1118. res = Scanner_GetStringTok(this, pptok);
  1119. }
  1120. // Is this a number?
  1121. else if (IS_DIGIT(this->chCur))
  1122. {
  1123. // Yes
  1124. res = Scanner_GetNumberTok(this, pptok);
  1125. }
  1126. // Is this punctuation or something else?
  1127. else
  1128. {
  1129. res = Scanner_GetPuncTok(this, pptok);
  1130. }
  1131. this->ptokCur = *pptok;
  1132. #ifdef DEBUG
  1133. if (RSUCCEEDED(res))
  1134. {
  1135. Tok_Dump(*pptok);
  1136. }
  1137. #endif
  1138. }
  1139. DBG_EXIT_RES(Scanner_GetToken, res);
  1140. return res;
  1141. }
  1142. /*----------------------------------------------------------
  1143. Purpose: Ungets the current token.
  1144. Returns: RES_OK
  1145. RES_E_FAIL (if a token was already ungotten since the
  1146. last get)
  1147. Cond: --
  1148. */
  1149. RES PUBLIC Scanner_UngetToken(
  1150. PSCANNER this)
  1151. {
  1152. RES res;
  1153. ASSERT(Scanner_Validate(this));
  1154. if (this->ptokUnget)
  1155. {
  1156. ASSERT(0);
  1157. res = RES_E_FAIL;
  1158. }
  1159. else
  1160. {
  1161. this->ptokUnget = this->ptokCur;
  1162. this->ptokCur = NULL;
  1163. res = RES_OK;
  1164. }
  1165. return res;
  1166. }
  1167. /*----------------------------------------------------------
  1168. Purpose: Returns the line of the currently read token.
  1169. Returns: see above
  1170. Cond: --
  1171. */
  1172. DWORD PUBLIC Scanner_GetLine(
  1173. PSCANNER this)
  1174. {
  1175. DWORD iLine;
  1176. ASSERT(this);
  1177. if (this->ptokUnget)
  1178. {
  1179. iLine = Tok_GetLine(this->ptokUnget);
  1180. }
  1181. else
  1182. {
  1183. iLine = this->iLine;
  1184. }
  1185. return iLine;
  1186. }
  1187. /*----------------------------------------------------------
  1188. Purpose: This function peeks at the next token and returns
  1189. the sym type.
  1190. Returns: RES_OK
  1191. RES_E_FAIL
  1192. RES_E_INVALIDPARAM
  1193. Cond: --
  1194. */
  1195. RES PUBLIC Scanner_Peek(
  1196. PSCANNER this,
  1197. PSYM psym)
  1198. {
  1199. RES res;
  1200. PTOK ptok;
  1201. ASSERT(this);
  1202. ASSERT(psym);
  1203. DBG_ENTER(Scanner_Peek);
  1204. res = Scanner_GetToken(this, &ptok);
  1205. if (RSUCCEEDED(res))
  1206. {
  1207. *psym = Tok_GetSym(ptok);
  1208. Scanner_UngetToken(this);
  1209. res = RES_OK;
  1210. }
  1211. DBG_EXIT_RES(Scanner_Peek, res);
  1212. return res;
  1213. }
  1214. /*----------------------------------------------------------
  1215. Purpose: This function expects that the next token that will
  1216. be read from the scanner is of the given sym type.
  1217. If the next token is of the expected type, the function
  1218. eats the token and returns RES_OK. Otherwise, the
  1219. function fails.
  1220. Returns: RES_OK
  1221. RES_E_FAIL
  1222. RES_E_INVALIDPARAM
  1223. Cond: --
  1224. */
  1225. RES PUBLIC Scanner_ReadToken(
  1226. PSCANNER this,
  1227. SYM sym)
  1228. {
  1229. RES res;
  1230. PTOK ptok;
  1231. DBG_ENTER(Scanner_ReadToken);
  1232. res = Scanner_GetToken(this, &ptok);
  1233. if (RSUCCEEDED(res))
  1234. {
  1235. if (Tok_GetSym(ptok) == sym)
  1236. {
  1237. // Eat the token
  1238. Tok_Delete(ptok);
  1239. res = RES_OK;
  1240. }
  1241. else
  1242. {
  1243. Scanner_UngetToken(this);
  1244. res = RES_E_FAIL;
  1245. }
  1246. }
  1247. DBG_EXIT_RES(Scanner_ReadToken, res);
  1248. return res;
  1249. }
  1250. /*----------------------------------------------------------
  1251. Purpose: This function reads the next token only if it is of
  1252. the given type.
  1253. If the next token is of the expected type, the function
  1254. eats the token and returns RES_OK. Otherwise, the
  1255. token is retained for the next read, and RES_FALSE is
  1256. returned.
  1257. If pptok is non-NULL and RES_OK is returned, the
  1258. retrieved token is returned in *pptok.
  1259. Returns: RES_OK
  1260. RES_FALSE (if the next token is not of the expected type)
  1261. RES_E_FAIL
  1262. RES_E_INVALIDPARAM
  1263. Cond: --
  1264. */
  1265. RES PUBLIC Scanner_CondReadToken(
  1266. PSCANNER this,
  1267. SYM symExpect,
  1268. PTOK * pptok) // May be NULL
  1269. {
  1270. RES res;
  1271. PTOK ptok;
  1272. DBG_ENTER(Scanner_CondReadToken);
  1273. res = Scanner_GetToken(this, &ptok);
  1274. if (RSUCCEEDED(res))
  1275. {
  1276. if (Tok_GetSym(ptok) == symExpect)
  1277. {
  1278. // Eat the token
  1279. if (pptok)
  1280. *pptok = ptok;
  1281. else
  1282. Tok_Delete(ptok);
  1283. res = RES_OK;
  1284. }
  1285. else
  1286. {
  1287. if (pptok)
  1288. *pptok = NULL;
  1289. Scanner_UngetToken(this);
  1290. res = RES_FALSE; // not a failure
  1291. }
  1292. }
  1293. DBG_EXIT_RES(Scanner_CondReadToken, res);
  1294. return res;
  1295. }
  1296. /*----------------------------------------------------------
  1297. Purpose: Wrapper to add an error for the scanner.
  1298. Returns: resErr
  1299. Cond: --
  1300. */
  1301. RES PUBLIC Scanner_AddError(
  1302. PSCANNER this,
  1303. PTOK ptok, // May be NULL
  1304. RES resErr)
  1305. {
  1306. STXERR stxerr;
  1307. ASSERT(this);
  1308. ASSERT(this->hsaStxerr);
  1309. // Initialize the structure
  1310. if (NULL == ptok)
  1311. {
  1312. if (RSUCCEEDED(Scanner_GetToken(this, &ptok)))
  1313. {
  1314. Stxerr_Init(&stxerr, Tok_GetLexeme(ptok), Tok_GetLine(ptok), resErr);
  1315. Tok_Delete(ptok);
  1316. }
  1317. else
  1318. {
  1319. Stxerr_Init(&stxerr, "", Scanner_GetLine(this), resErr);
  1320. }
  1321. }
  1322. else
  1323. {
  1324. Stxerr_Init(&stxerr, Tok_GetLexeme(ptok), Tok_GetLine(ptok), resErr);
  1325. }
  1326. // Add to the list of errors
  1327. SAInsertItem(this->hsaStxerr, SA_APPEND, &stxerr);
  1328. return resErr;
  1329. }
  1330. /*----------------------------------------------------------
  1331. Purpose: Adds an error to the list.
  1332. Returns: resErr
  1333. Cond: --
  1334. */
  1335. RES PUBLIC Stxerr_Add(
  1336. HSA hsaStxerr,
  1337. LPCSTR pszLexeme,
  1338. DWORD iLine,
  1339. RES resErr)
  1340. {
  1341. STXERR stxerr;
  1342. LPCSTR psz;
  1343. ASSERT(hsaStxerr);
  1344. if (pszLexeme)
  1345. psz = pszLexeme;
  1346. else
  1347. psz = "";
  1348. // Add to the list of errors
  1349. Stxerr_Init(&stxerr, psz, iLine, resErr);
  1350. SAInsertItem(hsaStxerr, SA_APPEND, &stxerr);
  1351. return resErr;
  1352. }
  1353. /*----------------------------------------------------------
  1354. Purpose: Adds an error to the list.
  1355. Returns: resErr
  1356. Cond: --
  1357. */
  1358. RES PUBLIC Stxerr_AddTok(
  1359. HSA hsaStxerr,
  1360. PTOK ptok,
  1361. RES resErr)
  1362. {
  1363. LPCSTR pszLexeme;
  1364. DWORD iLine;
  1365. ASSERT(hsaStxerr);
  1366. if (ptok)
  1367. {
  1368. pszLexeme = Tok_GetLexeme(ptok);
  1369. iLine = Tok_GetLine(ptok);
  1370. }
  1371. else
  1372. {
  1373. pszLexeme = NULL;
  1374. iLine = 0;
  1375. }
  1376. return Stxerr_Add(hsaStxerr, pszLexeme, iLine, resErr);
  1377. }
  1378. /*----------------------------------------------------------
  1379. Purpose: Shows a series of message boxes of all the errors
  1380. found in the script.
  1381. Returns: RES_OK
  1382. Cond: --
  1383. */
  1384. RES PUBLIC Stxerr_ShowErrors(
  1385. HSA hsaStxerr,
  1386. HWND hwndOwner)
  1387. {
  1388. DWORD cel;
  1389. DWORD i;
  1390. STXERR stxerr;
  1391. #ifndef WINNT_RAS
  1392. //
  1393. // On Win95, syntax-errors are reported using a series of message-boxes.
  1394. // On NT, syntax-error information is written to a file
  1395. // named %windir%\system32\ras\script.log.
  1396. //
  1397. cel = SAGetCount(hsaStxerr);
  1398. for (i = 0; i < cel; i++)
  1399. {
  1400. BOOL bRet = SAGetItem(hsaStxerr, i, &stxerr);
  1401. ASSERT(bRet);
  1402. if (bRet)
  1403. {
  1404. UINT ids = IdsFromRes(Stxerr_GetRes(&stxerr));
  1405. if (0 != ids)
  1406. {
  1407. MsgBox(g_hinst,
  1408. hwndOwner,
  1409. MAKEINTRESOURCE(ids),
  1410. MAKEINTRESOURCE(IDS_CAP_Script),
  1411. NULL,
  1412. MB_ERROR,
  1413. g_szScript,
  1414. Stxerr_GetLine(&stxerr),
  1415. Stxerr_GetLexeme(&stxerr));
  1416. }
  1417. }
  1418. }
  1419. #else // !WINNT_RAS
  1420. RxLogErrors(((SCRIPTDATA*)hwndOwner)->hscript, (VOID*)hsaStxerr);
  1421. #endif // !WINNT_RAS
  1422. return RES_OK;
  1423. }