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.

1682 lines
40 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. appdiff.c
  5. Abstract:
  6. Implements a stub tool that is designed to run with Win9x-side
  7. upgrade code.
  8. Author:
  9. Jim Schmidt (jimschm) 26-Feb-1998
  10. Revision History:
  11. <alias> <date> <comments>
  12. --*/
  13. #include "pch.h"
  14. #define S_FILES TEXT("Files")
  15. #define S_REG TEXT("Reg")
  16. #define S_INIFILES TEXT("IniFiles")
  17. #define S_EXCLUDE TEXT("Exclude")
  18. #define S_PATHS TEXT("Paths")
  19. #define S_REGISTRY TEXT("Registry")
  20. #define S_SUBSTITUTIONS TEXT("Substitutions")
  21. #define S_SRC TEXT("Src")
  22. #define S_DEST TEXT("Dest")
  23. #define S_ADDED TEXT("Added")
  24. #define S_CHANGED TEXT("Changed")
  25. #define S_ZERO TEXT("0")
  26. typedef struct {
  27. BOOL SnapMode;
  28. BOOL DiffMode;
  29. BOOL CheckBits;
  30. PCTSTR SnapFile;
  31. PCTSTR AppFile;
  32. PCTSTR Name;
  33. PCTSTR OutputFile;
  34. PCTSTR RegRoot;
  35. PCTSTR FileSysRoot;
  36. BOOL UseAppDiffInf;
  37. BOOL NoRoots;
  38. BOOL QuietMode;
  39. } OPTIONS, *POPTIONS;
  40. BOOL g_Quiet;
  41. BOOL g_Thorough;
  42. typedef struct {
  43. DWORD dwFileAttributes;
  44. FILETIME ftCreationTime;
  45. FILETIME ftLastWriteTime;
  46. DWORD nFileSizeHigh;
  47. DWORD nFileSizeLow;
  48. } FILEINFO, *PFILEINFO;
  49. BOOL
  50. DoSnapMode (
  51. POPTIONS Options
  52. );
  53. BOOL
  54. DoDiffMode (
  55. POPTIONS Options
  56. );
  57. HANDLE g_hHeap;
  58. HINSTANCE g_hInst;
  59. BOOL
  60. WINAPI
  61. MigUtil_Entry (
  62. HINSTANCE hInstance,
  63. DWORD dwReason,
  64. LPVOID lpReserved
  65. );
  66. BOOL
  67. WINAPI
  68. MemDb_Entry (
  69. HINSTANCE hInstance,
  70. DWORD dwReason,
  71. LPVOID lpReserved
  72. );
  73. BOOL
  74. Init (
  75. VOID
  76. )
  77. {
  78. HINSTANCE hInstance;
  79. DWORD dwReason;
  80. PVOID lpReserved;
  81. //
  82. // Simulate DllMain
  83. //
  84. hInstance = GetModuleHandle (NULL);
  85. dwReason = DLL_PROCESS_ATTACH;
  86. lpReserved = NULL;
  87. g_hInst = hInstance;
  88. g_hHeap = GetProcessHeap();
  89. MigUtil_Entry (
  90. hInstance,
  91. dwReason,
  92. lpReserved
  93. );
  94. MemDb_Entry (
  95. hInstance,
  96. dwReason,
  97. lpReserved
  98. );
  99. return TRUE;
  100. }
  101. VOID
  102. Terminate (
  103. VOID
  104. )
  105. {
  106. HINSTANCE hInstance;
  107. DWORD dwReason;
  108. PVOID lpReserved;
  109. //
  110. // Simulate DllMain
  111. //
  112. hInstance = GetModuleHandle (NULL);
  113. dwReason = DLL_PROCESS_DETACH;
  114. lpReserved = NULL;
  115. MemDb_Entry (
  116. hInstance,
  117. dwReason,
  118. lpReserved
  119. );
  120. MigUtil_Entry (
  121. hInstance,
  122. dwReason,
  123. lpReserved
  124. );
  125. }
  126. VOID
  127. HelpAndExit (
  128. VOID
  129. )
  130. {
  131. printf ("Command line syntax:\n\n"
  132. "appdiff -s[:snapfile] [-r:<regroot>] [-f:<fileroot>]\n"
  133. "appdiff -d[:snapfile] [-a:appfilelist] [-n:name] [-o:outfile]\n"
  134. "appdiff -s[:snapfile] -d [-n:name] [-o:outfile] [-r:<regroot>]\n"
  135. " [-f:<fileroot>]\n"
  136. "\n"
  137. "-s Specifies snapshot mode, where snapfile is the name of\n"
  138. " the memdb output file, and is snap.dat by default.\n"
  139. "\n"
  140. "-d Specifies diff mode, where snapfile is the name of a\n"
  141. " previously generated snapshot file, and is snap.dat by\n"
  142. " default.\n"
  143. "\n"
  144. "-a Specifies the application file list, as generated by\n"
  145. " migfiles.exe.\n"
  146. "\n"
  147. "-n Specifies the application section name, and the default\n"
  148. " is Application.\n"
  149. "\n"
  150. "-o Specifies the name of the output INF fragment, and the\n"
  151. " default is output.inf\n"
  152. "\n"
  153. "-r Specifies a registry root to compare. If specified,\n"
  154. " only the registry is scanned, unless -f is also specified.\n"
  155. "\n"
  156. "-f Specifies a file system root to compare. If specified,\n"
  157. " only the file system is scanned, unless -r is also\n"
  158. " specified.\n"
  159. "\n"
  160. "Additional Options:"
  161. "\n"
  162. "-q Quiet mode -- disables stderr output.\n"
  163. "-u Use appdiff.inf and output.inf (for generation of uninstall\n"
  164. " sections)\n"
  165. "-t Thorough checks (computes checksums for all data)\n"
  166. "\n"
  167. "APPDIFF.INF specifies the registry and file system roots to scan on\n"
  168. "a per-app basis, and is used to generate uninstall sections for\n"
  169. "migdb.inf. See \\\\jimschm-dev\\team\\tools\\appdiff.inf for info.\n"
  170. "\n"
  171. "OUTPUT.INF is generated by this tool, and is designed to be cut &\n"
  172. "pasted into migdb.inf.\n"
  173. );
  174. exit(0);
  175. }
  176. BOOL
  177. pParseCommandLine (
  178. IN INT ArgCount,
  179. IN PTSTR ArgArray[],
  180. OUT POPTIONS Options
  181. )
  182. {
  183. INT i;
  184. ZeroMemory (Options, sizeof (OPTIONS));
  185. Options->NoRoots = TRUE;
  186. for (i = 0 ; i < ArgCount ; i++) {
  187. if (ArgArray[i][0] == TEXT('-') || ArgArray[i][0] == TEXT('/')) {
  188. switch (_totlower (ArgArray[i][1])) {
  189. case TEXT('s'):
  190. Options->SnapMode = TRUE;
  191. if (ArgArray[i][2] == TEXT(':')) {
  192. if (Options->SnapFile) {
  193. return FALSE;
  194. }
  195. Options->SnapFile = &ArgArray[i][3];
  196. if (Options->SnapFile[0] == 0) {
  197. return FALSE;
  198. }
  199. }
  200. break;
  201. case TEXT('t'):
  202. if (g_Thorough) {
  203. return FALSE;
  204. }
  205. Options->CheckBits = TRUE;
  206. g_Thorough = TRUE;
  207. break;
  208. case TEXT('d'):
  209. Options->DiffMode = TRUE;
  210. if (ArgArray[i][2] == TEXT(':')) {
  211. if (Options->SnapFile) {
  212. return FALSE;
  213. }
  214. Options->SnapFile = &ArgArray[i][3];
  215. if (Options->SnapFile[0] == 0) {
  216. return FALSE;
  217. }
  218. }
  219. break;
  220. case TEXT('r'):
  221. Options->NoRoots = FALSE;
  222. if (Options->RegRoot) {
  223. return FALSE;
  224. }
  225. if (ArgArray[i][2] == TEXT(':')) {
  226. Options->RegRoot = &ArgArray[i][3];
  227. } else if (i + 1 < ArgCount) {
  228. i++;
  229. Options->RegRoot = ArgArray[i];
  230. } else {
  231. Options->RegRoot = &ArgArray[i][2];
  232. }
  233. if (Options->RegRoot[0] == 0) {
  234. return FALSE;
  235. }
  236. break;
  237. case TEXT('f'):
  238. Options->NoRoots = FALSE;
  239. if (Options->FileSysRoot) {
  240. return FALSE;
  241. }
  242. if (ArgArray[i][2] == TEXT(':')) {
  243. Options->FileSysRoot = &ArgArray[i][3];
  244. } else if (i + 1 < ArgCount) {
  245. i++;
  246. Options->FileSysRoot = ArgArray[i];
  247. } else {
  248. Options->FileSysRoot = &ArgArray[i][2];
  249. }
  250. if (Options->FileSysRoot[0] == 0) {
  251. return FALSE;
  252. }
  253. break;
  254. case TEXT('q'):
  255. if (g_Quiet) {
  256. return FALSE;
  257. }
  258. Options->QuietMode = TRUE;
  259. g_Quiet = TRUE;
  260. break;
  261. case TEXT('u'):
  262. if (Options->UseAppDiffInf) {
  263. return FALSE;
  264. }
  265. Options->UseAppDiffInf = TRUE;
  266. break;
  267. case TEXT('a'):
  268. if (Options->AppFile) {
  269. return FALSE;
  270. }
  271. if (ArgArray[i][2] == TEXT(':')) {
  272. Options->AppFile = &ArgArray[i][3];
  273. } else if (i + 1 < ArgCount) {
  274. i++;
  275. Options->AppFile = ArgArray[i];
  276. } else {
  277. Options->AppFile = &ArgArray[i][2];
  278. }
  279. if (Options->AppFile[0] == 0) {
  280. return FALSE;
  281. }
  282. break;
  283. case TEXT('n'):
  284. if (Options->Name) {
  285. return FALSE;
  286. }
  287. if (ArgArray[i][2] == TEXT(':')) {
  288. Options->Name = &ArgArray[i][3];
  289. } else if (i + 1 < ArgCount) {
  290. i++;
  291. Options->Name = ArgArray[i];
  292. } else {
  293. Options->Name = &ArgArray[i][2];
  294. }
  295. if (Options->Name[0] == 0) {
  296. return FALSE;
  297. }
  298. break;
  299. case TEXT('o'):
  300. if (Options->OutputFile) {
  301. return FALSE;
  302. }
  303. if (ArgArray[i][2] == TEXT(':')) {
  304. Options->OutputFile = &ArgArray[i][3];
  305. } else if (i + 1 < ArgCount) {
  306. i++;
  307. Options->OutputFile = ArgArray[i];
  308. } else {
  309. Options->OutputFile = &ArgArray[i][2];
  310. }
  311. if (Options->OutputFile[0] == 0) {
  312. return FALSE;
  313. }
  314. break;
  315. default:
  316. return FALSE;
  317. }
  318. }
  319. else {
  320. return FALSE;
  321. }
  322. }
  323. if (!Options->SnapMode && !Options->DiffMode) {
  324. return FALSE;
  325. }
  326. if (!Options->SnapFile) {
  327. Options->SnapFile = TEXT("snap.dat");
  328. }
  329. if (!Options->OutputFile && Options->UseAppDiffInf) {
  330. Options->OutputFile = TEXT("output.inf");
  331. }
  332. if (!Options->Name) {
  333. Options->Name = TEXT("Application");
  334. }
  335. if (!g_Quiet) {
  336. _ftprintf (stderr, TEXT("Snap file: %s\n"), Options->SnapFile);
  337. if (Options->OutputFile) {
  338. _ftprintf (stderr, TEXT("Output file: %s\n"), Options->OutputFile);
  339. }
  340. _ftprintf (stderr, TEXT("Thorough checks: %s\n"), Options->CheckBits ? "ENABLED" : "DISABLED");
  341. _ftprintf (stderr, TEXT("Application Name: %s\n\n"), Options->Name);
  342. }
  343. return TRUE;
  344. }
  345. INT
  346. __cdecl
  347. _tmain (
  348. INT argc,
  349. TCHAR *argv[]
  350. )
  351. {
  352. OPTIONS Options;
  353. if (!pParseCommandLine (argc - 1, &argv[1], &Options)) {
  354. HelpAndExit();
  355. }
  356. if (!Init()) {
  357. printf ("Unable to initialize!\n");
  358. return 255;
  359. }
  360. //
  361. // Snap Mode: Gather the directory, registry and INI files
  362. //
  363. if (Options.SnapMode) {
  364. DoSnapMode (&Options);
  365. }
  366. //
  367. // Diff Mode: Gather another snapshot, then compare against
  368. // original
  369. //
  370. if (Options.DiffMode) {
  371. if (Options.SnapMode) {
  372. _ftprintf (stderr, TEXT("Do your thing, then hit Enter.\n"));
  373. getchar();
  374. _ftprintf (stderr, TEXT("\n"));
  375. }
  376. DoDiffMode (&Options);
  377. }
  378. Terminate();
  379. return 0;
  380. }
  381. BOOL
  382. pCompareData (
  383. IN PCBYTE Src,
  384. IN PCBYTE Dest,
  385. IN UINT Size
  386. )
  387. {
  388. PCWSTR p, q;
  389. if (Size >= sizeof (WCHAR)) {
  390. p = (PCWSTR) (Src + Size - sizeof (WCHAR));
  391. q = (PCWSTR) (Dest + Size - sizeof (WCHAR));
  392. if (*p == 0 && *q == 0) {
  393. if (StringIMatchW ((PCWSTR) Src, (PCWSTR) Dest)) {
  394. return TRUE;
  395. }
  396. return FALSE;
  397. }
  398. }
  399. return memcmp (Src, Dest, Size) == 0;
  400. }
  401. VOID
  402. pSetMemDbKey (
  403. IN BOOL DiffMode,
  404. IN PCTSTR Group,
  405. IN PCTSTR Key,
  406. IN PBYTE Data,
  407. IN DWORD DataSize
  408. )
  409. {
  410. PCBYTE OrgData;
  411. DWORD OrgSize;
  412. TCHAR Node[MEMDB_MAX];
  413. wsprintf (Node, TEXT("%s\\%s"), S_EXCLUDE, Key);
  414. if (MemDbGetValue (Node, NULL)) {
  415. return;
  416. }
  417. if (!DiffMode) {
  418. MemDbSetBinaryValueEx (
  419. S_ZERO,
  420. Group,
  421. Key,
  422. Data,
  423. DataSize,
  424. NULL
  425. );
  426. }
  427. else {
  428. //
  429. // Compare against original data
  430. //
  431. wsprintf (Node, TEXT("0\\%s\\%s"), Group, Key);
  432. OrgData = MemDbGetBinaryValue (Node, &OrgSize);
  433. if (!OrgData) {
  434. //
  435. // Data has been added
  436. //
  437. wsprintf (Node, TEXT("%s\\%s\\%s"), S_ADDED, Group, Key);
  438. MemDbSetValue (Node, 0);
  439. }
  440. else {
  441. //
  442. // Delete memdb key, so remaining items will provide list of data
  443. // that was deleted.
  444. //
  445. if (OrgSize != DataSize || !pCompareData (OrgData, Data, DataSize)) {
  446. //
  447. // Data has changed
  448. //
  449. MemDbDeleteValue (Node);
  450. wsprintf (Node, TEXT("%s\\%s\\%s"), S_CHANGED, Group, Key);
  451. MemDbSetValue (Node, 0);
  452. } else {
  453. //
  454. // Data has not changed
  455. //
  456. MemDbDeleteValue (Node);
  457. }
  458. }
  459. }
  460. }
  461. VOID
  462. pConvertWin32FindData (
  463. IN PWIN32_FIND_DATA Data,
  464. OUT PFILEINFO Info
  465. )
  466. {
  467. Info->dwFileAttributes = Data->dwFileAttributes;
  468. Info->ftCreationTime = Data->ftCreationTime;
  469. Info->ftLastWriteTime = Data->ftLastWriteTime;
  470. Info->nFileSizeHigh = Data->nFileSizeHigh;
  471. Info->nFileSizeLow = Data->nFileSizeLow;
  472. }
  473. VOID
  474. pSetRegDataAndFreePtrs (
  475. BOOL DiffMode,
  476. PBYTE Data, OPTIONAL
  477. PBYTE Data2, OPTIONAL
  478. DWORD Size,
  479. PCTSTR Key,
  480. PCTSTR Value OPTIONAL
  481. )
  482. {
  483. PCTSTR Node;
  484. if (Data2) {
  485. Node = CreateEncodedRegistryString (Key, Value);
  486. pSetMemDbKey (
  487. DiffMode,
  488. S_REG,
  489. Node,
  490. Data2,
  491. Size
  492. );
  493. FreeEncodedRegistryString (Node);
  494. if (Data) {
  495. MemFree (g_hHeap, 0, Data);
  496. }
  497. MemFree (g_hHeap, 0, Data2);
  498. }
  499. }
  500. BOOL
  501. pRegSnap (
  502. BOOL DiffMode,
  503. PCTSTR Root
  504. )
  505. {
  506. REGTREE_ENUM Reg;
  507. REGVALUE_ENUM RegVal;
  508. PBYTE Data;
  509. PBYTE Data2;
  510. DWORD Size = 0;
  511. TCHAR SkipTree[MEMDB_MAX];
  512. TCHAR TempNode[MEMDB_MAX];
  513. UINT SkipTreeBytes = 0;
  514. SkipTree[0] = 0;
  515. wsprintf (TempNode, TEXT("%s\\%s"), S_EXCLUDE, Root);
  516. if (MemDbGetValue (TempNode, NULL)) {
  517. return TRUE;
  518. }
  519. if (!g_Quiet) {
  520. _ftprintf (stderr, TEXT("Taking snapshot of %s\n"), Root);
  521. }
  522. if (EnumFirstRegKeyInTree (&Reg, Root)) {
  523. do {
  524. //
  525. // Key/key tree exclude processing
  526. //
  527. if (SkipTree[0]) {
  528. if (StringIMatchByteCount (SkipTree, Reg.FullKeyName, SkipTreeBytes)) {
  529. continue;
  530. }
  531. SkipTree[0] = 0;
  532. }
  533. wsprintf (TempNode, TEXT("%s\\%s"), S_EXCLUDE, Reg.FullKeyName);
  534. if (MemDbGetValue (TempNode, NULL)) {
  535. StringCopy (SkipTree, Reg.FullKeyName);
  536. SkipTreeBytes = ByteCount (SkipTree);
  537. continue;
  538. }
  539. //
  540. // Non-excluded key
  541. //
  542. Data = NULL;
  543. if (EnumFirstRegValue (&RegVal, Reg.CurrentKey->KeyHandle)) {
  544. do {
  545. Data = GetRegValueData (RegVal.KeyHandle, RegVal.ValueName);
  546. Data2 = NULL;
  547. if (Data) {
  548. Size = RegVal.DataSize + sizeof (DWORD);
  549. Data2 = MemAlloc (g_hHeap, 0, Size);
  550. MYASSERT (Data2);
  551. CopyMemory ((PDWORD) Data2 + 1, Data, RegVal.DataSize);
  552. *((PDWORD) Data2) = RegVal.Type;
  553. }
  554. pSetRegDataAndFreePtrs (DiffMode, Data, Data2, Size, Reg.FullKeyName, RegVal.ValueName);
  555. } while (EnumNextRegValue (&RegVal));
  556. } else {
  557. Size = sizeof (DWORD);
  558. Data2 = MemAlloc (g_hHeap, 0, Size);
  559. MYASSERT (Data2);
  560. *((PDWORD) Data2) = 0xffffffff;
  561. pSetRegDataAndFreePtrs (DiffMode, Data, Data2, Size, Reg.FullKeyName, RegVal.ValueName);
  562. }
  563. } while (EnumNextRegKeyInTree (&Reg));
  564. }
  565. return TRUE;
  566. }
  567. DWORD
  568. pComputeChecksum (
  569. PCTSTR FullPath
  570. )
  571. {
  572. HANDLE File;
  573. HANDLE Map;
  574. PBYTE Data;
  575. UINT Size;
  576. UINT u;
  577. DWORD Checksum = 0;
  578. Data = MapFileIntoMemory (FullPath, &File, &Map);
  579. if (!Data) {
  580. return 0xFFFFFFFF;
  581. }
  582. Size = GetFileSize (File, NULL);
  583. for (u = 0 ; u < Size ; u++) {
  584. Checksum = _rotl (Checksum, 3);
  585. Checksum ^= Data[u];
  586. }
  587. UnmapFile (Data, Map, File);
  588. return Checksum;
  589. }
  590. BOOL
  591. pDirAndIniSnap (
  592. BOOL DiffMode,
  593. PCTSTR Root
  594. )
  595. {
  596. TREE_ENUM Dir;
  597. PCTSTR p, q, r;
  598. TCHAR SectionNames[32768];
  599. TCHAR KeyNames[32768];
  600. TCHAR KeyValue[4096];
  601. TCHAR Node[MEMDB_MAX];
  602. TCHAR ExcludeNode[MEMDB_MAX];
  603. UINT Count;
  604. FILEINFO fi;
  605. TCHAR SkipTree[MEMDB_MAX];
  606. UINT SkipTreeBytes = 0;
  607. DWORD Checksum;
  608. SkipTree[0] = 0;
  609. wsprintf (ExcludeNode, TEXT("%s\\%s"), S_EXCLUDE, Root);
  610. if (MemDbGetValue (ExcludeNode, NULL)) {
  611. return TRUE;
  612. }
  613. //
  614. // Take a snapshot of all dirs in drive specified by Root
  615. //
  616. if (!g_Quiet) {
  617. _ftprintf (stderr, TEXT("Taking snapshot of %s\n"), Root);
  618. }
  619. if (EnumFirstFileInTree (&Dir, Root, NULL, TRUE)) {
  620. do {
  621. //
  622. // Exclude processing
  623. //
  624. if (SkipTree[0]) {
  625. if (StringIMatchByteCount (SkipTree, Dir.FullPath, SkipTreeBytes)) {
  626. continue;
  627. }
  628. SkipTree[0] = 0;
  629. }
  630. if (Dir.Directory) {
  631. wsprintf (ExcludeNode, TEXT("%s\\%s"), S_EXCLUDE, Dir.FullPath);
  632. if (MemDbGetValue (ExcludeNode, NULL)) {
  633. StringCopy (SkipTree, Dir.FullPath);
  634. AppendWack (SkipTree);
  635. SkipTreeBytes = ByteCount (SkipTree);
  636. continue;
  637. }
  638. }
  639. //
  640. // Non-excluded file
  641. //
  642. if (g_Thorough) {
  643. Checksum = pComputeChecksum (Dir.FullPath);
  644. pSetMemDbKey (
  645. DiffMode,
  646. S_FILES,
  647. Dir.FullPath,
  648. (PBYTE) &Checksum,
  649. sizeof (Checksum)
  650. );
  651. } else {
  652. pConvertWin32FindData (Dir.FindData, &fi);
  653. pSetMemDbKey (
  654. DiffMode,
  655. S_FILES,
  656. Dir.FullPath,
  657. (PBYTE) &fi,
  658. sizeof (FILEINFO)
  659. );
  660. }
  661. p = _tcsrchr (Dir.Name, TEXT('.'));
  662. if (p) {
  663. p = _tcsinc (p);
  664. if (StringIMatch (p, TEXT("INI"))) {
  665. //
  666. // Found INI file, take a snapshot of it
  667. //
  668. if (!g_Quiet) {
  669. _ftprintf (stderr, TEXT(" Taking snapshot of %s\n"), Dir.FullPath);
  670. }
  671. Count = GetPrivateProfileString (NULL, NULL, TEXT("\0"), SectionNames, 32768, Dir.FullPath);
  672. SectionNames[Count] = 0;
  673. SectionNames[Count + 1] = 0;
  674. p = SectionNames;
  675. while (*p) {
  676. //
  677. // Filter out dup sections
  678. //
  679. r = SectionNames;
  680. while (r < p) {
  681. if (StringIMatch (p, r)) {
  682. break;
  683. }
  684. r = GetEndOfString (r) + 1;
  685. }
  686. if (r < p) {
  687. if (!g_Quiet) {
  688. _ftprintf (stderr, TEXT(" ***Duplicate section ignored: [%s]\n"), p);
  689. }
  690. p = GetEndOfString (p) + 1;
  691. continue;
  692. }
  693. //
  694. // Process each key in the section
  695. //
  696. Count = GetPrivateProfileString (
  697. p,
  698. NULL,
  699. TEXT("\0"),
  700. KeyNames,
  701. 32768,
  702. Dir.FullPath
  703. );
  704. KeyNames[Count] = 0;
  705. KeyNames[Count + 1] = 0;
  706. q = KeyNames;
  707. while (*q) {
  708. //
  709. // Ignore duplicate value names
  710. //
  711. r = KeyNames;
  712. while (r < q) {
  713. if (StringIMatch (q, r)) {
  714. break;
  715. }
  716. r = GetEndOfString (r) + 1;
  717. }
  718. if (r < q) {
  719. if (!g_Quiet) {
  720. _ftprintf (stderr, TEXT(" ***Duplicate key ignored: [%s] %s\n"), p, q);
  721. }
  722. q = GetEndOfString (q) + 1;
  723. continue;
  724. }
  725. GetPrivateProfileString (
  726. p,
  727. q,
  728. TEXT(""),
  729. KeyValue,
  730. 4096,
  731. Dir.FullPath
  732. );
  733. wsprintf (Node, TEXT("%s\\[%s]\\%s"), Dir.FullPath, p, q);
  734. pSetMemDbKey (
  735. DiffMode,
  736. S_INIFILES,
  737. Node,
  738. (PBYTE) KeyValue,
  739. ByteCount (KeyValue) + sizeof (TCHAR)
  740. );
  741. q = GetEndOfString (q) + 1;
  742. }
  743. p = GetEndOfString (p) + 1;
  744. }
  745. }
  746. }
  747. } while (EnumNextFileInTree (&Dir));
  748. }
  749. return TRUE;
  750. }
  751. VOID
  752. pCreateSubst (
  753. IN PCTSTR Src,
  754. IN PCTSTR Dest
  755. )
  756. {
  757. DWORD Offset;
  758. MemDbSetValueEx (S_SUBSTITUTIONS, S_DEST, Dest, NULL, 0, &Offset);
  759. MemDbSetValueEx (S_SUBSTITUTIONS, S_SRC, Src, NULL, Offset, NULL);
  760. }
  761. BOOL
  762. pTakeSnapshot (
  763. POPTIONS Options,
  764. BOOL DiffMode
  765. )
  766. {
  767. HINF Inf;
  768. INFSTRUCT is = INITINFSTRUCT_POOLHANDLE;
  769. TCHAR Path[MAX_TCHAR_PATH];
  770. PTSTR p, q;
  771. TCHAR Section[256];
  772. UINT Dirs = 0;
  773. UINT RegRoots = 0;
  774. TCHAR WinDir[MAX_TCHAR_PATH];
  775. TCHAR SystemDir[MAX_TCHAR_PATH];
  776. TCHAR System32Dir[MAX_TCHAR_PATH];
  777. TCHAR SystemDrive[8];
  778. TCHAR ProgramFilesDir[MAX_TCHAR_PATH];
  779. GetWindowsDirectory (WinDir, MAX_TCHAR_PATH);
  780. StringCopy (SystemDir, WinDir);
  781. StringCopy (AppendWack (SystemDir), TEXT("system"));
  782. StringCopy (System32Dir, SystemDir);
  783. StringCat (System32Dir, TEXT("32"));
  784. SystemDrive[0] = SystemDir[0];
  785. SystemDrive[1] = TEXT(':');
  786. SystemDrive[2] = 0;
  787. StringCopy (ProgramFilesDir, SystemDrive);
  788. StringCopy (AppendWack (ProgramFilesDir), TEXT("Program Files"));
  789. pCreateSubst (WinDir, TEXT("%%WINDIR%%"));
  790. pCreateSubst (SystemDir, TEXT("%%SYSTEMDIR%%"));
  791. pCreateSubst (System32Dir, TEXT("%%SYSTEM32DIR%%"));
  792. pCreateSubst (SystemDrive, TEXT("%%SYSTEMDRIVE%%"));
  793. pCreateSubst (ProgramFilesDir, TEXT("%%PROGRAMFILES%%"));
  794. if (Options->UseAppDiffInf) {
  795. GetModuleFileName (NULL, Path, MAX_TCHAR_PATH);
  796. p = _tcsrchr (Path, TEXT('\\'));
  797. MYASSERT (p);
  798. StringCopy (p + 1, TEXT("appdiff.inf"));
  799. Inf = InfOpenInfFile (Path);
  800. } else {
  801. Inf = INVALID_HANDLE_VALUE;
  802. }
  803. if (Inf == INVALID_HANDLE_VALUE) {
  804. //
  805. // Take snapshot of file system and INI files
  806. //
  807. if (Options->FileSysRoot) {
  808. pDirAndIniSnap (DiffMode, Options->FileSysRoot);
  809. } else if (Options->NoRoots) {
  810. pDirAndIniSnap (DiffMode, TEXT("C:\\"));
  811. }
  812. //
  813. // Take snapshot of registry
  814. //
  815. if (Options->RegRoot) {
  816. pRegSnap (DiffMode, Options->RegRoot);
  817. } else if (Options->NoRoots) {
  818. pRegSnap (DiffMode, TEXT("HKLM"));
  819. pRegSnap (DiffMode, TEXT("HKU"));
  820. }
  821. }
  822. else {
  823. //
  824. // Fill in the [Exclude] section
  825. //
  826. if (InfFindFirstLine (Inf, S_EXCLUDE, NULL, &is)) {
  827. do {
  828. p = InfGetLineText (&is);
  829. MemDbSetValueEx (S_EXCLUDE, p, NULL, NULL, 0, NULL);
  830. } while (InfFindNextLine (&is));
  831. }
  832. InfResetInfStruct (&is);
  833. if (Options->Name) {
  834. wsprintf (Section, TEXT("%s.%s"), Options->Name, S_EXCLUDE);
  835. if (InfFindFirstLine (Inf, Section, NULL, &is)) {
  836. do {
  837. p = InfGetLineText (&is);
  838. MemDbSetValueEx (S_EXCLUDE, p, NULL, NULL, 0, NULL);
  839. } while (InfFindNextLine (&is));
  840. }
  841. InfResetInfStruct (&is);
  842. }
  843. //
  844. // Fill in the [Substitutions] section
  845. //
  846. if (InfFindFirstLine (Inf, S_SUBSTITUTIONS, NULL, &is)) {
  847. do {
  848. p = InfGetStringField (&is, 0);
  849. q = InfGetStringField (&is, 1);
  850. pCreateSubst (p, q);
  851. } while (InfFindNextLine (&is));
  852. }
  853. InfResetInfStruct (&is);
  854. if (Options->Name) {
  855. wsprintf (Section, TEXT("%s.%s"), Options->Name, S_EXCLUDE);
  856. if (InfFindFirstLine (Inf, Section, NULL, &is)) {
  857. do {
  858. p = InfGetLineText (&is);
  859. MemDbSetValueEx (S_EXCLUDE, p, NULL, NULL, 0, NULL);
  860. } while (InfFindNextLine (&is));
  861. }
  862. InfResetInfStruct (&is);
  863. }
  864. //
  865. // Enumerate the [Paths] section, use c:\ by default
  866. //
  867. if (InfFindFirstLine (Inf, S_PATHS, NULL, &is)) {
  868. do {
  869. p = InfGetLineText (&is);
  870. pDirAndIniSnap (DiffMode, p);
  871. Dirs++;
  872. } while (InfFindNextLine (&is));
  873. InfResetInfStruct (&is);
  874. }
  875. if (Options->Name) {
  876. wsprintf (Section, TEXT("%s.%s"), Options->Name, S_PATHS);
  877. if (InfFindFirstLine (Inf, Section, NULL, &is)) {
  878. do {
  879. p = InfGetLineText (&is);
  880. pDirAndIniSnap (DiffMode, p);
  881. Dirs++;
  882. } while (InfFindNextLine (&is));
  883. }
  884. InfResetInfStruct (&is);
  885. }
  886. if (!Dirs) {
  887. pDirAndIniSnap (DiffMode, TEXT("C:\\"));
  888. }
  889. //
  890. // Enumerate the [Registry] section, use HKLM and HKU by default
  891. //
  892. if (InfFindFirstLine (Inf, S_REGISTRY, NULL, &is)) {
  893. do {
  894. p = InfGetLineText (&is);
  895. pRegSnap (DiffMode, p);
  896. RegRoots++;
  897. } while (InfFindNextLine (&is));
  898. InfResetInfStruct (&is);
  899. }
  900. if (Options->Name) {
  901. wsprintf (Section, TEXT("%s.%s"), Options->Name, S_REGISTRY);
  902. if (InfFindFirstLine (Inf, Section, NULL, &is)) {
  903. do {
  904. p = InfGetLineText (&is);
  905. pRegSnap (DiffMode, p);
  906. RegRoots++;
  907. } while (InfFindNextLine (&is));
  908. InfResetInfStruct (&is);
  909. }
  910. }
  911. if (!RegRoots) {
  912. pRegSnap (DiffMode, TEXT("HKLM"));
  913. pRegSnap (DiffMode, TEXT("HKU"));
  914. }
  915. InfCloseInfFile (Inf);
  916. }
  917. InfCleanUpInfStruct (&is);
  918. return TRUE;
  919. }
  920. PCTSTR
  921. pPerformSubstitution (
  922. PGROWLIST EnvVars,
  923. PCTSTR OrgStr
  924. )
  925. {
  926. PCTSTR PathStr;
  927. PCTSTR NewPathString;
  928. UINT Count;
  929. UINT u;
  930. PCTSTR Src;
  931. PCTSTR Dest;
  932. Count = GrowListGetSize (EnvVars);
  933. PathStr = DuplicatePathString (OrgStr, 0);
  934. MYASSERT (PathStr);
  935. for (u = 0 ; u < Count ; u += 2) {
  936. Src = GrowListGetString (EnvVars, u);
  937. Dest = GrowListGetString (EnvVars, u + 1);
  938. NewPathString = StringSearchAndReplace (PathStr, Src, Dest);
  939. if (NewPathString) {
  940. FreePathString (PathStr);
  941. PathStr = NewPathString;
  942. }
  943. }
  944. return PathStr;
  945. }
  946. VOID
  947. pCreateEnvVars (
  948. PGROWLIST EnvVars
  949. )
  950. {
  951. MEMDB_ENUM e;
  952. TCHAR Dest[MEMDB_MAX];
  953. UINT Count;
  954. UINT u;
  955. UINT Len;
  956. //
  957. // Enumerate source strings
  958. //
  959. Count = 0;
  960. if (MemDbGetValueEx (&e, S_SUBSTITUTIONS, S_SRC, NULL)) {
  961. do {
  962. MemDbBuildKeyFromOffset (e.dwValue, Dest, 2, NULL);
  963. Len = ByteCount (e.szName);
  964. for (u = 0 ; u < Count ; u += 2) {
  965. if (ByteCount (GrowListGetString (EnvVars, u)) < Len) {
  966. break;
  967. }
  968. }
  969. if (u < Count) {
  970. GrowListInsertString (EnvVars, u, e.szName);
  971. GrowListInsertString (EnvVars, u + 1, Dest);
  972. } else {
  973. GrowListAppendString (EnvVars, e.szName);
  974. GrowListAppendString (EnvVars, Dest);
  975. }
  976. Count += 2;
  977. } while (MemDbEnumNextValue (&e));
  978. }
  979. }
  980. VOID
  981. pDecodeRegStr (
  982. IN PCTSTR RegStr,
  983. OUT PTSTR Key,
  984. OUT PCTSTR *ValuePtr
  985. )
  986. {
  987. PTSTR p;
  988. PCTSTR Val = NULL;
  989. StringCopy (Key, RegStr);
  990. p = _tcschr (Key, TEXT('['));
  991. if (p) {
  992. Val = _tcsinc (p);
  993. p = _tcsdec2 (Key, p);
  994. while (p) {
  995. if (_tcsnextc (p) != TEXT(' ')) {
  996. break;
  997. }
  998. p = _tcsdec2 (Key, p);
  999. }
  1000. *p = 0;
  1001. }
  1002. *ValuePtr = Val;
  1003. }
  1004. BOOL
  1005. pAreAllValuesInMemDb (
  1006. IN PCTSTR RegStr,
  1007. IN BOOL Encoded,
  1008. IN HKEY KeyHandle OPTIONAL
  1009. )
  1010. {
  1011. BOOL WeOpen = FALSE;
  1012. REGVALUE_ENUM e;
  1013. TCHAR Key[MAX_REGISTRY_KEY];
  1014. PCTSTR Value;
  1015. BOOL b = TRUE;
  1016. //
  1017. // If encoded, decode first.
  1018. //
  1019. if (Encoded) {
  1020. pDecodeRegStr (RegStr, Key, &Value);
  1021. } else {
  1022. StringCopy (Key, RegStr);
  1023. Value = NULL;
  1024. }
  1025. //
  1026. // If key not open, open now
  1027. //
  1028. if (!KeyHandle) {
  1029. KeyHandle = OpenRegKeyStr (Key);
  1030. WeOpen = TRUE;
  1031. if (!KeyHandle) {
  1032. return TRUE;
  1033. }
  1034. }
  1035. //
  1036. // if there is at least one value remaining, fail
  1037. //
  1038. b = !EnumFirstRegValue (&e, KeyHandle);
  1039. if (WeOpen) {
  1040. CloseRegKey (KeyHandle);
  1041. }
  1042. return b;
  1043. }
  1044. BOOL
  1045. pIsEntireSubKeyGone (
  1046. IN PCTSTR RegStr,
  1047. IN BOOL Encoded
  1048. )
  1049. {
  1050. TCHAR Key[MAX_REGISTRY_KEY];
  1051. PCTSTR Value;
  1052. HKEY KeyHandle;
  1053. //
  1054. // If encoded, decode now
  1055. //
  1056. if (Encoded) {
  1057. pDecodeRegStr (RegStr, Key, &Value);
  1058. } else {
  1059. StringCopy (Key, RegStr);
  1060. Value = NULL;
  1061. }
  1062. //
  1063. // Open key
  1064. //
  1065. KeyHandle = OpenRegKeyStr (Key);
  1066. if (!KeyHandle) {
  1067. return TRUE;
  1068. }
  1069. CloseRegKey (KeyHandle);
  1070. return FALSE;
  1071. }
  1072. VOID
  1073. pAppendThingsToDelete (
  1074. POPTIONS Options,
  1075. HANDLE File
  1076. )
  1077. {
  1078. MEMDB_ENUM e;
  1079. PCTSTR p;
  1080. GROWLIST EnvVars = GROWLIST_INIT;
  1081. TCHAR SkipKey[MEMDB_MAX];
  1082. UINT SkipKeyBytes = 0;
  1083. BOOL RegFlag;
  1084. BOOL AppendStar;
  1085. BOOL RemoveVal;
  1086. PCTSTR OutLine;
  1087. TCHAR KeyBuf[MAX_REGISTRY_KEY];
  1088. PCTSTR DontCare;
  1089. SkipKey[0] = 0;
  1090. //
  1091. // Generate substitution mapping
  1092. //
  1093. pCreateEnvVars (&EnvVars);
  1094. //
  1095. // Write section name
  1096. //
  1097. if (!Options->Name) {
  1098. return;
  1099. }
  1100. WriteFileString (File, TEXT("["));
  1101. WriteFileString (File, Options->Name);
  1102. WriteFileString (File, TEXT("]\r\n"));
  1103. //
  1104. // Write all the things in the deleted key
  1105. //
  1106. if (MemDbGetValueEx (&e, S_ZERO, NULL, NULL)) {
  1107. do {
  1108. p = _tcschr (e.szName, TEXT('\\'));
  1109. MYASSERT (p);
  1110. if (StringIMatchAB (S_REG, e.szName, p)) {
  1111. RegFlag = TRUE;
  1112. } else {
  1113. RegFlag = FALSE;
  1114. }
  1115. //
  1116. // Skip if this node is a subkey of a deleted key
  1117. //
  1118. p = _tcsinc (p);
  1119. if (SkipKey[0]) {
  1120. if (StringIMatchByteCount (SkipKey, p, SkipKeyBytes)) {
  1121. continue;
  1122. }
  1123. SkipKey[0] = 0;
  1124. }
  1125. RemoveVal = FALSE;
  1126. AppendStar = FALSE;
  1127. OutLine = p;
  1128. if (RegFlag) {
  1129. //
  1130. // If this is a registry key, and everything in
  1131. // the registry key has been deleted, then
  1132. // just write the one key with a star after it.
  1133. //
  1134. if (pIsEntireSubKeyGone (p, TRUE)) {
  1135. RemoveVal = TRUE;
  1136. AppendStar = TRUE;
  1137. }
  1138. //
  1139. // If it's a registry key, and all the subvalues
  1140. // are deleted, then just write the one key, but
  1141. // without a star.
  1142. //
  1143. else if (pAreAllValuesInMemDb (p, TRUE, NULL)) {
  1144. RemoveVal = TRUE;
  1145. }
  1146. }
  1147. //
  1148. // The value spec needs to be removed from the reg key
  1149. //
  1150. if (RemoveVal) {
  1151. pDecodeRegStr (p, KeyBuf, &DontCare);
  1152. OutLine = CreateEncodedRegistryString (KeyBuf, NULL);
  1153. //
  1154. // Workaround: CreateEncodedRegistryString always appends
  1155. // an asterisk, and we want to control when the asterisk
  1156. // appears.
  1157. //
  1158. p = _tcsrchr (OutLine, TEXT('*'));
  1159. if (p && p[1] == 0) {
  1160. p = _tcsdec2 (OutLine, p);
  1161. if (p) {
  1162. *((PTSTR) p) = 0;
  1163. }
  1164. }
  1165. //
  1166. // If this entire key is going to be deleted, then
  1167. // turn on SkipKey so the memdb nodes will be skipped.
  1168. //
  1169. if (AppendStar && SkipKey[0] == 0) {
  1170. StringCopy (SkipKey, OutLine);
  1171. AppendWack (SkipKey);
  1172. SkipKeyBytes = ByteCount (SkipKey);
  1173. }
  1174. }
  1175. //
  1176. // Perform substitution on the string
  1177. //
  1178. p = pPerformSubstitution (&EnvVars, OutLine);
  1179. MYASSERT (p);
  1180. if (RemoveVal) {
  1181. FreeEncodedRegistryString (OutLine);
  1182. }
  1183. //
  1184. // Write the file/reg key to the file
  1185. //
  1186. WriteFileString (File, p);
  1187. if (AppendStar) {
  1188. WriteFileString (File, TEXT("\\*"));
  1189. }
  1190. WriteFileString (File, TEXT("\r\n"));
  1191. FreePathString (p);
  1192. } while (MemDbEnumNextValue (&e));
  1193. }
  1194. //
  1195. // Write blank line at the end
  1196. //
  1197. WriteFileString (File, TEXT("\r\n"));
  1198. FreeGrowList (&EnvVars);
  1199. }
  1200. BOOL
  1201. pDumpDiffs (
  1202. VOID
  1203. )
  1204. {
  1205. MEMDB_ENUM e;
  1206. BOOL Changes = FALSE;
  1207. if (MemDbGetValueEx (&e, S_ZERO, NULL, NULL)) {
  1208. _tprintf (TEXT("Deleted Items:\n"));
  1209. Changes = TRUE;
  1210. do {
  1211. _tprintf (TEXT(" %s\n"), e.szName);
  1212. } while (MemDbEnumNextValue (&e));
  1213. }
  1214. if (MemDbGetValueEx (&e, S_ADDED, NULL, NULL)) {
  1215. _tprintf (TEXT("Added Items:\n"));
  1216. do {
  1217. _tprintf (TEXT(" %s\n"), e.szName);
  1218. } while (MemDbEnumNextValue (&e));
  1219. }
  1220. if (MemDbGetValueEx (&e, S_CHANGED, NULL, NULL)) {
  1221. _tprintf (TEXT("Changed Items:\n"));
  1222. do {
  1223. _tprintf (TEXT(" %s\n"), e.szName);
  1224. } while (MemDbEnumNextValue (&e));
  1225. }
  1226. return Changes;
  1227. }
  1228. BOOL
  1229. pGenerateInf (
  1230. POPTIONS Options
  1231. )
  1232. {
  1233. HANDLE File;
  1234. BOOL DelChanges;
  1235. //
  1236. // Dump changes to stdout
  1237. //
  1238. DelChanges = pDumpDiffs();
  1239. if (Options->OutputFile) {
  1240. //
  1241. // Write a section to our output file
  1242. //
  1243. File = CreateFile (
  1244. Options->OutputFile,
  1245. GENERIC_WRITE,
  1246. 0,
  1247. NULL,
  1248. CREATE_ALWAYS,
  1249. FILE_ATTRIBUTE_NORMAL,
  1250. NULL
  1251. );
  1252. if (File == INVALID_HANDLE_VALUE) {
  1253. _ftprintf (stderr, TEXT("Cannot generate %s, error %u\n"), Options->OutputFile, GetLastError());
  1254. return FALSE;
  1255. }
  1256. if (DelChanges) {
  1257. pAppendThingsToDelete (Options, File);
  1258. }
  1259. CloseHandle (File);
  1260. }
  1261. return TRUE;
  1262. }
  1263. BOOL
  1264. DoSnapMode (
  1265. POPTIONS Options
  1266. )
  1267. {
  1268. DWORD Start;
  1269. Start = GetTickCount();
  1270. if (!pTakeSnapshot (Options, FALSE)) {
  1271. return FALSE;
  1272. }
  1273. MemDbSave (Options->SnapFile);
  1274. if (!g_Quiet) {
  1275. _ftprintf (stderr, TEXT("Run time: %u seconds\n"), (GetTickCount() - Start) / 1000);
  1276. }
  1277. return TRUE;
  1278. }
  1279. BOOL
  1280. DoDiffMode (
  1281. POPTIONS Options
  1282. )
  1283. {
  1284. DWORD Start;
  1285. Start = GetTickCount();
  1286. if (GetFileAttributes (Options->SnapFile) == 0xffffffff) {
  1287. _ftprintf (stderr, TEXT("Bogus file arg: %s\n"), Options->SnapFile);
  1288. }
  1289. if (!Options->SnapMode) {
  1290. MemDbLoad (Options->SnapFile);
  1291. }
  1292. if (!pTakeSnapshot (Options, TRUE)) {
  1293. return FALSE;
  1294. }
  1295. pGenerateInf (Options);
  1296. if (!g_Quiet) {
  1297. _ftprintf (stderr, TEXT("Run time: %u seconds\n"), (GetTickCount() - Start) / 1000);
  1298. }
  1299. return TRUE;
  1300. }