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.

903 lines
25 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. showinst.c
  5. Abstract:
  6. This program compares the actions described by two Installation Modification Log file
  7. created by the INSTALER program
  8. Author:
  9. Steve Wood (stevewo) 15-Jan-1996
  10. Revision History:
  11. --*/
  12. #include "instutil.h"
  13. #include "iml.h"
  14. BOOLEAN VerboseOutput;
  15. BOOLEAN
  16. CompareIml(
  17. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  18. PINSTALLATION_MODIFICATION_LOGFILE pIml2
  19. );
  20. int
  21. __cdecl
  22. main(
  23. int argc,
  24. char *argv[]
  25. )
  26. {
  27. char *s;
  28. PWSTR ImlPathAlt = NULL;
  29. PINSTALLATION_MODIFICATION_LOGFILE pIml1 = NULL;
  30. PINSTALLATION_MODIFICATION_LOGFILE pIml2 = NULL;
  31. InitCommonCode( "COMPINST",
  32. "InstallationName2 [-v]",
  33. "-v verbose output\n"
  34. );
  35. VerboseOutput = FALSE;
  36. while (--argc) {
  37. s = *++argv;
  38. if (*s == '-' || *s == '/') {
  39. while (*++s) {
  40. switch( tolower( *s ) ) {
  41. case 'v':
  42. VerboseOutput = TRUE;
  43. break;
  44. default:
  45. CommonSwitchProcessing( &argc, &argv, *s );
  46. break;
  47. }
  48. }
  49. }
  50. else
  51. if (!CommonArgProcessing( &argc, &argv )) {
  52. if (ImlPathAlt != NULL) {
  53. Usage( "Too many installation names specified - '%s'", (ULONG)s );
  54. }
  55. ImlPathAlt = FormatImlPath( InstalerDirectory, GetArgAsUnicode( s ) );
  56. }
  57. }
  58. if (ImlPath == NULL || ImlPathAlt == NULL) {
  59. Usage( "Must specify two installation names to compare", 0 );
  60. }
  61. if (!SetCurrentDirectory( InstalerDirectory )) {
  62. FatalError( "Unable to change to '%ws' directory (%u)",
  63. (ULONG)InstalerDirectory,
  64. GetLastError()
  65. );
  66. }
  67. pIml1 = LoadIml( ImlPath );
  68. if (pIml1 == NULL) {
  69. FatalError( "Unable to load '%ws' (%u)",
  70. (ULONG)ImlPath,
  71. GetLastError()
  72. );
  73. }
  74. pIml2 = LoadIml( ImlPathAlt );
  75. if (pIml2 == NULL) {
  76. FatalError( "Unable to load '%ws' (%u)",
  77. (ULONG)ImlPathAlt,
  78. GetLastError()
  79. );
  80. }
  81. printf( "Displaying differences between:\n" );
  82. printf( " Installation 1: %ws\n", ImlPath );
  83. printf( " Installation 2: %ws\n", ImlPathAlt );
  84. exit( CompareIml( pIml1, pIml2 ) == FALSE );
  85. return 0;
  86. }
  87. typedef struct _IML_GENERIC_RECORD {
  88. POFFSET Next; // IML_GENERIC_RECORD
  89. ULONG Action;
  90. POFFSET Name; // WCHAR
  91. POFFSET Records; // IML_GENERIC_RECORD
  92. } IML_GENERIC_RECORD, *PIML_GENERIC_RECORD;
  93. typedef
  94. VOID
  95. (*PIML_PRINT_RECORD_ROUTINE)(
  96. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  97. PIML_GENERIC_RECORD pGeneric,
  98. PWSTR Parents[],
  99. ULONG Depth,
  100. ULONG i
  101. );
  102. typedef
  103. BOOLEAN
  104. (*PIML_COMPARE_CONTENTS_ROUTINE)(
  105. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  106. PIML_GENERIC_RECORD pGeneric1,
  107. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  108. PIML_GENERIC_RECORD pGeneric2,
  109. PWSTR Parents[]
  110. );
  111. PINSTALLATION_MODIFICATION_LOGFILE pSortIml;
  112. int
  113. __cdecl
  114. CompareGeneric(
  115. const void *Reference1,
  116. const void *Reference2
  117. )
  118. {
  119. PIML_GENERIC_RECORD p1 = *(PIML_GENERIC_RECORD *)Reference1;
  120. PIML_GENERIC_RECORD p2 = *(PIML_GENERIC_RECORD *)Reference2;
  121. if (p1->Name == 0) {
  122. if (p2->Name == 0) {
  123. return 0;
  124. }
  125. else {
  126. return -1;
  127. }
  128. }
  129. else
  130. if (p2->Name == 0) {
  131. return 1;
  132. }
  133. return _wcsicmp( MP( PWSTR, pSortIml, p1->Name ),
  134. MP( PWSTR, pSortIml, p2->Name )
  135. );
  136. }
  137. PIML_GENERIC_RECORD *
  138. GetSortedGenericListAsArray(
  139. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  140. PIML_GENERIC_RECORD pGeneric
  141. )
  142. {
  143. PIML_GENERIC_RECORD p, *pp;
  144. ULONG n;
  145. p = pGeneric;
  146. n = 1;
  147. while (p != NULL) {
  148. n += 1;
  149. p = MP( PIML_GENERIC_RECORD, pIml, p->Next );
  150. }
  151. pp = HeapAlloc( GetProcessHeap(), 0, n * sizeof( *pp ) );
  152. if (pp == NULL) {
  153. printf ("Memory allocation failure\n");
  154. ExitProcess (0);
  155. }
  156. p = pGeneric;
  157. n = 0;
  158. while (p != NULL) {
  159. pp[ n++ ] = p;
  160. p = MP( PIML_GENERIC_RECORD, pIml, p->Next );
  161. }
  162. pp[ n ] = NULL;
  163. pSortIml = pIml;
  164. qsort( (void *)pp, n, sizeof( *pp ), CompareGeneric );
  165. pSortIml = NULL;
  166. return pp;
  167. }
  168. BOOLEAN
  169. CompareGenericIml(
  170. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  171. PIML_GENERIC_RECORD pGeneric1,
  172. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  173. PIML_GENERIC_RECORD pGeneric2,
  174. PWSTR Parents[],
  175. ULONG Depth,
  176. PIML_PRINT_RECORD_ROUTINE PrintRecordRoutine,
  177. PIML_COMPARE_CONTENTS_ROUTINE CompareContentsRoutine
  178. )
  179. {
  180. PVOID pBufferToFree1;
  181. PVOID pBufferToFree2;
  182. PIML_GENERIC_RECORD *ppGeneric1;
  183. PIML_GENERIC_RECORD *ppGeneric2;
  184. PIML_GENERIC_RECORD pShow1;
  185. PIML_GENERIC_RECORD pShow2;
  186. BOOLEAN Result = FALSE;
  187. PWSTR s1, s2;
  188. int cmpResult;
  189. ppGeneric1 = GetSortedGenericListAsArray( pIml1, pGeneric1 );
  190. if (ppGeneric1 == NULL) {
  191. return FALSE;
  192. }
  193. pBufferToFree1 = ppGeneric1;
  194. ppGeneric2 = GetSortedGenericListAsArray( pIml2, pGeneric2 );
  195. if (ppGeneric2 == NULL) {
  196. HeapFree( GetProcessHeap(), 0, pBufferToFree1 );
  197. return FALSE;
  198. }
  199. pBufferToFree2 = ppGeneric2;
  200. pGeneric1 = *ppGeneric1++;
  201. pGeneric2 = *ppGeneric2++;
  202. while (TRUE) {
  203. pShow1 = NULL;
  204. pShow2 = NULL;
  205. if (pGeneric1 == NULL) {
  206. if (pGeneric2 == NULL) {
  207. break;
  208. }
  209. //
  210. // pGeneric2 is new
  211. //
  212. pShow2 = pGeneric2;
  213. pGeneric2 = *ppGeneric2++;
  214. Result = FALSE;
  215. }
  216. else
  217. if (pGeneric2 == NULL) {
  218. //
  219. // pGeneric1 is new
  220. //
  221. pShow1 = pGeneric1;
  222. pGeneric1 = *ppGeneric1++;
  223. Result = FALSE;
  224. }
  225. else {
  226. s1 = MP( PWSTR, pIml1, pGeneric1->Name );
  227. s2 = MP( PWSTR, pIml2, pGeneric2->Name );
  228. if (s1 == NULL) {
  229. if (s2 == NULL) {
  230. cmpResult = 0;
  231. }
  232. else {
  233. cmpResult = -1;
  234. }
  235. }
  236. else
  237. if (s2 == NULL) {
  238. cmpResult = 1;
  239. }
  240. else {
  241. cmpResult = _wcsicmp( s1, s2 );
  242. }
  243. if (cmpResult == 0) {
  244. if (Depth > 1) {
  245. Parents[ Depth - 1 ] = MP( PWSTR, pIml1, pGeneric1->Name );
  246. Result = CompareGenericIml( pIml1,
  247. MP( PIML_GENERIC_RECORD, pIml1, pGeneric1->Records ),
  248. pIml2,
  249. MP( PIML_GENERIC_RECORD, pIml2, pGeneric2->Records ),
  250. Parents,
  251. Depth - 1,
  252. PrintRecordRoutine,
  253. CompareContentsRoutine
  254. );
  255. }
  256. else {
  257. Result = (*CompareContentsRoutine)( pIml1, pGeneric1,
  258. pIml2, pGeneric2,
  259. Parents
  260. );
  261. }
  262. pGeneric1 = *ppGeneric1++;
  263. pGeneric2 = *ppGeneric2++;
  264. }
  265. else
  266. if (cmpResult > 0) {
  267. pShow2 = pGeneric2;
  268. pGeneric2 = *ppGeneric2++;
  269. }
  270. else {
  271. pShow1 = pGeneric1;
  272. pGeneric1 = *ppGeneric1++;
  273. }
  274. }
  275. if (pShow1) {
  276. (*PrintRecordRoutine)( pIml1, pShow1, Parents, Depth, 1 );
  277. }
  278. if (pShow2) {
  279. (*PrintRecordRoutine)( pIml2, pShow2, Parents, Depth, 2 );
  280. }
  281. }
  282. HeapFree( GetProcessHeap(), 0, pBufferToFree1 );
  283. HeapFree( GetProcessHeap(), 0, pBufferToFree2 );
  284. return Result;
  285. }
  286. char *FileActionStrings[] = {
  287. "CreateNewFile",
  288. "ModifyOldFile",
  289. "DeleteOldFile",
  290. "RenameOldFile",
  291. "ModifyFileDateTime",
  292. "ModifyFileAttributes"
  293. };
  294. PWSTR
  295. FormatFileTime(
  296. LPFILETIME LastWriteTime
  297. )
  298. {
  299. FILETIME LocalFileTime;
  300. SYSTEMTIME DateTime;
  301. static WCHAR DateTimeBuffer[ 128 ];
  302. FileTimeToLocalFileTime( LastWriteTime, &LocalFileTime );
  303. FileTimeToSystemTime( &LocalFileTime, &DateTime );
  304. _snwprintf( DateTimeBuffer,
  305. 128,
  306. L"%02u/%02u/%04u %02u:%02u:%02u",
  307. (ULONG)DateTime.wMonth,
  308. (ULONG)DateTime.wDay,
  309. (ULONG)DateTime.wYear,
  310. (ULONG)DateTime.wHour,
  311. (ULONG)DateTime.wMinute,
  312. (ULONG)DateTime.wSecond
  313. );
  314. return DateTimeBuffer;
  315. }
  316. VOID
  317. PrintFileRecordIml(
  318. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  319. PIML_GENERIC_RECORD pGeneric,
  320. PWSTR Parents[],
  321. ULONG Depth,
  322. ULONG i
  323. )
  324. {
  325. PIML_FILE_RECORD pFile = (PIML_FILE_RECORD)pGeneric;
  326. printf( "File: %ws\n %u: %s\n",
  327. MP( PWSTR, pIml, pFile->Name ),
  328. i, FileActionStrings[ pFile->Action ]
  329. );
  330. }
  331. BOOLEAN
  332. CompareFileContentsIml(
  333. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  334. PIML_GENERIC_RECORD pGeneric1,
  335. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  336. PIML_GENERIC_RECORD pGeneric2,
  337. PWSTR Parents[]
  338. )
  339. {
  340. PIML_FILE_RECORD pFile1 = (PIML_FILE_RECORD)pGeneric1;
  341. PIML_FILE_RECORD pFile2 = (PIML_FILE_RECORD)pGeneric2;
  342. PIML_FILE_RECORD_CONTENTS pFileContents1;
  343. PIML_FILE_RECORD_CONTENTS pFileContents2;
  344. BOOLEAN ActionsDiffer = FALSE;
  345. BOOLEAN DatesDiffer = FALSE;
  346. BOOLEAN AttributesDiffer = FALSE;
  347. BOOLEAN SizesDiffer = FALSE;
  348. BOOLEAN ContentsDiffer = FALSE;
  349. BOOLEAN Result = TRUE;
  350. PCHAR s1, s2;
  351. ULONG n;
  352. pFileContents1 = MP( PIML_FILE_RECORD_CONTENTS, pIml1, pFile1->NewFile );
  353. pFileContents2 = MP( PIML_FILE_RECORD_CONTENTS, pIml2, pFile2->NewFile );
  354. if (pFile1->Action != pFile2->Action) {
  355. ActionsDiffer = TRUE;
  356. Result = FALSE;
  357. }
  358. else
  359. if (pFileContents1 != NULL && pFileContents2 != NULL) {
  360. if (pFile1->Action != CreateNewFile &&
  361. ((pFileContents1->LastWriteTime.dwHighDateTime !=
  362. pFileContents2->LastWriteTime.dwHighDateTime
  363. ) ||
  364. (pFileContents1->LastWriteTime.dwLowDateTime !=
  365. pFileContents2->LastWriteTime.dwLowDateTime
  366. )
  367. )
  368. ) {
  369. DatesDiffer = TRUE;
  370. Result = FALSE;
  371. }
  372. if (pFileContents1->FileAttributes != pFileContents2->FileAttributes) {
  373. AttributesDiffer = TRUE;
  374. Result = FALSE;
  375. }
  376. if (pFileContents1->FileSize != pFileContents2->FileSize) {
  377. SizesDiffer = TRUE;
  378. Result = FALSE;
  379. }
  380. else
  381. if (pFileContents1->Contents == 0 ||
  382. pFileContents2->Contents == 0 ||
  383. memcmp( MP( PVOID, pIml1, pFileContents1->Contents ),
  384. MP( PVOID, pIml2, pFileContents2->Contents ),
  385. pFileContents1->FileSize
  386. ) != 0
  387. ) {
  388. s1 = MP( PVOID, pIml1, pFileContents1->Contents );
  389. s2 = MP( PVOID, pIml2, pFileContents2->Contents );
  390. if (s1 == NULL || s2 == NULL) {
  391. n = 0;
  392. }
  393. else {
  394. n = pFileContents1->FileSize;
  395. }
  396. while (n) {
  397. if (*s1 != *s2) {
  398. n = pFileContents1->FileSize - n;
  399. break;
  400. }
  401. n -= 1;
  402. s1 += 1;
  403. s2 += 1;
  404. }
  405. ContentsDiffer = TRUE;
  406. Result = FALSE;
  407. }
  408. }
  409. if (!Result) {
  410. printf( "File: %ws\n", MP( PWSTR, pIml1, pFile1->Name ) );
  411. if (ActionsDiffer) {
  412. printf( " 1: Action - %s\n", FileActionStrings[ pFile1->Action ] );
  413. printf( " 2: Action - %s\n", FileActionStrings[ pFile2->Action ] );
  414. }
  415. if (DatesDiffer) {
  416. printf( " 1: LastWriteTime - %ws\n",
  417. FormatFileTime( &pFileContents1->LastWriteTime )
  418. );
  419. printf( " 2: LastWriteTime - %ws\n",
  420. FormatFileTime( &pFileContents2->LastWriteTime )
  421. );
  422. }
  423. if (AttributesDiffer) {
  424. printf( " 1: Attributes - 0x%08x\n", pFileContents1->FileAttributes );
  425. printf( " 2: Attributes - 0x%08x\n", pFileContents2->FileAttributes );
  426. }
  427. if (SizesDiffer) {
  428. printf( " 1: File Size - 0x%08x\n", pFileContents1->FileSize );
  429. printf( " 2: File Size - 0x%08x\n", pFileContents2->FileSize );
  430. }
  431. if (ContentsDiffer) {
  432. printf( " 1: Contents Differs\n" );
  433. printf( " 2: from each other at offset %08x\n", n );
  434. }
  435. }
  436. return Result;
  437. }
  438. char *KeyActionStrings[] = {
  439. "CreateNewKey",
  440. "DeleteOldKey",
  441. "ModifyKeyValues"
  442. };
  443. char *ValueActionStrings[] = {
  444. "CreateNewValue",
  445. "DeleteOldValue",
  446. "ModifyOldValue"
  447. };
  448. char *ValueTypeStrings[] = {
  449. "REG_NONE",
  450. "REG_SZ",
  451. "REG_EXPAND_SZ",
  452. "REG_BINARY",
  453. "REG_DWORD",
  454. "REG_DWORD_BIG_ENDIAN",
  455. "REG_LINK",
  456. "REG_MULTI_SZ",
  457. "REG_RESOURCE_LIST",
  458. "REG_FULL_RESOURCE_DESCRIPTOR",
  459. "REG_RESOURCE_REQUIREMENTS_LIST"
  460. };
  461. VOID
  462. PrintKeyValueRecordIml(
  463. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  464. PIML_GENERIC_RECORD pGeneric,
  465. PWSTR Parents[],
  466. ULONG Depth,
  467. ULONG i
  468. )
  469. {
  470. PIML_KEY_RECORD pKey = (PIML_KEY_RECORD)pGeneric;
  471. PIML_VALUE_RECORD pValue = (PIML_VALUE_RECORD)pGeneric;
  472. if (Depth == 2) {
  473. printf( "Key: %ws\n %u: %s\n",
  474. MP( PWSTR, pIml, pKey->Name ),
  475. i, KeyActionStrings[ pKey->Action ]
  476. );
  477. }
  478. else {
  479. if (Parents[ 1 ] != NULL) {
  480. printf( "Key: %ws\n", Parents[ 1 ] );
  481. Parents[ 1 ] = NULL;
  482. }
  483. printf( " Value: %ws\n %u: %s\n",
  484. MP( PWSTR, pIml, pValue->Name ),
  485. i, ValueActionStrings[ pValue->Action ]
  486. );
  487. }
  488. }
  489. UCHAR BlanksForPadding[] =
  490. " ";
  491. VOID
  492. PrintValueContents(
  493. PCHAR PrefixString,
  494. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  495. PIML_VALUE_RECORD_CONTENTS pValueContents
  496. )
  497. {
  498. ULONG ValueType;
  499. ULONG ValueLength;
  500. PVOID ValueData;
  501. ULONG cbPrefix, cb, i, j;
  502. PWSTR pw;
  503. PULONG p;
  504. ValueType = pValueContents->Type;
  505. ValueLength = pValueContents->Length;
  506. ValueData = MP( PVOID, pIml, pValueContents->Data );
  507. cbPrefix = printf( "%s", PrefixString );
  508. cb = cbPrefix + printf( "%s", ValueTypeStrings[ ValueType ] );
  509. switch( ValueType ) {
  510. case REG_SZ:
  511. case REG_LINK:
  512. case REG_EXPAND_SZ:
  513. pw = (PWSTR)ValueData;
  514. printf( " (%u) \"%.*ws\"\n", ValueLength, ValueLength/sizeof(WCHAR), pw );
  515. break;
  516. case REG_MULTI_SZ:
  517. pw = (PWSTR)ValueData;
  518. i = 0;
  519. if (*pw)
  520. while (i < (ValueLength - 1) / sizeof( WCHAR )) {
  521. if (i > 0) {
  522. printf( " \\\n%.*s", cbPrefix, BlanksForPadding );
  523. }
  524. printf( "\"%ws\" ", pw+i );
  525. do {
  526. ++i;
  527. }
  528. while (pw[i] != UNICODE_NULL);
  529. ++i;
  530. }
  531. printf( "\n" );
  532. break;
  533. case REG_DWORD:
  534. case REG_DWORD_BIG_ENDIAN:
  535. printf( " 0x%08x\n", *(PULONG)ValueData );
  536. break;
  537. case REG_RESOURCE_LIST:
  538. case REG_FULL_RESOURCE_DESCRIPTOR:
  539. case REG_RESOURCE_REQUIREMENTS_LIST:
  540. case REG_BINARY:
  541. case REG_NONE:
  542. cb = printf( " [0x%08lx]", ValueLength );
  543. if (ValueLength != 0) {
  544. p = (PULONG)ValueData;
  545. i = (ValueLength + 3) / sizeof( ULONG );
  546. for (j=0; j<i; j++) {
  547. if ((cbPrefix + cb + 11) > 78) {
  548. printf( " \\\n%.*s", cbPrefix, BlanksForPadding );
  549. cb = 0;
  550. }
  551. else {
  552. cb += printf( " " );
  553. }
  554. cb += printf( "0x%08lx", *p++ );
  555. }
  556. }
  557. printf( "\n" );
  558. break;
  559. }
  560. }
  561. BOOLEAN
  562. CompareKeyValueContentsIml(
  563. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  564. PIML_GENERIC_RECORD pGeneric1,
  565. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  566. PIML_GENERIC_RECORD pGeneric2,
  567. PWSTR Parents[]
  568. )
  569. {
  570. PIML_VALUE_RECORD pValue1 = (PIML_VALUE_RECORD)pGeneric1;
  571. PIML_VALUE_RECORD pValue2 = (PIML_VALUE_RECORD)pGeneric2;
  572. PIML_VALUE_RECORD_CONTENTS pValueContents1;
  573. PIML_VALUE_RECORD_CONTENTS pValueContents2;
  574. BOOLEAN ActionsDiffer = FALSE;
  575. BOOLEAN TypesDiffer = FALSE;
  576. BOOLEAN LengthsDiffer = FALSE;
  577. BOOLEAN ContentsDiffer = FALSE;
  578. BOOLEAN Result = TRUE;
  579. PCHAR s1, s2;
  580. ULONG n;
  581. pValueContents1 = MP( PIML_VALUE_RECORD_CONTENTS, pIml1, pValue1->NewValue );
  582. pValueContents2 = MP( PIML_VALUE_RECORD_CONTENTS, pIml2, pValue2->NewValue );
  583. if (pValue1->Action != pValue2->Action) {
  584. ActionsDiffer = TRUE;
  585. Result = FALSE;
  586. }
  587. else
  588. if (pValueContents1 != NULL && pValueContents2 != NULL) {
  589. if (pValue1->Action != CreateNewValue &&
  590. (pValueContents1->Type != pValueContents2->Type)
  591. ) {
  592. TypesDiffer = TRUE;
  593. Result = FALSE;
  594. }
  595. if (pValueContents1->Length != pValueContents2->Length) {
  596. LengthsDiffer = TRUE;
  597. Result = FALSE;
  598. }
  599. else
  600. if (pValueContents1->Data == 0 ||
  601. pValueContents2->Data == 0 ||
  602. memcmp( MP( PVOID, pIml1, pValueContents1->Data ),
  603. MP( PVOID, pIml2, pValueContents2->Data ),
  604. pValueContents1->Length
  605. ) != 0
  606. ) {
  607. s1 = MP( PVOID, pIml1, pValueContents1->Data );
  608. s2 = MP( PVOID, pIml2, pValueContents2->Data );
  609. if (s1 == NULL || s2 == NULL) {
  610. n = 0;
  611. }
  612. else {
  613. n = pValueContents1->Length;
  614. }
  615. while (n) {
  616. if (*s1 != *s2) {
  617. n = pValueContents1->Length - n;
  618. break;
  619. }
  620. n -= 1;
  621. s1 += 1;
  622. s2 += 1;
  623. }
  624. ContentsDiffer = TRUE;
  625. Result = FALSE;
  626. }
  627. }
  628. if (!Result) {
  629. if (Parents[ 2 ] != NULL) {
  630. printf( "Key: %ws\n", Parents[ 2 ] );
  631. Parents[ 2 ] = NULL;
  632. }
  633. printf( " Value: %ws\n", MP( PWSTR, pIml1, pValue1->Name ) );
  634. if (ActionsDiffer) {
  635. printf( " 1: Action - %s\n", ValueActionStrings[ pValue1->Action ] );
  636. printf( " 2: Action - %s\n", ValueActionStrings[ pValue2->Action ] );
  637. }
  638. if (TypesDiffer || LengthsDiffer || ContentsDiffer ) {
  639. PrintValueContents( " 1: ", pIml1, pValueContents1 );
  640. PrintValueContents( " 2: ", pIml2, pValueContents2 );
  641. }
  642. }
  643. return Result;
  644. }
  645. char *IniActionStrings[] = {
  646. "CreateNewIniFile",
  647. "ModifyOldIniFile"
  648. };
  649. char *SectionActionStrings[] = {
  650. "CreateNewSection",
  651. "DeleteOldSection",
  652. "ModifySectionVariables"
  653. };
  654. char *VariableActionStrings[] = {
  655. "CreateNewVariable",
  656. "DeleteOldVariable",
  657. "ModifyOldVariable"
  658. };
  659. VOID
  660. PrintIniSectionVariableRecordIml(
  661. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  662. PIML_GENERIC_RECORD pGeneric,
  663. PWSTR Parents[],
  664. ULONG Depth,
  665. ULONG i
  666. )
  667. {
  668. PIML_INI_RECORD pIni = (PIML_INI_RECORD)pGeneric;
  669. PIML_INISECTION_RECORD pSection = (PIML_INISECTION_RECORD)pGeneric;
  670. PIML_INIVARIABLE_RECORD pVariable = (PIML_INIVARIABLE_RECORD)pGeneric;
  671. if (Depth == 3) {
  672. printf( "Ini File: %ws\n %u: %s\n",
  673. MP( PWSTR, pIml, pIni->Name ),
  674. i, IniActionStrings[ pIni->Action ]
  675. );
  676. }
  677. else
  678. if (Depth == 2) {
  679. if (Parents[ 2 ] != NULL) {
  680. printf( "Ini File: %ws\n", Parents[ 2 ] );
  681. Parents[ 2 ] = NULL;
  682. }
  683. printf( " Section: %ws\n %u: %s\n",
  684. MP( PWSTR, pIml, pSection->Name ),
  685. i, SectionActionStrings[ pSection->Action ]
  686. );
  687. }
  688. else {
  689. if (Parents[ 2 ] != NULL) {
  690. printf( "Ini File: %ws\n", Parents[ 2 ] );
  691. Parents[ 2 ] = NULL;
  692. }
  693. if (Parents[ 1 ] != NULL) {
  694. printf( " Section: %ws\n", Parents[ 1 ] );
  695. Parents[ 1 ] = NULL;
  696. }
  697. printf( " Variable: %ws\n %u: %s\n",
  698. MP( PWSTR, pIml, pVariable->Name ),
  699. i, VariableActionStrings[ pVariable->Action ]
  700. );
  701. }
  702. }
  703. BOOLEAN
  704. CompareIniSectionVariableContentsIml(
  705. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  706. PIML_GENERIC_RECORD pGeneric1,
  707. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  708. PIML_GENERIC_RECORD pGeneric2,
  709. PWSTR Parents[]
  710. )
  711. {
  712. PIML_INIVARIABLE_RECORD pVariable1 = (PIML_INIVARIABLE_RECORD)pGeneric1;
  713. PIML_INIVARIABLE_RECORD pVariable2 = (PIML_INIVARIABLE_RECORD)pGeneric2;
  714. PWSTR pVariableContents1;
  715. PWSTR pVariableContents2;
  716. BOOLEAN ActionsDiffer = FALSE;
  717. BOOLEAN ContentsDiffer = FALSE;
  718. BOOLEAN Result = TRUE;
  719. pVariableContents1 = MP( PWSTR, pIml1, pVariable1->NewValue );
  720. pVariableContents2 = MP( PWSTR, pIml2, pVariable2->NewValue );
  721. if (pVariable1->Action != pVariable2->Action) {
  722. ActionsDiffer = TRUE;
  723. Result = FALSE;
  724. }
  725. else
  726. if (pVariableContents1 != NULL && pVariableContents2 != NULL) {
  727. if (wcscmp( pVariableContents1, pVariableContents2 ) != 0) {
  728. ContentsDiffer = TRUE;
  729. Result = FALSE;
  730. }
  731. }
  732. if (!Result) {
  733. if (Parents[ 2 ] != NULL) {
  734. printf( "Ini File: %ws\n", Parents[ 2 ] );
  735. Parents[ 2 ] = NULL;
  736. }
  737. if (Parents[ 1 ] != NULL) {
  738. printf( " Section: %ws\n", Parents[ 1 ] );
  739. Parents[ 1 ] = NULL;
  740. }
  741. printf( " Variable: %ws\n", MP( PWSTR, pIml1, pVariable1->Name ) );
  742. if (ActionsDiffer) {
  743. printf( " 1: Action - %s\n", VariableActionStrings[ pVariable1->Action ] );
  744. printf( " 2: Action - %s\n", VariableActionStrings[ pVariable2->Action ] );
  745. }
  746. if (ContentsDiffer) {
  747. printf( " 1: '%ws'\n", pVariableContents1 );
  748. printf( " 2: '%ws'\n", pVariableContents2 );
  749. }
  750. }
  751. return Result;
  752. }
  753. BOOLEAN
  754. CompareIml(
  755. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  756. PINSTALLATION_MODIFICATION_LOGFILE pIml2
  757. )
  758. {
  759. BOOLEAN Result = TRUE;
  760. PWSTR Parents[ 3 ];
  761. Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->FileRecords ),
  762. pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->FileRecords ),
  763. NULL,
  764. 1,
  765. PrintFileRecordIml,
  766. CompareFileContentsIml
  767. );
  768. memset( Parents, 0, sizeof( Parents ) );
  769. Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->KeyRecords ),
  770. pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->KeyRecords ),
  771. Parents,
  772. 2,
  773. PrintKeyValueRecordIml,
  774. CompareKeyValueContentsIml
  775. );
  776. memset( Parents, 0, sizeof( Parents ) );
  777. Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->IniRecords ),
  778. pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->IniRecords ),
  779. Parents,
  780. 3,
  781. PrintIniSectionVariableRecordIml,
  782. CompareIniSectionVariableContentsIml
  783. );
  784. return Result;
  785. }