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.

904 lines
26 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. DateTimeBuffer[127] = L'\0';
  305. _snwprintf( DateTimeBuffer,
  306. sizeof(DateTimeBuffer) / sizeof(WCHAR) - 1,
  307. L"%02u/%02u/%04u %02u:%02u:%02u",
  308. (ULONG)DateTime.wMonth,
  309. (ULONG)DateTime.wDay,
  310. (ULONG)DateTime.wYear,
  311. (ULONG)DateTime.wHour,
  312. (ULONG)DateTime.wMinute,
  313. (ULONG)DateTime.wSecond
  314. );
  315. return DateTimeBuffer;
  316. }
  317. VOID
  318. PrintFileRecordIml(
  319. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  320. PIML_GENERIC_RECORD pGeneric,
  321. PWSTR Parents[],
  322. ULONG Depth,
  323. ULONG i
  324. )
  325. {
  326. PIML_FILE_RECORD pFile = (PIML_FILE_RECORD)pGeneric;
  327. printf( "File: %ws\n %u: %s\n",
  328. MP( PWSTR, pIml, pFile->Name ),
  329. i, FileActionStrings[ pFile->Action ]
  330. );
  331. }
  332. BOOLEAN
  333. CompareFileContentsIml(
  334. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  335. PIML_GENERIC_RECORD pGeneric1,
  336. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  337. PIML_GENERIC_RECORD pGeneric2,
  338. PWSTR Parents[]
  339. )
  340. {
  341. PIML_FILE_RECORD pFile1 = (PIML_FILE_RECORD)pGeneric1;
  342. PIML_FILE_RECORD pFile2 = (PIML_FILE_RECORD)pGeneric2;
  343. PIML_FILE_RECORD_CONTENTS pFileContents1;
  344. PIML_FILE_RECORD_CONTENTS pFileContents2;
  345. BOOLEAN ActionsDiffer = FALSE;
  346. BOOLEAN DatesDiffer = FALSE;
  347. BOOLEAN AttributesDiffer = FALSE;
  348. BOOLEAN SizesDiffer = FALSE;
  349. BOOLEAN ContentsDiffer = FALSE;
  350. BOOLEAN Result = TRUE;
  351. PCHAR s1, s2;
  352. ULONG n;
  353. pFileContents1 = MP( PIML_FILE_RECORD_CONTENTS, pIml1, pFile1->NewFile );
  354. pFileContents2 = MP( PIML_FILE_RECORD_CONTENTS, pIml2, pFile2->NewFile );
  355. if (pFile1->Action != pFile2->Action) {
  356. ActionsDiffer = TRUE;
  357. Result = FALSE;
  358. }
  359. else
  360. if (pFileContents1 != NULL && pFileContents2 != NULL) {
  361. if (pFile1->Action != CreateNewFile &&
  362. ((pFileContents1->LastWriteTime.dwHighDateTime !=
  363. pFileContents2->LastWriteTime.dwHighDateTime
  364. ) ||
  365. (pFileContents1->LastWriteTime.dwLowDateTime !=
  366. pFileContents2->LastWriteTime.dwLowDateTime
  367. )
  368. )
  369. ) {
  370. DatesDiffer = TRUE;
  371. Result = FALSE;
  372. }
  373. if (pFileContents1->FileAttributes != pFileContents2->FileAttributes) {
  374. AttributesDiffer = TRUE;
  375. Result = FALSE;
  376. }
  377. if (pFileContents1->FileSize != pFileContents2->FileSize) {
  378. SizesDiffer = TRUE;
  379. Result = FALSE;
  380. }
  381. else
  382. if (pFileContents1->Contents == 0 ||
  383. pFileContents2->Contents == 0 ||
  384. memcmp( MP( PVOID, pIml1, pFileContents1->Contents ),
  385. MP( PVOID, pIml2, pFileContents2->Contents ),
  386. pFileContents1->FileSize
  387. ) != 0
  388. ) {
  389. s1 = MP( PVOID, pIml1, pFileContents1->Contents );
  390. s2 = MP( PVOID, pIml2, pFileContents2->Contents );
  391. if (s1 == NULL || s2 == NULL) {
  392. n = 0;
  393. }
  394. else {
  395. n = pFileContents1->FileSize;
  396. }
  397. while (n) {
  398. if (*s1 != *s2) {
  399. n = pFileContents1->FileSize - n;
  400. break;
  401. }
  402. n -= 1;
  403. s1 += 1;
  404. s2 += 1;
  405. }
  406. ContentsDiffer = TRUE;
  407. Result = FALSE;
  408. }
  409. }
  410. if (!Result) {
  411. printf( "File: %ws\n", MP( PWSTR, pIml1, pFile1->Name ) );
  412. if (ActionsDiffer) {
  413. printf( " 1: Action - %s\n", FileActionStrings[ pFile1->Action ] );
  414. printf( " 2: Action - %s\n", FileActionStrings[ pFile2->Action ] );
  415. }
  416. if (DatesDiffer) {
  417. printf( " 1: LastWriteTime - %ws\n",
  418. FormatFileTime( &pFileContents1->LastWriteTime )
  419. );
  420. printf( " 2: LastWriteTime - %ws\n",
  421. FormatFileTime( &pFileContents2->LastWriteTime )
  422. );
  423. }
  424. if (AttributesDiffer) {
  425. printf( " 1: Attributes - 0x%08x\n", pFileContents1->FileAttributes );
  426. printf( " 2: Attributes - 0x%08x\n", pFileContents2->FileAttributes );
  427. }
  428. if (SizesDiffer) {
  429. printf( " 1: File Size - 0x%08x\n", pFileContents1->FileSize );
  430. printf( " 2: File Size - 0x%08x\n", pFileContents2->FileSize );
  431. }
  432. if (ContentsDiffer) {
  433. printf( " 1: Contents Differs\n" );
  434. printf( " 2: from each other at offset %08x\n", n );
  435. }
  436. }
  437. return Result;
  438. }
  439. char *KeyActionStrings[] = {
  440. "CreateNewKey",
  441. "DeleteOldKey",
  442. "ModifyKeyValues"
  443. };
  444. char *ValueActionStrings[] = {
  445. "CreateNewValue",
  446. "DeleteOldValue",
  447. "ModifyOldValue"
  448. };
  449. char *ValueTypeStrings[] = {
  450. "REG_NONE",
  451. "REG_SZ",
  452. "REG_EXPAND_SZ",
  453. "REG_BINARY",
  454. "REG_DWORD",
  455. "REG_DWORD_BIG_ENDIAN",
  456. "REG_LINK",
  457. "REG_MULTI_SZ",
  458. "REG_RESOURCE_LIST",
  459. "REG_FULL_RESOURCE_DESCRIPTOR",
  460. "REG_RESOURCE_REQUIREMENTS_LIST"
  461. };
  462. VOID
  463. PrintKeyValueRecordIml(
  464. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  465. PIML_GENERIC_RECORD pGeneric,
  466. PWSTR Parents[],
  467. ULONG Depth,
  468. ULONG i
  469. )
  470. {
  471. PIML_KEY_RECORD pKey = (PIML_KEY_RECORD)pGeneric;
  472. PIML_VALUE_RECORD pValue = (PIML_VALUE_RECORD)pGeneric;
  473. if (Depth == 2) {
  474. printf( "Key: %ws\n %u: %s\n",
  475. MP( PWSTR, pIml, pKey->Name ),
  476. i, KeyActionStrings[ pKey->Action ]
  477. );
  478. }
  479. else {
  480. if (Parents[ 1 ] != NULL) {
  481. printf( "Key: %ws\n", Parents[ 1 ] );
  482. Parents[ 1 ] = NULL;
  483. }
  484. printf( " Value: %ws\n %u: %s\n",
  485. MP( PWSTR, pIml, pValue->Name ),
  486. i, ValueActionStrings[ pValue->Action ]
  487. );
  488. }
  489. }
  490. UCHAR BlanksForPadding[] =
  491. " ";
  492. VOID
  493. PrintValueContents(
  494. PCHAR PrefixString,
  495. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  496. PIML_VALUE_RECORD_CONTENTS pValueContents
  497. )
  498. {
  499. ULONG ValueType;
  500. ULONG ValueLength;
  501. PVOID ValueData;
  502. ULONG cbPrefix, cb, i, j;
  503. PWSTR pw;
  504. PULONG p;
  505. ValueType = pValueContents->Type;
  506. ValueLength = pValueContents->Length;
  507. ValueData = MP( PVOID, pIml, pValueContents->Data );
  508. cbPrefix = printf( "%s", PrefixString );
  509. cb = cbPrefix + printf( "%s", ValueTypeStrings[ ValueType ] );
  510. switch( ValueType ) {
  511. case REG_SZ:
  512. case REG_LINK:
  513. case REG_EXPAND_SZ:
  514. pw = (PWSTR)ValueData;
  515. printf( " (%u) \"%.*ws\"\n", ValueLength, ValueLength/sizeof(WCHAR), pw );
  516. break;
  517. case REG_MULTI_SZ:
  518. pw = (PWSTR)ValueData;
  519. i = 0;
  520. if (*pw)
  521. while (i < (ValueLength - 1) / sizeof( WCHAR )) {
  522. if (i > 0) {
  523. printf( " \\\n%.*s", cbPrefix, BlanksForPadding );
  524. }
  525. printf( "\"%ws\" ", pw+i );
  526. do {
  527. ++i;
  528. }
  529. while (pw[i] != UNICODE_NULL);
  530. ++i;
  531. }
  532. printf( "\n" );
  533. break;
  534. case REG_DWORD:
  535. case REG_DWORD_BIG_ENDIAN:
  536. printf( " 0x%08x\n", *(PULONG)ValueData );
  537. break;
  538. case REG_RESOURCE_LIST:
  539. case REG_FULL_RESOURCE_DESCRIPTOR:
  540. case REG_RESOURCE_REQUIREMENTS_LIST:
  541. case REG_BINARY:
  542. case REG_NONE:
  543. cb = printf( " [0x%08lx]", ValueLength );
  544. if (ValueLength != 0) {
  545. p = (PULONG)ValueData;
  546. i = (ValueLength + 3) / sizeof( ULONG );
  547. for (j=0; j<i; j++) {
  548. if ((cbPrefix + cb + 11) > 78) {
  549. printf( " \\\n%.*s", cbPrefix, BlanksForPadding );
  550. cb = 0;
  551. }
  552. else {
  553. cb += printf( " " );
  554. }
  555. cb += printf( "0x%08lx", *p++ );
  556. }
  557. }
  558. printf( "\n" );
  559. break;
  560. }
  561. }
  562. BOOLEAN
  563. CompareKeyValueContentsIml(
  564. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  565. PIML_GENERIC_RECORD pGeneric1,
  566. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  567. PIML_GENERIC_RECORD pGeneric2,
  568. PWSTR Parents[]
  569. )
  570. {
  571. PIML_VALUE_RECORD pValue1 = (PIML_VALUE_RECORD)pGeneric1;
  572. PIML_VALUE_RECORD pValue2 = (PIML_VALUE_RECORD)pGeneric2;
  573. PIML_VALUE_RECORD_CONTENTS pValueContents1;
  574. PIML_VALUE_RECORD_CONTENTS pValueContents2;
  575. BOOLEAN ActionsDiffer = FALSE;
  576. BOOLEAN TypesDiffer = FALSE;
  577. BOOLEAN LengthsDiffer = FALSE;
  578. BOOLEAN ContentsDiffer = FALSE;
  579. BOOLEAN Result = TRUE;
  580. PCHAR s1, s2;
  581. ULONG n;
  582. pValueContents1 = MP( PIML_VALUE_RECORD_CONTENTS, pIml1, pValue1->NewValue );
  583. pValueContents2 = MP( PIML_VALUE_RECORD_CONTENTS, pIml2, pValue2->NewValue );
  584. if (pValue1->Action != pValue2->Action) {
  585. ActionsDiffer = TRUE;
  586. Result = FALSE;
  587. }
  588. else
  589. if (pValueContents1 != NULL && pValueContents2 != NULL) {
  590. if (pValue1->Action != CreateNewValue &&
  591. (pValueContents1->Type != pValueContents2->Type)
  592. ) {
  593. TypesDiffer = TRUE;
  594. Result = FALSE;
  595. }
  596. if (pValueContents1->Length != pValueContents2->Length) {
  597. LengthsDiffer = TRUE;
  598. Result = FALSE;
  599. }
  600. else
  601. if (pValueContents1->Data == 0 ||
  602. pValueContents2->Data == 0 ||
  603. memcmp( MP( PVOID, pIml1, pValueContents1->Data ),
  604. MP( PVOID, pIml2, pValueContents2->Data ),
  605. pValueContents1->Length
  606. ) != 0
  607. ) {
  608. s1 = MP( PVOID, pIml1, pValueContents1->Data );
  609. s2 = MP( PVOID, pIml2, pValueContents2->Data );
  610. if (s1 == NULL || s2 == NULL) {
  611. n = 0;
  612. }
  613. else {
  614. n = pValueContents1->Length;
  615. }
  616. while (n) {
  617. if (*s1 != *s2) {
  618. n = pValueContents1->Length - n;
  619. break;
  620. }
  621. n -= 1;
  622. s1 += 1;
  623. s2 += 1;
  624. }
  625. ContentsDiffer = TRUE;
  626. Result = FALSE;
  627. }
  628. }
  629. if (!Result) {
  630. if (Parents[ 2 ] != NULL) {
  631. printf( "Key: %ws\n", Parents[ 2 ] );
  632. Parents[ 2 ] = NULL;
  633. }
  634. printf( " Value: %ws\n", MP( PWSTR, pIml1, pValue1->Name ) );
  635. if (ActionsDiffer) {
  636. printf( " 1: Action - %s\n", ValueActionStrings[ pValue1->Action ] );
  637. printf( " 2: Action - %s\n", ValueActionStrings[ pValue2->Action ] );
  638. }
  639. if (TypesDiffer || LengthsDiffer || ContentsDiffer ) {
  640. PrintValueContents( " 1: ", pIml1, pValueContents1 );
  641. PrintValueContents( " 2: ", pIml2, pValueContents2 );
  642. }
  643. }
  644. return Result;
  645. }
  646. char *IniActionStrings[] = {
  647. "CreateNewIniFile",
  648. "ModifyOldIniFile"
  649. };
  650. char *SectionActionStrings[] = {
  651. "CreateNewSection",
  652. "DeleteOldSection",
  653. "ModifySectionVariables"
  654. };
  655. char *VariableActionStrings[] = {
  656. "CreateNewVariable",
  657. "DeleteOldVariable",
  658. "ModifyOldVariable"
  659. };
  660. VOID
  661. PrintIniSectionVariableRecordIml(
  662. PINSTALLATION_MODIFICATION_LOGFILE pIml,
  663. PIML_GENERIC_RECORD pGeneric,
  664. PWSTR Parents[],
  665. ULONG Depth,
  666. ULONG i
  667. )
  668. {
  669. PIML_INI_RECORD pIni = (PIML_INI_RECORD)pGeneric;
  670. PIML_INISECTION_RECORD pSection = (PIML_INISECTION_RECORD)pGeneric;
  671. PIML_INIVARIABLE_RECORD pVariable = (PIML_INIVARIABLE_RECORD)pGeneric;
  672. if (Depth == 3) {
  673. printf( "Ini File: %ws\n %u: %s\n",
  674. MP( PWSTR, pIml, pIni->Name ),
  675. i, IniActionStrings[ pIni->Action ]
  676. );
  677. }
  678. else
  679. if (Depth == 2) {
  680. if (Parents[ 2 ] != NULL) {
  681. printf( "Ini File: %ws\n", Parents[ 2 ] );
  682. Parents[ 2 ] = NULL;
  683. }
  684. printf( " Section: %ws\n %u: %s\n",
  685. MP( PWSTR, pIml, pSection->Name ),
  686. i, SectionActionStrings[ pSection->Action ]
  687. );
  688. }
  689. else {
  690. if (Parents[ 2 ] != NULL) {
  691. printf( "Ini File: %ws\n", Parents[ 2 ] );
  692. Parents[ 2 ] = NULL;
  693. }
  694. if (Parents[ 1 ] != NULL) {
  695. printf( " Section: %ws\n", Parents[ 1 ] );
  696. Parents[ 1 ] = NULL;
  697. }
  698. printf( " Variable: %ws\n %u: %s\n",
  699. MP( PWSTR, pIml, pVariable->Name ),
  700. i, VariableActionStrings[ pVariable->Action ]
  701. );
  702. }
  703. }
  704. BOOLEAN
  705. CompareIniSectionVariableContentsIml(
  706. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  707. PIML_GENERIC_RECORD pGeneric1,
  708. PINSTALLATION_MODIFICATION_LOGFILE pIml2,
  709. PIML_GENERIC_RECORD pGeneric2,
  710. PWSTR Parents[]
  711. )
  712. {
  713. PIML_INIVARIABLE_RECORD pVariable1 = (PIML_INIVARIABLE_RECORD)pGeneric1;
  714. PIML_INIVARIABLE_RECORD pVariable2 = (PIML_INIVARIABLE_RECORD)pGeneric2;
  715. PWSTR pVariableContents1;
  716. PWSTR pVariableContents2;
  717. BOOLEAN ActionsDiffer = FALSE;
  718. BOOLEAN ContentsDiffer = FALSE;
  719. BOOLEAN Result = TRUE;
  720. pVariableContents1 = MP( PWSTR, pIml1, pVariable1->NewValue );
  721. pVariableContents2 = MP( PWSTR, pIml2, pVariable2->NewValue );
  722. if (pVariable1->Action != pVariable2->Action) {
  723. ActionsDiffer = TRUE;
  724. Result = FALSE;
  725. }
  726. else
  727. if (pVariableContents1 != NULL && pVariableContents2 != NULL) {
  728. if (wcscmp( pVariableContents1, pVariableContents2 ) != 0) {
  729. ContentsDiffer = TRUE;
  730. Result = FALSE;
  731. }
  732. }
  733. if (!Result) {
  734. if (Parents[ 2 ] != NULL) {
  735. printf( "Ini File: %ws\n", Parents[ 2 ] );
  736. Parents[ 2 ] = NULL;
  737. }
  738. if (Parents[ 1 ] != NULL) {
  739. printf( " Section: %ws\n", Parents[ 1 ] );
  740. Parents[ 1 ] = NULL;
  741. }
  742. printf( " Variable: %ws\n", MP( PWSTR, pIml1, pVariable1->Name ) );
  743. if (ActionsDiffer) {
  744. printf( " 1: Action - %s\n", VariableActionStrings[ pVariable1->Action ] );
  745. printf( " 2: Action - %s\n", VariableActionStrings[ pVariable2->Action ] );
  746. }
  747. if (ContentsDiffer) {
  748. printf( " 1: '%ws'\n", pVariableContents1 );
  749. printf( " 2: '%ws'\n", pVariableContents2 );
  750. }
  751. }
  752. return Result;
  753. }
  754. BOOLEAN
  755. CompareIml(
  756. PINSTALLATION_MODIFICATION_LOGFILE pIml1,
  757. PINSTALLATION_MODIFICATION_LOGFILE pIml2
  758. )
  759. {
  760. BOOLEAN Result = TRUE;
  761. PWSTR Parents[ 3 ];
  762. Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->FileRecords ),
  763. pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->FileRecords ),
  764. NULL,
  765. 1,
  766. PrintFileRecordIml,
  767. CompareFileContentsIml
  768. );
  769. memset( Parents, 0, sizeof( Parents ) );
  770. Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->KeyRecords ),
  771. pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->KeyRecords ),
  772. Parents,
  773. 2,
  774. PrintKeyValueRecordIml,
  775. CompareKeyValueContentsIml
  776. );
  777. memset( Parents, 0, sizeof( Parents ) );
  778. Result &= CompareGenericIml( pIml1, MP( PIML_GENERIC_RECORD, pIml1, pIml1->IniRecords ),
  779. pIml2, MP( PIML_GENERIC_RECORD, pIml2, pIml2->IniRecords ),
  780. Parents,
  781. 3,
  782. PrintIniSectionVariableRecordIml,
  783. CompareIniSectionVariableContentsIml
  784. );
  785. return Result;
  786. }