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.

795 lines
22 KiB

  1. /*++
  2. Copyright (c) 1997-2000 Microsoft Corporation
  3. Module Name:
  4. EzParse.cpp
  5. Abstract:
  6. Poor man C/C++/any file parser.
  7. Author:
  8. Gor Nishanov (gorn) 03-Apr-1999
  9. Revision History:
  10. Gor Nishanov (gorn) 03-Apr-1999 -- hacked together to prove that this can work
  11. GorN: 29-Sep-2000 - fix enumeration bug
  12. GorN: 29-Sep-2000 - add support for KdPrintEx like function
  13. GorN: 09-Oct-2000 - fixed "//" in the string bug
  14. GorN: 23-Oct-2000 - IGNORE_CPP_COMMENT, IGNORE_POUND_COMMENT options added
  15. GorN: 16-Apr-2001 - Properly handle \" within a string
  16. ToDo:
  17. Clean it up
  18. --*/
  19. #define STRICT
  20. #include <stdio.h>
  21. #include <windows.h>
  22. #pragma warning(disable: 4100)
  23. #include <algorithm>
  24. #include <xstring>
  25. #include "ezparse.h"
  26. DWORD ErrorCount = 0;
  27. PEZPARSE_CONTEXT EzParseCurrentContext = NULL;
  28. // To force build tool to recognize our errors
  29. #define BUILD_PREFIX_FNAME "cl %s\n"
  30. #define BUILD_PREFIX "cl wpp\n"
  31. void ExParsePrintErrorPrefix(FILE* f, char * func)
  32. {
  33. ++ErrorCount;
  34. if (EzParseCurrentContext) {
  35. fprintf(f,BUILD_PREFIX_FNAME "%s(%d) : error : (%s)",
  36. EzParseCurrentContext->filename,
  37. EzParseCurrentContext->filename,
  38. EzGetLineNo(EzParseCurrentContext->currentStart, EzParseCurrentContext),
  39. func);
  40. } else {
  41. fprintf(f,BUILD_PREFIX "wpp : error : (%s)", func);
  42. }
  43. }
  44. LPCSTR skip_stuff_in_quotes(LPCSTR q, LPCSTR begin)
  45. {
  46. char ch = *q;
  47. if (q > begin) {
  48. if (q[-1] == '\\') {
  49. return q - 1;
  50. }
  51. }
  52. for(;;) {
  53. if (q == begin) {
  54. return 0;
  55. }
  56. --q;
  57. if (*q == ch && ( (q == begin) || (q[-1] != '\\') ) ) {
  58. return q;
  59. }
  60. }
  61. }
  62. void
  63. adjust_pair( STR_PAIR& str )
  64. /*++
  65. Shrink the pair to remote leading and trailing whitespace
  66. */
  67. {
  68. while (str.beg < str.end && isspace(*str.beg)) { ++str.beg; }
  69. while (str.beg < str.end && isspace(str.end[-1])) { --str.end; }
  70. }
  71. void
  72. remove_cpp_comment(STR_PAIR& str)
  73. {
  74. LPCSTR p = str.beg;
  75. // printf("rcb: %s\n", std::string(str.beg, str.end).c_str());
  76. // let's cut the comment in the beginning of the string
  77. for(;;) {
  78. // skip the whitespace
  79. for(;;) {
  80. if (p == str.end) return;
  81. if (!isspace(*p)) break;
  82. ++p;
  83. }
  84. str.beg = p;
  85. if (p + 1 == str.end) return;
  86. if (p[0] == '/' && p[1] == '/') {
  87. // we have a comment. Need to get to the end of the comment
  88. p += 2;
  89. // printf("rcd: %s %s\n", std::string(str.beg, p).c_str(), std::string(p,str.end).c_str());
  90. for(;;) {
  91. if (p == str.end) return;
  92. if (*p == '\r' || *p == '\n') {
  93. str.beg = p;
  94. break;
  95. }
  96. ++p;
  97. }
  98. } else {
  99. // no leading comment
  100. break;
  101. }
  102. }
  103. // printf("rcc: %s %s\n", std::string(str.beg, p).c_str(), std::string(p,str.end).c_str());
  104. for(;;) {
  105. if (p == str.end) return;
  106. if (*p == '"') {
  107. // don't look for comments within a string
  108. for(;;) {
  109. if (++p == str.end) return;
  110. if (*p == '"' && p[-1] != '\\') break;
  111. }
  112. ++p;
  113. continue;
  114. }
  115. if (p + 1 == str.end) return;
  116. if (p[0] == '/')
  117. if (p[1] == '/') break;
  118. else p += 2;
  119. else
  120. p += 1;
  121. }
  122. str.end = p;
  123. // printf("rce: %s\n", std::string(str.beg, str.end).c_str());
  124. }
  125. DWORD
  126. ScanForFunctionCallsEx(
  127. IN LPCSTR begin,
  128. IN LPCSTR end,
  129. IN EZPARSE_CALLBACK Callback,
  130. IN PVOID Context,
  131. IN OUT PEZPARSE_CONTEXT ParseContext,
  132. IN DWORD Options
  133. )
  134. /*++
  135. Routine Description:
  136. Scan the buffer for expressions that looks like function calls,
  137. i.e name(sd,sdf,sdf,sdf,sdf); . It will treat variable declaration
  138. with constructor call as a function call as well.
  139. Inputs:
  140. begin, end -- pointers to the beginning and the end of the buffer
  141. Callback -- to be called for every function
  142. Context -- opaque context to be passed to callback
  143. ParseContext -- holds current parse state information
  144. --*/
  145. {
  146. LPCSTR p = begin;
  147. LPCSTR q, funcNameEnd;
  148. DWORD Status = ERROR_SUCCESS;
  149. bool double_par = FALSE;
  150. no_match:
  151. if (Options & NO_SEMICOLON) {
  152. q = end;
  153. Options &= ~NO_SEMICOLON;
  154. } else {
  155. do {
  156. ++p;
  157. if (p == end) {
  158. return Status;
  159. }
  160. } while ( *p != ';' );
  161. // Ok. Now p points to ';' //
  162. q = p;
  163. }
  164. do {
  165. if (--q <= begin) {
  166. goto no_match;
  167. }
  168. } while ( isspace(*q) );
  169. // Now q points on the first non white space character //
  170. // If it is not a ')' then we need to search for the next ';' //
  171. if (*q != ')') {
  172. goto no_match;
  173. }
  174. ParseContext->macroEnd = q;
  175. // Ok. This is a function call (definition).
  176. // Now, let's go and collect all the arguments of the first level and
  177. // get to the name of the function
  178. // HACKHACK
  179. // We need a special case for functions that looks like
  180. // KdPrintEx((Level, Indent, Msg, ...));
  181. // Essentially, we need to treat them as
  182. // KdPrintEx(Level, Indent, Msg, ...);
  183. const char *r = q;
  184. // check if we have ));
  185. do {
  186. if (--r <= begin) break; // no "));"
  187. } while ( isspace(*r) );
  188. double_par = r > begin && *r == ')';
  189. if (double_par) {
  190. q = r;
  191. // we assume that this is KdPrint((a,b,c,d,...)); at the moment
  192. // if our assumtion is wrong, we will retry the loop below
  193. }
  194. retry:
  195. {
  196. int level = 0;
  197. LPCSTR ends[128], *current = ends;
  198. STR_PAIR strs[128];
  199. // LPCSTR closing_parenthisis = q;
  200. *current = q;
  201. for(;;) {
  202. --q;
  203. if (q <= begin) {
  204. goto no_match;
  205. }
  206. switch (*q) {
  207. case ',': if (!level) {
  208. if (current - ends == 127) goto no_match;
  209. *++current = q;
  210. }
  211. break;
  212. case '(': if (level) --level; else goto maybe_match; break;
  213. case ')': ++level; break;
  214. case '\'':
  215. case '"':
  216. q = skip_stuff_in_quotes(q, begin); if(!q) goto no_match;
  217. }
  218. }
  219. maybe_match:
  220. if (current - ends == 127) goto no_match;
  221. *++current = q;
  222. funcNameEnd = q;
  223. // now q point to '(' we need to find name of the function //
  224. do {
  225. --q;
  226. if (q <= begin) {
  227. goto no_match;
  228. }
  229. } while(isspace(*q));
  230. // now q points to first not white character
  231. if (double_par) {
  232. // if we see )); and found a matching
  233. // parenthesis for the inner one, we can have
  234. // one of two cases
  235. // 1) KdPrint((a,b,c,d,...));
  236. // or
  237. // 2) DebugPrint(a,b,(c,d));
  238. // If it is the latter, we just need to
  239. // retry the scanning, now using leftmost bracket as a starting point
  240. if (*q != '(') {
  241. // restore q to the rightmost parenthesis
  242. q = ParseContext->macroEnd;
  243. double_par = FALSE;
  244. goto retry;
  245. }
  246. funcNameEnd = q;
  247. // now q point to '(' we need to find name of the function //
  248. do {
  249. --q;
  250. if (q <= begin) {
  251. goto no_match;
  252. }
  253. } while(isspace(*q));
  254. }
  255. // now q points to first non white character
  256. // BUGBUG '{' and '}' are allowed only in config files
  257. if (*q == '}') {
  258. for(;;) {
  259. if (--q < begin) goto no_match;
  260. if (*q == '{') break;
  261. }
  262. if (--q < begin) goto no_match;
  263. }
  264. if (!(isalpha(*q) || isdigit(*q) || *q == '_')) {
  265. goto no_match;
  266. }
  267. do {
  268. --q;
  269. if (q <= begin) {
  270. goto found;
  271. }
  272. } while ( isalpha(*q) || isdigit(*q) || *q == '_');
  273. ++q;
  274. if (isdigit(*q)) {
  275. goto no_match;
  276. }
  277. found:
  278. if (Options & IGNORE_COMMENT)
  279. // Verify that it is not a comment
  280. // # sign in the beginning of the line
  281. {
  282. LPCSTR line = q;
  283. //
  284. // Find the beginning of the line or file
  285. //
  286. for(;;) {
  287. if (line == begin) {
  288. // Beginning of the file. Good enough
  289. break;
  290. }
  291. if (Options & IGNORE_CPP_COMMENT && line[0] == '/' && line[1] == '/') {
  292. // C++ comment. Ignore
  293. goto no_match;
  294. }
  295. if (*line == 13 || *line == 10) {
  296. ++line;
  297. break;
  298. }
  299. --line;
  300. }
  301. //
  302. // If the first non-white character is #, ignore it
  303. //
  304. while (line <= q) {
  305. if ( *line != ' ' && *line != '\t' ) {
  306. break;
  307. }
  308. ++line;
  309. }
  310. if (Options & IGNORE_POUND_COMMENT && *line == '#') {
  311. goto no_match;
  312. }
  313. }
  314. {
  315. int i = 0;
  316. strs[0].beg = q;
  317. strs[0].end = funcNameEnd;
  318. adjust_pair(strs[0]);
  319. while (current != ends) {
  320. // putchar('<');printrange(current[0]+1, current[-1]); putchar('>');
  321. ++i;
  322. strs[i].beg = current[0]+1;
  323. --current;
  324. strs[i].end = current[0];
  325. adjust_pair(strs[i]);
  326. remove_cpp_comment(strs[i]);
  327. }
  328. ParseContext->currentStart = strs[0].beg;
  329. ParseContext->currentEnd = strs[0].end;
  330. ParseContext->doubleParent = double_par;
  331. Status = Callback(strs, i+1, Context, ParseContext);
  332. if (Status != ERROR_SUCCESS) {
  333. return Status;
  334. }
  335. }
  336. goto no_match;
  337. }
  338. // return ERROR_SUCCESS; // unreachable code
  339. }
  340. DWORD
  341. ScanForFunctionCalls(
  342. IN LPCSTR begin,
  343. IN LPCSTR end,
  344. IN EZPARSE_CALLBACK Callback,
  345. IN PVOID Context,
  346. IN OUT PEZPARSE_CONTEXT ParseContext
  347. )
  348. {
  349. return ScanForFunctionCallsEx(
  350. begin, end, Callback, Context,
  351. ParseContext, IGNORE_COMMENT);
  352. }
  353. DWORD
  354. EzGetLineNo(
  355. IN LPCSTR Ptr,
  356. IN OUT PEZPARSE_CONTEXT ParseContext
  357. )
  358. /*++
  359. Computes a line number based on
  360. an pointer within a buffer.
  361. Last known lineno/pointer is cached in ParseContext
  362. for performance
  363. */
  364. {
  365. int count = ParseContext->scannedLineCount;
  366. LPCSTR downto = ParseContext->lastScanned;
  367. LPCSTR p = Ptr;
  368. if (downto > p) {
  369. count = 1;
  370. downto = ParseContext->start;
  371. }
  372. while (p > downto) {
  373. if (*p == '\n') {
  374. ++count;
  375. }
  376. --p;
  377. }
  378. ParseContext->scannedLineCount = count;
  379. ParseContext->lastScanned = Ptr;
  380. return count;
  381. }
  382. const char begin_wpp[] = "begin_wpp";
  383. const char end_wpp[] = "end_wpp";
  384. const char define_[] = "#define";
  385. const char enum_[] = "enum ";
  386. enum {
  387. begin_wpp_size = (sizeof(begin_wpp)-1),
  388. end_wpp_size = (sizeof(end_wpp)-1),
  389. define_size = (sizeof(define_)-1),
  390. enum_size = (sizeof(enum_)-1),
  391. };
  392. typedef struct _SmartContext {
  393. EZPARSE_CALLBACK Callback;
  394. PVOID Context;
  395. OUT PEZPARSE_CONTEXT ParseContext;
  396. std::string buf;
  397. } SMART_CONTEXT, *PSMART_CONTEXT;
  398. void DoEnumItems(PSTR_PAIR name, LPCSTR begin, LPCSTR end, PSMART_CONTEXT ctx)
  399. {
  400. LPCSTR p,q;
  401. ULONG value = 0;
  402. STR_PAIR Item;
  403. BOOL First = TRUE;
  404. ctx->buf.assign("CUSTOM_TYPE(");
  405. ctx->buf.append(name->beg, name->end);
  406. ctx->buf.append(", ItemListLong");
  407. p = begin;
  408. while(begin < end && isspace(*--end)); // skip spaces
  409. if (begin < end && *end != ',') ++end;
  410. for(;p < end;) {
  411. Item.beg = p;
  412. q = p;
  413. for(;;) {
  414. if (q == end) {
  415. goto enum_end;
  416. }
  417. if (*q == ',' || *q == '}') {
  418. // valueless item. Use current
  419. Item.end = q;
  420. break;
  421. } else if (*q == '=') {
  422. // need to calc the value. Skip for now //
  423. Item.end = q;
  424. while (q < end && *q != ',') ++q;
  425. break;
  426. }
  427. ++q;
  428. }
  429. adjust_pair(Item);
  430. if (Item.beg == Item.end) {
  431. break;
  432. }
  433. if (First) {ctx->buf.append("("); First = FALSE;} else ctx->buf.append(",");
  434. ctx->buf.append(Item.beg, Item.end);
  435. if (q == end) break;
  436. p = q+1;
  437. ++value;
  438. }
  439. enum_end:;
  440. ctx->buf.append(") )");
  441. ScanForFunctionCallsEx(
  442. &ctx->buf[0], &ctx->buf[0] + ctx->buf.size(), ctx->Callback, ctx->Context,
  443. ctx->ParseContext, NO_SEMICOLON);
  444. Flood("enum %s\n", ctx->buf.c_str());
  445. }
  446. void DoEnum(LPCSTR begin, LPCSTR end, PSMART_CONTEXT Ctx)
  447. {
  448. LPCSTR p, q, current = begin;
  449. for(;;) {
  450. p = std::search(current, end, enum_, enum_ + enum_size);
  451. if (p == end) break;
  452. q = std::find(p, end, '{');
  453. if (q == end) break;
  454. // let's figure out enum name //
  455. STR_PAIR name;
  456. name.beg = p + enum_size;
  457. name.end = q;
  458. adjust_pair(name);
  459. if ( *name.beg == '_' ) ++name.beg;
  460. p = q+1; // past "{";
  461. q = std::find(p, end, '}');
  462. if (q == end) break;
  463. if (name.end > name.beg) {
  464. DoEnumItems(&name, p, q, Ctx);
  465. } else {
  466. ReportError("Cannot handle tagless enums yet");
  467. }
  468. current = q;
  469. }
  470. }
  471. DWORD
  472. SmartScan(
  473. IN LPCSTR begin,
  474. IN LPCSTR end,
  475. IN EZPARSE_CALLBACK Callback,
  476. IN PVOID Context,
  477. IN OUT PEZPARSE_CONTEXT ParseContext
  478. )
  479. {
  480. LPCSTR block_start, block_end, current = begin;
  481. SMART_CONTEXT Ctx;
  482. Ctx.Callback = Callback;
  483. Ctx.Context = Context;
  484. Ctx.ParseContext = ParseContext;
  485. for(;;) {
  486. block_start = std::search(current, end, begin_wpp, begin_wpp + begin_wpp_size);
  487. if (block_start == end) break;
  488. current = block_start;
  489. block_end = std::search(block_start, end, end_wpp, end_wpp + end_wpp_size);
  490. if (block_end == end) break;
  491. Flood("Block Found\n");
  492. // determine block type //
  493. // begin_wpp enum
  494. // begin_wpp config
  495. // begin_wpp func
  496. // begin_wpp define
  497. LPCSTR block_type = block_start + begin_wpp_size + 1;
  498. Flood("block_type = %c%c%c%c\n", block_type[0],block_type[1],block_type[2],block_type[3]);
  499. if (memcmp(block_type, "enum", 4) == 0) {
  500. // do enum block //
  501. DoEnum( block_type + 4, block_end, &Ctx );
  502. } else if (memcmp(block_type, "config", 6) == 0) {
  503. // do config block //
  504. ScanForFunctionCallsEx(block_type + 6, block_end, Callback, Context, ParseContext, IGNORE_POUND_COMMENT);
  505. } else if (memcmp(block_type, "func", 4) == 0) {
  506. LPCSTR func_start, func_end;
  507. current = block_type + 6;
  508. for(;;) {
  509. func_start = std::search(current, block_end, define_, define_ + define_size);
  510. if (func_start == block_end) break;
  511. func_start += define_size;
  512. while (isspace(*func_start)) {
  513. if(++func_start == block_end) goto no_func;
  514. }
  515. func_end = func_start;
  516. while (!isspace(*func_end)) {
  517. if(*func_end == '(') break;
  518. if(++func_end == block_end) goto no_func;
  519. }
  520. if(*func_end != '(') {
  521. Ctx.buf.assign(func_start, func_end);
  522. Ctx.buf.append("(MSGARGS)");
  523. } else {
  524. func_end = std::find(func_start, block_end, ')');
  525. if (func_end == block_end) break;
  526. ++func_end; // include ")"
  527. Ctx.buf.assign(func_start, func_end);
  528. }
  529. Flood("Func %s\n", Ctx.buf.c_str());
  530. ScanForFunctionCallsEx(
  531. Ctx.buf.begin(), Ctx.buf.end(), Callback, Context,
  532. ParseContext, NO_SEMICOLON);
  533. current = func_end;
  534. }
  535. no_func:;
  536. } else if (memcmp(block_type, "define", 6) == 0) {
  537. // do define block
  538. } else {
  539. ReportError("Unknown block");
  540. }
  541. current = block_end + end_wpp_size;
  542. }
  543. if (current == begin) {
  544. // file without marking, let's do default processing
  545. Unusual("Reverting back to plain scan\n");
  546. ScanForFunctionCalls(begin, end, Callback, Context, ParseContext);
  547. }
  548. return ERROR_SUCCESS;
  549. }
  550. DWORD
  551. EzParse(
  552. IN LPCSTR filename,
  553. IN EZPARSE_CALLBACK Callback,
  554. IN PVOID Context)
  555. {
  556. // return EzParseEx(filename, SmartScan, Callback, Context);
  557. return EzParseEx(filename, ScanForFunctionCalls, Callback, Context, IGNORE_POUND_COMMENT);
  558. }
  559. DWORD
  560. EzParseWithOptions(
  561. IN LPCSTR filename,
  562. IN EZPARSE_CALLBACK Callback,
  563. IN PVOID Context,
  564. IN DWORD Options)
  565. {
  566. return EzParseEx(filename, ScanForFunctionCalls, Callback, Context, Options);
  567. }
  568. DWORD
  569. EzParseEx(
  570. IN LPCSTR filename,
  571. IN PROCESSFILE_CALLBACK ProcessData,
  572. IN EZPARSE_CALLBACK Callback,
  573. IN PVOID Context,
  574. IN DWORD Options
  575. )
  576. {
  577. DWORD Status = ERROR_SUCCESS;
  578. HANDLE mapping;
  579. HANDLE file = CreateFileA(filename,
  580. GENERIC_READ, FILE_SHARE_READ, NULL,
  581. OPEN_EXISTING, 0, 0);
  582. if (file == INVALID_HANDLE_VALUE) {
  583. Status = GetLastError();
  584. ReportError("Cannot open file %s, error %u\n", filename, Status );
  585. return Status;
  586. }
  587. DWORD size = GetFileSize(file, 0);
  588. mapping = CreateFileMapping(file,0,PAGE_READONLY,0,0, 0);
  589. if (!mapping) {
  590. Status = GetLastError();
  591. ReportError("Cannot create mapping, error %u\n", Status );
  592. CloseHandle(file);
  593. return Status;
  594. }
  595. PCHAR buf = (PCHAR)MapViewOfFileEx(mapping, FILE_MAP_READ,0,0,0,0);
  596. if (buf) {
  597. EZPARSE_CONTEXT ParseContext;
  598. ZeroMemory(&ParseContext, sizeof(ParseContext) );
  599. ParseContext.start = buf;
  600. ParseContext.filename = filename;
  601. ParseContext.scannedLineCount = 1;
  602. ParseContext.lastScanned = buf;
  603. ParseContext.previousContext = EzParseCurrentContext;
  604. ParseContext.Options = Options;
  605. EzParseCurrentContext = &ParseContext;
  606. Status = (*ProcessData)(buf, buf + size, Callback, Context, &ParseContext);
  607. EzParseCurrentContext = ParseContext.previousContext;
  608. UnmapViewOfFile( buf );
  609. } else {
  610. Status = GetLastError();
  611. ReportError("MapViewOfFileEx failed, error %u\n", Status );
  612. }
  613. CloseHandle(mapping);
  614. CloseHandle(file);
  615. return Status;
  616. }
  617. DWORD
  618. EzParseResourceEx(
  619. IN LPCSTR ResName,
  620. IN PROCESSFILE_CALLBACK ProcessData,
  621. IN EZPARSE_CALLBACK Callback,
  622. IN PVOID Context)
  623. {
  624. DWORD Status = ERROR_SUCCESS;
  625. HRSRC hRsrc;
  626. hRsrc = FindResource(
  627. NULL, //this Module
  628. ResName,
  629. RT_RCDATA);
  630. if (hRsrc == NULL) {
  631. Status = GetLastError();
  632. ReportError("Cannot open resource %s, error %u\n", ResName, Status );
  633. return Status;
  634. }
  635. HGLOBAL hGlobal = LoadResource(NULL, hRsrc);
  636. if (!hGlobal) {
  637. Status = GetLastError();
  638. ReportError("LockResource failed, error %u\n", Status );
  639. return Status;
  640. }
  641. DWORD size = SizeofResource(NULL, hRsrc);
  642. PCHAR buf = (PCHAR)LockResource(hGlobal);
  643. if (buf) {
  644. EZPARSE_CONTEXT ParseContext;
  645. ZeroMemory(&ParseContext, sizeof(ParseContext) );
  646. ParseContext.start = buf;
  647. ParseContext.filename = ResName;
  648. ParseContext.scannedLineCount = 1;
  649. ParseContext.lastScanned = buf;
  650. ParseContext.previousContext = EzParseCurrentContext;
  651. EzParseCurrentContext = &ParseContext;
  652. Status = (*ProcessData)(buf, buf + size, Callback, Context, &ParseContext);
  653. EzParseCurrentContext = ParseContext.previousContext;
  654. } else {
  655. Status = GetLastError();
  656. ReportError("LockResource failed, error %u\n", Status );
  657. }
  658. // According to MSDN. There is no need to call Unlock/Free Resource
  659. return Status;
  660. }