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.

975 lines
20 KiB

  1. #include <windows.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <malloc.h>
  5. #include <assert.h>
  6. #include <imagehlp.h>
  7. #include <dbhpriv.h>
  8. #define MAX_STR 256
  9. #define WILD_UNDERSCORE 1
  10. #define SYM_BUFFER_SIZE (sizeof(IMAGEHLP_SYMBOL64) + MAX_SYM_NAME)
  11. typedef struct {
  12. char mask[MAX_STR];
  13. DWORD64 base;
  14. } ENUMSYMDATA, *PENUMSYMDATA;
  15. typedef enum
  16. {
  17. cmdQuit = 0,
  18. cmdHelp,
  19. cmdVerbose,
  20. cmdLoad,
  21. cmdUnload,
  22. cmdEnum,
  23. cmdName,
  24. cmdAddr,
  25. cmdBase,
  26. cmdNext,
  27. cmdPrev,
  28. cmdLine,
  29. cmdSymInfo,
  30. cmdDiaVer,
  31. cmdUndec,
  32. cmdFindFile,
  33. cmdEnumSrcFiles,
  34. cmdMax
  35. };
  36. typedef BOOL (*CMDPROC)(char *params);
  37. typedef struct _CMD
  38. {
  39. char token[MAX_STR + 1];
  40. char shorttoken[4];
  41. CMDPROC fn;
  42. } CMD, *PCMD;
  43. BOOL fnQuit(char *);
  44. BOOL fnHelp(char *);
  45. BOOL fnVerbose(char *);
  46. BOOL fnLoad(char *);
  47. BOOL fnUnload(char *);
  48. BOOL fnEnum(char *);
  49. BOOL fnName(char *);
  50. BOOL fnAddr(char *);
  51. BOOL fnBase(char *);
  52. BOOL fnNext(char *);
  53. BOOL fnPrev(char *);
  54. BOOL fnLine(char *);
  55. BOOL fnSymInfo(char *);
  56. BOOL fnDiaVer(char *);
  57. BOOL fnUndec(char *);
  58. BOOL fnFindFile(char *);
  59. BOOL fnEnumSrcFiles(char *);
  60. CMD gCmd[cmdMax] =
  61. {
  62. {"quit", "q", fnQuit},
  63. {"help", "h", fnHelp},
  64. {"verbose", "v", fnVerbose},
  65. {"load", "l", fnLoad},
  66. {"unload", "u", fnUnload},
  67. {"enum", "x", fnEnum},
  68. {"name", "n", fnName},
  69. {"addr", "a", fnAddr},
  70. {"base", "b", fnBase},
  71. {"next", "t", fnNext},
  72. {"prev", "v", fnPrev},
  73. {"line", "i", fnLine},
  74. {"sym" , "s", fnSymInfo},
  75. {"dia", "d", fnDiaVer},
  76. {"undec", "n", fnUndec},
  77. {"ff", "f", fnFindFile},
  78. {"src", "r", fnEnumSrcFiles}
  79. };
  80. char gModName[MAX_STR];
  81. char gImageName[MAX_STR];
  82. char gSymbolSearchPath[MAX_STR];
  83. DWORD64 gBase;
  84. DWORD64 gDefaultBase;
  85. DWORD64 gDefaultBaseForPDB;
  86. DWORD gOptions;
  87. HANDLE gHP;
  88. int
  89. WINAPIV
  90. dprintf(
  91. LPSTR Format,
  92. ...
  93. )
  94. {
  95. static char buf[1000] = "DBGHELP: ";
  96. va_list args;
  97. if ((gOptions & SYMOPT_DEBUG) == 0)
  98. return 1;
  99. va_start(args, Format);
  100. _vsnprintf(buf, sizeof(buf)-9, Format, args);
  101. va_end(args);
  102. printf(buf);
  103. return 1;
  104. }
  105. __inline int ucase(int c)
  106. {
  107. return (gOptions & SYMOPT_CASE_INSENSITIVE) ? toupper(c) : c;
  108. }
  109. void dumpsym(
  110. PIMAGEHLP_SYMBOL64 sym
  111. )
  112. {
  113. printf(" name : %s\n", sym->Name);
  114. printf(" addr : 0x%I64x\n", sym->Address);
  115. printf(" size : 0x%x\n", sym->Size);
  116. printf("flags : 0x%x\n", sym->Flags);
  117. }
  118. BOOL
  119. MatchPattern(
  120. char *sz,
  121. char *pattern
  122. )
  123. {
  124. char c, p, l;
  125. if (!*pattern)
  126. return TRUE;
  127. for (; ;) {
  128. p = *pattern++;
  129. p = (char)ucase(p);
  130. switch (p) {
  131. case 0: // end of pattern
  132. return *sz ? FALSE : TRUE; // if end of string TRUE
  133. case '*':
  134. while (*sz) { // match zero or more char
  135. if (MatchPattern (sz++, pattern)) {
  136. return TRUE;
  137. }
  138. }
  139. return MatchPattern (sz, pattern);
  140. case '?':
  141. if (*sz++ == 0) { // match any one char
  142. return FALSE; // not end of string
  143. }
  144. break;
  145. case WILD_UNDERSCORE:
  146. while (*sz == '_') {
  147. sz++;
  148. }
  149. break;
  150. case '[':
  151. if ( (c = *sz++) == 0) { // match char set
  152. return FALSE; // syntax
  153. }
  154. c = (CHAR)ucase(c);
  155. l = 0;
  156. while (p = *pattern++) {
  157. if (p == ']') { // if end of char set, then
  158. return FALSE; // no match found
  159. }
  160. if (p == '-') { // check a range of chars?
  161. p = *pattern; // get high limit of range
  162. if (p == 0 || p == ']') {
  163. return FALSE; // syntax
  164. }
  165. if (c >= l && c <= p) {
  166. break; // if in range, move on
  167. }
  168. }
  169. l = p;
  170. if (c == p) { // if char matches this element
  171. break; // move on
  172. }
  173. }
  174. while (p && p != ']') { // got a match in char set
  175. p = *pattern++; // skip to end of set
  176. }
  177. break;
  178. default:
  179. c = *sz++;
  180. if (ucase(c) != p) { // check for exact char
  181. return FALSE; // not a match
  182. }
  183. break;
  184. }
  185. }
  186. }
  187. BOOL
  188. cbEnumSymbols(
  189. PSYMBOL_INFO si,
  190. ULONG size,
  191. PVOID context
  192. )
  193. {
  194. PENUMSYMDATA esd = (PENUMSYMDATA)context;
  195. printf("0x%I64x : ", si->Address, si->Name);
  196. if (si->Flags & SYMF_FORWARDER)
  197. printf("%c ", 'F');
  198. else if (si->Flags & SYMF_EXPORT)
  199. printf("%c ", 'E');
  200. else
  201. printf(" ");
  202. printf("%s\n", si->Name);
  203. return TRUE;
  204. }
  205. BOOL
  206. cbEnumSym(
  207. PTSTR name,
  208. DWORD64 address,
  209. ULONG size,
  210. PVOID context
  211. )
  212. {
  213. PENUMSYMDATA esd = (PENUMSYMDATA)context;
  214. if (MatchPattern(name, esd->mask))
  215. printf("0x%I64x : %s\n", address, name);
  216. return TRUE;
  217. }
  218. BOOL
  219. cbSrcFiles(
  220. PSOURCEFILE pSourceFile,
  221. PVOID UserContext
  222. )
  223. {
  224. if (!pSourceFile)
  225. return FALSE;
  226. printf("%s\n", pSourceFile->FileName);
  227. return TRUE;
  228. }
  229. BOOL
  230. cbSymbol(
  231. HANDLE hProcess,
  232. ULONG ActionCode,
  233. ULONG64 CallbackData,
  234. ULONG64 UserContext
  235. )
  236. {
  237. PIMAGEHLP_DEFERRED_SYMBOL_LOAD64 idsl;
  238. PIMAGEHLP_CBA_READ_MEMORY prm;
  239. IMAGEHLP_MODULE64 mi;
  240. PUCHAR p;
  241. ULONG i;
  242. idsl = (PIMAGEHLP_DEFERRED_SYMBOL_LOAD64) CallbackData;
  243. switch ( ActionCode ) {
  244. case CBA_DEBUG_INFO:
  245. dprintf("%s", (LPSTR)CallbackData);
  246. break;
  247. #if 0
  248. case CBA_DEFERRED_SYMBOL_LOAD_CANCEL:
  249. if (fControlC)
  250. {
  251. fControlC = 0;
  252. return TRUE;
  253. }
  254. break;
  255. #endif
  256. case CBA_DEFERRED_SYMBOL_LOAD_START:
  257. dprintf("loading symbols for %s\n", gModName);
  258. break;
  259. case CBA_DEFERRED_SYMBOL_LOAD_FAILURE:
  260. if (idsl->FileName && *idsl->FileName)
  261. dprintf( "*** Error: could not load symbols for %s\n", idsl->FileName );
  262. else
  263. dprintf( "*** Error: could not load symbols [MODNAME UNKNOWN]\n");
  264. break;
  265. case CBA_DEFERRED_SYMBOL_LOAD_COMPLETE:
  266. dprintf("loaded symbols for %s\n", gModName);
  267. break;
  268. case CBA_SYMBOLS_UNLOADED:
  269. dprintf("unloaded symbols for %s\n", gModName);
  270. break;
  271. #if 0
  272. case CBA_READ_MEMORY:
  273. prm = (PIMAGEHLP_CBA_READ_MEMORY)CallbackData;
  274. return g_Target->ReadVirtual(prm->addr,
  275. prm->buf,
  276. prm->bytes,
  277. prm->bytesread) == S_OK;
  278. #endif
  279. default:
  280. return FALSE;
  281. }
  282. return FALSE;
  283. }
  284. PIMAGEHLP_SYMBOL64 SymbolFromName(char *param)
  285. {
  286. BOOL rc;
  287. PIMAGEHLP_SYMBOL64 sym;
  288. char name[MAX_STR];
  289. assert(name & *name);
  290. sym = malloc(SYM_BUFFER_SIZE);
  291. if (!sym)
  292. return FALSE;
  293. ZeroMemory(sym, SYM_BUFFER_SIZE);
  294. sym->MaxNameLength = MAX_SYM_NAME;
  295. sprintf(name, "%s!%s", gModName, param);
  296. rc = SymGetSymFromName64(gHP, name, sym);
  297. if (!rc) {
  298. free(sym);
  299. return NULL;
  300. }
  301. return sym;
  302. }
  303. BOOL fnQuit(char *param)
  304. {
  305. printf("goodbye\n");
  306. return FALSE;
  307. }
  308. BOOL fnHelp(char *param)
  309. {
  310. printf(" dbh commands :\n");
  311. printf(" help : prints this message\n");
  312. printf(" quit : quits this program\n");
  313. printf("verbose <on/off> : controls debug spew\n");
  314. printf(" load <modname> : loads the requested module\n");
  315. printf(" unload : unloads the current module\n");
  316. printf(" enum <mask> : enumerates all matching symbols\n");
  317. printf(" name <symname> : finds a symbol by it's name\n");
  318. printf(" addr <address> : finds a symbol by it's hex address\n");
  319. printf(" base <address> : sets the new default base address\n");
  320. printf(" next <add/nam> : finds the symbol after the passed sym\n");
  321. printf(" prev <add/nam> : finds the symbol before the passed sym\n");
  322. printf(" line <file:#> : finds the matching line number\n");
  323. printf(" sym : displays type and location of symbols\n");
  324. printf(" dia : displays the DIA version\n");
  325. printf("ff <path> <file> : finds file in path\n");
  326. printf(" src <mask> : lists source files\n");
  327. return TRUE;
  328. }
  329. BOOL fnVerbose(char *param)
  330. {
  331. int opts = gOptions;
  332. if (!param || !*param)
  333. printf("");
  334. else if (!_strcmpi(param, "on"))
  335. opts |= SYMOPT_DEBUG;
  336. else if (!_strcmpi(param, "off"))
  337. opts = gOptions & ~SYMOPT_DEBUG;
  338. else
  339. printf("verbose <on//off>\n");
  340. gOptions = SymSetOptions(opts);
  341. printf("verbose mode %s.\n", gOptions & SYMOPT_DEBUG ? "on" : "off");
  342. return TRUE;
  343. }
  344. BOOL fnLoad(char *param)
  345. {
  346. DWORD64 addr;
  347. char ext[MAX_STR];
  348. char mod[MAX_STR];
  349. if (!param || !*param || !strchr(param, '.'))
  350. {
  351. printf("load <modname> must specify a file to load symbols for.\n");
  352. return TRUE;
  353. }
  354. _splitpath(param, NULL, NULL, mod, ext);
  355. addr = 0;
  356. if (gDefaultBase)
  357. addr = gDefaultBase;
  358. else if (!_strcmpi(ext, ".pdb"))
  359. addr = gDefaultBaseForPDB;
  360. fnUnload(NULL);
  361. addr = SymLoadModule64(gHP,
  362. NULL, // hFile,
  363. param, // ImageName,
  364. mod, // ModuleName,
  365. addr, // BaseOfDll,
  366. 0x1000000); // SizeOfDll
  367. if (!addr)
  368. {
  369. printf("error 0x%x loading %s\n", GetLastError(), param);
  370. return TRUE;
  371. }
  372. if (gBase && !SymUnloadModule64(gHP, gBase))
  373. printf("error unloading %s at 0x%x\n", gModName, gBase);
  374. strcpy(gModName, mod);
  375. strcpy(gImageName, param);
  376. gBase = addr;
  377. return TRUE;
  378. }
  379. BOOL fnUnload(char *param)
  380. {
  381. if (!gBase)
  382. return TRUE;
  383. if (!SymUnloadModule64(gHP, gBase))
  384. printf("error unloading %s at 0x%x\n", gModName, gBase);
  385. gBase = 0;
  386. *gModName = 0;
  387. return TRUE;
  388. }
  389. BOOL fnEnum(char *param)
  390. {
  391. BOOL rc;
  392. ENUMSYMDATA esd;
  393. esd.base = gBase;
  394. strcpy(esd.mask, param ? param : "");
  395. rc = SymEnumSymbols(gHP, gBase, param, cbEnumSymbols, &esd);
  396. if (!rc)
  397. printf("error 0x%0 calling SymEnumerateSymbols()\n", GetLastError());
  398. return TRUE;
  399. }
  400. BOOL fnEnumSrcFiles(char *param)
  401. {
  402. BOOL rc;
  403. rc = SymEnumSourceFiles(gHP, gBase, param, cbSrcFiles, NULL);
  404. if (!rc)
  405. printf("error 0x%0 calling SymEnumSourceFiles()\n", GetLastError());
  406. return TRUE;
  407. }
  408. BOOL fnName(char *param)
  409. {
  410. BOOL rc;
  411. PIMAGEHLP_SYMBOL64 sym;
  412. if (!param || !*param)
  413. {
  414. printf("name <symbolname> - finds a symbol by it's name\n");
  415. return TRUE;
  416. }
  417. sym = SymbolFromName(param);
  418. if (!sym)
  419. return TRUE;
  420. dumpsym(sym);
  421. free(sym);
  422. return TRUE;
  423. }
  424. BOOL fnAddr(char *param)
  425. {
  426. BOOL rc;
  427. PIMAGEHLP_SYMBOL64 sym;
  428. DWORD64 addr;
  429. DWORD64 disp;
  430. char *p;
  431. addr = 0;
  432. if (param && *param)
  433. {
  434. p = param;
  435. if (*(p + 1) == 'x' || *(p + 1) == 'X')
  436. p += 2;
  437. sscanf(p, "%I64x", &addr);
  438. }
  439. if (!addr)
  440. {
  441. printf("addr <address> : finds a symbol by it's hex address\n");
  442. return TRUE;
  443. }
  444. sym = malloc(SYM_BUFFER_SIZE);
  445. if (!sym)
  446. return FALSE;
  447. ZeroMemory(sym, SYM_BUFFER_SIZE);
  448. sym->MaxNameLength = MAX_SYM_NAME;
  449. rc = SymGetSymFromAddr64(gHP, addr, &disp, sym);
  450. if (rc)
  451. {
  452. printf("%s", sym->Name);
  453. if (disp)
  454. printf("+0x%I64x", disp);
  455. printf("\n");
  456. dumpsym(sym);
  457. }
  458. free(sym);
  459. return TRUE;
  460. }
  461. BOOL fnNextPrev(int direction, char *param)
  462. {
  463. BOOL rc;
  464. PIMAGEHLP_SYMBOL64 sym;
  465. DWORD64 addr;
  466. char name[MAX_STR];
  467. char *p;
  468. addr = 0;
  469. if (param && *param)
  470. {
  471. p = param;
  472. if (*(p + 1) == 'x' || *(p + 1) == 'X')
  473. p += 2;
  474. sscanf(p, "%I64x", &addr);
  475. }
  476. if (!addr)
  477. {
  478. sym = SymbolFromName(param);
  479. if (!sym)
  480. return TRUE;
  481. addr = sym->Address;
  482. if (!addr) {
  483. free(sym);
  484. return TRUE;
  485. }
  486. }
  487. else
  488. {
  489. sym = malloc(SYM_BUFFER_SIZE);
  490. if (!sym)
  491. return FALSE;
  492. }
  493. if (direction > 0)
  494. rc = SymGetSymNext64(gHP, sym);
  495. else
  496. rc = SymGetSymPrev64(gHP, sym);
  497. if (rc)
  498. dumpsym(sym);
  499. free(sym);
  500. return TRUE;
  501. }
  502. BOOL fnNext(char *param)
  503. {
  504. return fnNextPrev(1, param);
  505. }
  506. BOOL fnPrev(char *param)
  507. {
  508. return fnNextPrev(-1, param);
  509. }
  510. BOOL fnBase(char *param)
  511. {
  512. BOOL rc;
  513. PIMAGEHLP_SYMBOL64 sym;
  514. DWORD64 addr;
  515. DWORD64 disp;
  516. char *p;
  517. addr = 0;
  518. if (param && *param)
  519. {
  520. p = param;
  521. if (*(p + 1) == 'x' || *(p + 1) == 'X')
  522. p += 2;
  523. sscanf(p, "%I64x", &addr);
  524. }
  525. if (!addr)
  526. {
  527. printf("base <address> : sets the base address for module loads\n");
  528. return TRUE;
  529. }
  530. gDefaultBase = addr;
  531. if (gBase)
  532. fnLoad(gImageName);
  533. return TRUE;
  534. }
  535. BOOL fnLine(char *param)
  536. {
  537. char *file;
  538. DWORD linenum;
  539. BOOL rc;
  540. IMAGEHLP_LINE64 line;
  541. LONG disp;
  542. if (!param || !*param)
  543. return TRUE;
  544. file = param;
  545. while (*param != ':') {
  546. if (!*param)
  547. return TRUE;
  548. param++;
  549. }
  550. *param++ = 0;
  551. linenum = atoi(param);
  552. if (!linenum)
  553. return TRUE;
  554. memset(&line, 0, sizeof(line));
  555. line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
  556. rc = SymGetLineFromName64(gHP,
  557. gModName,
  558. file,
  559. linenum,
  560. &disp,
  561. &line);
  562. if (!rc) {
  563. printf("line: error 0x%x looking for %s#%d\n",
  564. GetLastError(),
  565. file,
  566. linenum);
  567. return TRUE;
  568. }
  569. printf("file : %s\n", line.FileName);
  570. printf("line : %d\n", linenum);
  571. printf("addr : 0x%I64x\n", line.Address);
  572. printf("disp : 0x%x\n", disp);
  573. return TRUE;
  574. }
  575. BOOL fnSymInfo(char *param)
  576. {
  577. DBH_MODSYMINFO msi;
  578. if (!gBase)
  579. return TRUE;
  580. msi.function = dbhModSymInfo;
  581. msi.sizeofstruct = sizeof(msi);
  582. msi.addr = gBase;
  583. if (!dbghelp(gHP, (PVOID)&msi))
  584. printf("error grabbing symbol info\n");
  585. printf("%s: symtype=0x%x, src=%s\n", gModName, msi.type, msi.file);
  586. return TRUE;
  587. }
  588. BOOL fnDiaVer(char *param)
  589. {
  590. DBH_DIAVERSION dv;
  591. dv.function = dbhDiaVersion;
  592. dv.sizeofstruct = sizeof(dv);
  593. if (!dbghelp(0, (PVOID)&dv))
  594. printf("error grabbing dia version info\n");
  595. printf("DIA version 0x%x\n", dv.ver);
  596. return TRUE;
  597. }
  598. BOOL fnUndec(char *param)
  599. {
  600. DWORD rc;
  601. char uname[MAX_SYM_NAME + 1];
  602. if (!param || !*param)
  603. {
  604. printf("undec <symbolname> - undecorates a C++ mangled symbol name\n");
  605. return TRUE;
  606. }
  607. rc = UnDecorateSymbolName(param, uname, MAX_SYM_NAME, UNDNAME_COMPLETE);
  608. if (!rc) {
  609. printf("error 0x%u undecorating %s\n", GetLastError(), param);
  610. } else {
  611. printf("%s = %s\n", param, uname);
  612. }
  613. return TRUE;
  614. }
  615. BOOL fnFindFile(char *param)
  616. {
  617. DWORD rc;
  618. char root[MAX_PATH + 1];
  619. char file[MAX_PATH + 1];
  620. char found[MAX_PATH + 1];
  621. if (!param)
  622. {
  623. printf("ff <root path> <file name> - finds file in path\n");
  624. return TRUE;
  625. }
  626. sscanf(param, "%s %s", root, file);
  627. if (!*root || !*file)
  628. {
  629. printf("ff <root path> <file name> - finds file in path\n");
  630. return TRUE;
  631. }
  632. *found = 0;
  633. rc = SearchTreeForFile(root, file, found);
  634. if (!rc) {
  635. printf("error 0x%u looking for %s\n", GetLastError(), file);
  636. } else {
  637. printf("found %s\n", found);
  638. }
  639. return TRUE;
  640. }
  641. char *GetParameters(char *cmd)
  642. {
  643. char *p = cmd;
  644. char *param = NULL;
  645. while (*p++)
  646. {
  647. if (isspace(*p))
  648. {
  649. *p++ = 0;
  650. return *p ? p : NULL;
  651. }
  652. }
  653. return NULL;
  654. }
  655. void prompt()
  656. {
  657. if (!*gModName)
  658. printf("dbh: ");
  659. else
  660. printf("%s [0x%I64x]: ", gModName, gBase);
  661. }
  662. int InputLoop()
  663. {
  664. char cmd[MAX_STR + 1];
  665. char *params;
  666. int i;
  667. BOOL rc;
  668. printf("\n");
  669. do
  670. {
  671. prompt();
  672. gets(cmd);
  673. params = GetParameters(cmd);
  674. // printf("cmd[%s] params[%s]\n", cmd, params);
  675. for (i = 0; i < cmdMax; i++)
  676. {
  677. if (!_strcmpi(cmd, gCmd[i].token) ||
  678. !_strcmpi(cmd, gCmd[i].shorttoken))
  679. break;
  680. }
  681. if (i == cmdMax)
  682. {
  683. printf("[%s] is an unrecognized command.\n", cmd);
  684. rc = TRUE;
  685. continue;
  686. }
  687. else
  688. rc = gCmd[i].fn(params);
  689. } while (rc);
  690. return 0;
  691. }
  692. BOOL init()
  693. {
  694. int i;
  695. BOOL rc;
  696. *gModName = 0;
  697. gBase = 0;;
  698. gDefaultBaseForPDB = 0x1000000;
  699. printf("dbh: initializing...\n");
  700. i = GetEnvironmentVariable("_NT_SYMBOL_PATH", gSymbolSearchPath, MAX_STR);
  701. if (i < 1)
  702. *gSymbolSearchPath = 0;
  703. printf("Symbol Path = [%s]\n", gSymbolSearchPath);
  704. gHP = GetCurrentProcess();
  705. rc = SymInitialize(gHP, gSymbolSearchPath, FALSE);
  706. if (!rc)
  707. {
  708. printf("error 0x%x from SymInitialize()\n", GetLastError());
  709. return rc;
  710. }
  711. gOptions = SymSetOptions(SYMOPT_CASE_INSENSITIVE | SYMOPT_UNDNAME | SYMOPT_NO_CPP | SYMOPT_LOAD_LINES);
  712. printf("SymOpts = 0x%x\n", gOptions);
  713. rc = SymRegisterCallback64(gHP, cbSymbol, 0);
  714. if (!rc)
  715. {
  716. printf("error 0x%x from SymRegisterCallback64()\n", GetLastError());
  717. return rc;
  718. }
  719. return rc;
  720. }
  721. void cleanup()
  722. {
  723. fnUnload(NULL);
  724. SymCleanup(gHP);
  725. }
  726. BOOL cmdline(int argc, char *argv[])
  727. {
  728. int i;
  729. char *p;
  730. for (i = 1; i < argc; i++)
  731. {
  732. p = argv[i];
  733. switch (*p)
  734. {
  735. case '/':
  736. case '-':
  737. p++;
  738. switch (tolower(*p))
  739. {
  740. case 'v':
  741. fnVerbose("on");
  742. break;
  743. default:
  744. printf("%s is an unknown switch\n", argv[i]);
  745. break;
  746. }
  747. break;
  748. default:
  749. fnLoad(argv[i]);
  750. break;
  751. }
  752. }
  753. return TRUE;
  754. }
  755. #include <crtdbg.h>
  756. __cdecl
  757. main(
  758. int argc,
  759. char *argv[],
  760. char *envp[]
  761. )
  762. {
  763. DWORD rc;
  764. _CrtSetDbgFlag( ( _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF ) | _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG ) );
  765. if (!init())
  766. return 1;
  767. cmdline(argc, argv);
  768. rc = InputLoop();
  769. cleanup();
  770. _CrtDumpMemoryLeaks();
  771. return rc;
  772. }