1 /*-
2 ***********************************************************************
3 *
4 * $Id: compare.c,v 1.54 2014/07/30 07:07:30 mavrik Exp $
5 *
6 ***********************************************************************
7 *
8 * Copyright 2000-2014 The FTimes Project, All Rights Reserved.
9 *
10 ***********************************************************************
11 */
12 #include "all-includes.h"
13
14 /*-
15 ***********************************************************************
16 *
17 * Globals
18 *
19 ***********************************************************************
20 */
21 static CMP_PROPERTIES *gpsCmpProperties;
22
23 /*-
24 ***********************************************************************
25 *
26 * CompareDecodeLine
27 *
28 ***********************************************************************
29 */
30 int
CompareDecodeLine(char * pcLine,SNAPSHOT_CONTEXT * psBaseline,char ** ppcDecodeFields,char * pcError)31 CompareDecodeLine(char *pcLine, SNAPSHOT_CONTEXT *psBaseline, char **ppcDecodeFields, char *pcError)
32 {
33 const char acRoutine[] = "CompareDecodeLine()";
34 char acTempLine[CMP_MAX_LINE] = { 0 };
35 char *pcHead = NULL;
36 char *pcTail = NULL;
37 int i = 0;
38 int iDone = 0;
39 int iFound = 0;
40 int iField = 0;
41 int iLength = 0;
42 int iMaskTableLength = MaskGetTableLength(MASK_RUNMODE_TYPE_CMP);
43 unsigned long ul = 0;
44
45 /*-
46 *********************************************************************
47 *
48 * Terminate each output field.
49 *
50 *********************************************************************
51 */
52 for (i = 0; i < iMaskTableLength; i++)
53 {
54 ppcDecodeFields[i][0] = 0;
55 }
56
57 /*-
58 *********************************************************************
59 *
60 * Check the line's length.
61 *
62 *********************************************************************
63 */
64 iLength = strlen(pcLine);
65 if (iLength > CMP_MAX_LINE - 1)
66 {
67 snprintf(pcError, MESSAGE_SIZE, "%s: Length = [%d]: Length exceeds %d bytes.", acRoutine, iLength, CMP_MAX_LINE - 1);
68 return ER;
69 }
70 snprintf(acTempLine, CMP_MAX_LINE, "%s", pcLine);
71
72 /*-
73 *********************************************************************
74 *
75 * Parse data, and make a copy of each specified field.
76 *
77 *********************************************************************
78 */
79 for (pcHead = pcTail = acTempLine, iDone = iField = 0; !iDone; pcHead = ++pcTail, iField++)
80 {
81 while (*pcTail != CMP_SEPARATOR_C && *pcTail != 0)
82 {
83 pcTail++;
84 }
85 if (*pcTail == 0)
86 {
87 iDone = 1;
88 }
89 else
90 {
91 *pcTail = 0;
92 }
93 for (i = 0, iFound = -1; i < iMaskTableLength; i++)
94 {
95 ul = 1 << i;
96 if (MASK_BIT_IS_SET(psBaseline->ulFieldMask, ul))
97 {
98 iFound++;
99 }
100 if (iFound == iField)
101 {
102 break;
103 }
104 }
105 if (i != iMaskTableLength)
106 {
107 snprintf(ppcDecodeFields[psBaseline->aiIndex2Map[iField]], CMP_MAX_LINE, "%s", pcHead);
108 }
109 }
110
111 return ER_OK;
112 }
113
114
115 /*-
116 ***********************************************************************
117 *
118 * CompareEnumerateChanges
119 *
120 ***********************************************************************
121 */
122 int
CompareEnumerateChanges(SNAPSHOT_CONTEXT * psBaseline,SNAPSHOT_CONTEXT * psSnapshot,char * pcError)123 CompareEnumerateChanges(SNAPSHOT_CONTEXT *psBaseline, SNAPSHOT_CONTEXT *psSnapshot, char *pcError)
124 {
125 const char acRoutine[] = "CompareEnumerateChanges()";
126 char acLocalError[MESSAGE_SIZE] = "";
127 char **ppcBaselineFields = NULL;
128 char **ppcSnapshotFields = NULL;
129 CMP_DATA sCompareData;
130 CMP_PROPERTIES *psProperties = CompareGetPropertiesReference();
131 int iLastIndex = 0;
132 int iTempIndex = 0;
133 int i = 0;
134 int iError = 0;
135 int iFound = 0;
136 int iKeysIndex = 0;
137 int iMaskTableLength = MaskGetTableLength(MASK_RUNMODE_TYPE_CMP);
138 unsigned long ul = 0;
139
140 /*-
141 *********************************************************************
142 *
143 * Allocate enough memory to hold one complete baseline record broken
144 * down into individual fields and stored in an array.
145 *
146 *********************************************************************
147 */
148 /* FIXME Move this code to a subroutine. Add logic to free this memory when it's no longer required. */
149 ppcBaselineFields = (char **) malloc(iMaskTableLength * sizeof(char **));
150 if (ppcBaselineFields == NULL)
151 {
152 snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): %s", acRoutine, strerror(errno));
153 return ER;
154 }
155
156 for (i = 0; i < iMaskTableLength; i++)
157 {
158 ppcBaselineFields[i] = (char *) malloc(CMP_MAX_LINE);
159 if (ppcBaselineFields[i] == NULL)
160 {
161 snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): %s", acRoutine, strerror(errno));
162 return ER;
163 }
164 }
165
166 /*-
167 *********************************************************************
168 *
169 * Enumerate changed and new files.
170 *
171 *********************************************************************
172 */
173
174 /*-
175 *********************************************************************
176 *
177 * Read and process baseline data. If the read returns NULL, it
178 * could mean an error has occured or EOF was reached. If a record
179 * fails to parse (compressed files only), set a flag so that the
180 * next read will automatically skip all records up to the next
181 * checkpoint.
182 *
183 *********************************************************************
184 */
185 while (DecodeReadLine(psSnapshot, acLocalError) != NULL)
186 {
187 psSnapshot->sDecodeStats.ulAnalyzed++;
188 iError = DecodeParseRecord(psSnapshot, acLocalError);
189 if (iError != ER_OK)
190 {
191 if (psSnapshot->iCompressed)
192 {
193 psSnapshot->iSkipToNext = TRUE;
194 }
195 psSnapshot->sDecodeStats.ulSkipped++;
196 continue;
197 }
198 psSnapshot->sDecodeStats.ulDecoded++;
199
200 /*-
201 *******************************************************************
202 *
203 * Search for the hash and compare specified fields.
204 *
205 *******************************************************************
206 */
207 psProperties->ulAnalyzed++;
208 iFound = 0;
209 ppcSnapshotFields = psSnapshot->psCurrRecord->ppcFields;
210 sCompareData.cCategory = 0;
211 sCompareData.pcRecord = NULL;
212 sCompareData.iBaselineRecord = 0;
213 sCompareData.iSnapshotRecord = psSnapshot->iLineNumber;
214 iKeysIndex = CMP_GET_NODE_INDEX(psSnapshot->psCurrRecord->aucHash);
215
216 iLastIndex = iTempIndex = psProperties->aiBaselineKeys[iKeysIndex];
217 while (iTempIndex != -1)
218 {
219 if (memcmp(psProperties->psBaselineNodes[iTempIndex].aucHash, psSnapshot->psCurrRecord->aucHash, MD5_HASH_SIZE) == 0)
220 {
221 iFound++;
222 if (++psProperties->psBaselineNodes[iTempIndex].iFound > 1)
223 {
224 snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Line = [%d]: Hash collision. Check for duplicate filenames.", acRoutine, psSnapshot->pcFile, psSnapshot->iLineNumber);
225 return ER;
226 }
227 CompareDecodeLine(psProperties->psBaselineNodes[iTempIndex].pcData, psBaseline, ppcBaselineFields, acLocalError);
228 sCompareData.ulChangedMask = 0;
229 sCompareData.ulUnknownMask = 0;
230 sCompareData.iBaselineRecord = psProperties->psBaselineNodes[iTempIndex].iLineNumber;
231 for (i = 0; i < iMaskTableLength; i++)
232 {
233 ul = 1 << i;
234 if (MASK_BIT_IS_SET(psProperties->psCompareMask->ulMask, ul))
235 {
236 if (ppcBaselineFields[i][0] != 0 && ppcSnapshotFields[i][0] != 0)
237 {
238 if (strcmp(ppcBaselineFields[i], ppcSnapshotFields[i]) != 0)
239 {
240 sCompareData.ulChangedMask |= ul;
241 }
242 }
243 else
244 {
245 sCompareData.ulUnknownMask |= ul;
246 }
247 }
248 }
249 if (sCompareData.ulChangedMask && !sCompareData.ulUnknownMask)
250 {
251 sCompareData.cCategory = 'C';
252 psProperties->ulChanged++;
253 }
254 else if (!sCompareData.ulChangedMask && sCompareData.ulUnknownMask)
255 {
256 sCompareData.cCategory = 'U';
257 psProperties->ulUnknown++;
258 }
259 else if (sCompareData.ulChangedMask && sCompareData.ulUnknownMask)
260 {
261 sCompareData.cCategory = 'X';
262 psProperties->ulCrossed++;
263 }
264 sCompareData.pcRecord = ppcBaselineFields[0];
265 break;
266 }
267 iLastIndex = iTempIndex;
268 iTempIndex = psProperties->psBaselineNodes[iTempIndex].iNextIndex;
269 }
270 if (iFound == 0 || iLastIndex == -1)
271 {
272 sCompareData.cCategory = 'N';
273 sCompareData.pcRecord = ppcSnapshotFields[0];
274 psProperties->ulNew++;
275 }
276 else if (sCompareData.cCategory == 0)
277 {
278 continue; /* Nothing to report. */
279 }
280 iError = CompareWriteRecord(psProperties, &sCompareData, acLocalError);
281 if (iError != ER_OK)
282 {
283 snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Line = [%d]: %s", acRoutine, psSnapshot->pcFile, psSnapshot->iLineNumber, acLocalError);
284 return ER;
285 }
286 }
287 if (ferror(psSnapshot->pFile))
288 {
289 snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Line = [%d]: %s", acRoutine, psSnapshot->pcFile, psSnapshot->iLineNumber, acLocalError);
290 psSnapshot->sDecodeStats.ulSkipped++;
291 return ER;
292 }
293
294 /*-
295 *********************************************************************
296 *
297 * Enumerate missing objects.
298 *
299 *********************************************************************
300 */
301 sCompareData.iSnapshotRecord = 0;
302 for (iKeysIndex = 0; iKeysIndex < CMP_MODULUS; iKeysIndex++)
303 {
304 iTempIndex = psProperties->aiBaselineKeys[iKeysIndex];
305 while (iTempIndex != -1)
306 {
307 if (psProperties->psBaselineNodes[iTempIndex].iFound == 0)
308 {
309 sCompareData.cCategory = 'M';
310 sCompareData.pcRecord = psProperties->psBaselineNodes[iTempIndex].pcData;
311 sCompareData.iBaselineRecord = psProperties->psBaselineNodes[iTempIndex].iLineNumber;
312 iError = CompareWriteRecord(psProperties, &sCompareData, acLocalError);
313 if (iError != ER_OK)
314 {
315 snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Line = [%d]: %s", acRoutine, psSnapshot->pcFile, psSnapshot->iLineNumber, acLocalError);
316 return ER;
317 }
318 psProperties->ulMissing++;
319 }
320 iTempIndex = psProperties->psBaselineNodes[iTempIndex].iNextIndex;
321 }
322 }
323
324 return ER_OK;
325 }
326
327
328 /*-
329 ***********************************************************************
330 *
331 * CompareFreeNodeData
332 *
333 ***********************************************************************
334 */
335 void
CompareFreeNodeData(int * piKeys,CMP_NODE * psNodes)336 CompareFreeNodeData(int *piKeys, CMP_NODE *psNodes)
337 {
338 int iKeysIndex = 0;
339 int iNodeIndex = 0;
340
341 for (iKeysIndex = 0; iKeysIndex < CMP_MODULUS; iKeysIndex++)
342 {
343 iNodeIndex = piKeys[iKeysIndex];
344 while (iNodeIndex != -1)
345 {
346 if (psNodes[iNodeIndex].pcData)
347 {
348 free(psNodes[iNodeIndex].pcData);
349 }
350 iNodeIndex = psNodes[iNodeIndex].iNextIndex;
351 }
352 }
353 }
354
355
356 /*-
357 ***********************************************************************
358 *
359 * CompareFreeProperties
360 *
361 ***********************************************************************
362 */
363 void
CompareFreeProperties(CMP_PROPERTIES * psProperties)364 CompareFreeProperties(CMP_PROPERTIES *psProperties)
365 {
366 if (psProperties != NULL)
367 {
368 if (psProperties->pcMemoryMapFile != NULL)
369 {
370 free(psProperties->pcMemoryMapFile);
371 }
372 if (psProperties->psBaselineNodes != NULL)
373 {
374 /*-
375 *****************************************************************
376 * NOTE: CompareFreeNodeData() should be used to free this data,
377 * but it causes severe cleanup lag (e.g., it takes more time to
378 * free the data than it did to compare it) on some systems such
379 * as FreeBSD for large jobs (~700K+ records). Therefore, the
380 * following code has been disabled, and it is being kept as a
381 * reminder. Other systems such as Linux and Solaris didn't have
382 * this problem, so there may be other issues involved that are
383 * not yet understood. However, since the program is essentially
384 * terminal at this point, the risk of not doing this seems low.
385 *
386 * if (!psProperties->iMemoryMapFile)
387 * {
388 * CompareFreeNodeData(psProperties->aiBaselineKeys, psProperties->psBaselineNodes);
389 * }
390 *
391 *****************************************************************
392 */
393 free(psProperties->psBaselineNodes);
394 }
395 free(psProperties);
396 }
397 }
398
399
400 /*-
401 ***********************************************************************
402 *
403 * CompareGetChangedCount
404 *
405 ***********************************************************************
406 */
407 int
CompareGetChangedCount(void)408 CompareGetChangedCount(void)
409 {
410 return gpsCmpProperties->ulChanged;
411 }
412
413
414 /*-
415 ***********************************************************************
416 *
417 * CompareGetCrossedCount
418 *
419 ***********************************************************************
420 */
421 int
CompareGetCrossedCount(void)422 CompareGetCrossedCount(void)
423 {
424 return gpsCmpProperties->ulCrossed;
425 }
426
427
428 /*-
429 ***********************************************************************
430 *
431 * CompareGetMissingCount
432 *
433 ***********************************************************************
434 */
435 int
CompareGetMissingCount(void)436 CompareGetMissingCount(void)
437 {
438 return gpsCmpProperties->ulMissing;
439 }
440
441
442 /*-
443 ***********************************************************************
444 *
445 * CompareGetNewCount
446 *
447 ***********************************************************************
448 */
449 int
CompareGetNewCount(void)450 CompareGetNewCount(void)
451 {
452 return gpsCmpProperties->ulNew;
453 }
454
455
456 /*-
457 ***********************************************************************
458 *
459 * CompareGetNodeIndexReference
460 *
461 ***********************************************************************
462 */
463 int *
CompareGetNodeIndexReference(unsigned char * pucHash,int * piKeys,CMP_NODE * psNodes)464 CompareGetNodeIndexReference(unsigned char *pucHash, int *piKeys, CMP_NODE *psNodes)
465 {
466 int iKeysIndex = 0;
467 int iLastIndex = 0;
468 int iTempIndex = 0;
469
470 iKeysIndex = CMP_GET_NODE_INDEX(pucHash);
471
472 if ((iLastIndex = iTempIndex = piKeys[iKeysIndex]) == -1)
473 {
474 return &piKeys[iKeysIndex];
475 }
476 else
477 {
478 while (iTempIndex != -1)
479 {
480 if (memcmp(psNodes[iTempIndex].aucHash, pucHash, MD5_HASH_SIZE) == 0)
481 {
482 return NULL;
483 }
484 iLastIndex = iTempIndex;
485 iTempIndex = psNodes[iTempIndex].iNextIndex;
486 }
487 return &psNodes[iLastIndex].iNextIndex;
488 }
489 }
490
491
492 /*-
493 ***********************************************************************
494 *
495 * CompareGetPropertiesReference
496 *
497 ***********************************************************************
498 */
499 CMP_PROPERTIES *
CompareGetPropertiesReference(void)500 CompareGetPropertiesReference(void)
501 {
502 return gpsCmpProperties;
503 }
504
505
506 /*-
507 ***********************************************************************
508 *
509 * CompareGetRecordCount
510 *
511 ***********************************************************************
512 */
513 int
CompareGetRecordCount(void)514 CompareGetRecordCount(void)
515 {
516 return gpsCmpProperties->ulAnalyzed;
517 }
518
519
520 /*-
521 ***********************************************************************
522 *
523 * CompareGetUnknownCount
524 *
525 ***********************************************************************
526 */
527 int
CompareGetUnknownCount(void)528 CompareGetUnknownCount(void)
529 {
530 return gpsCmpProperties->ulUnknown;
531 }
532
533
534 /*-
535 ***********************************************************************
536 *
537 * CompareLoadBaselineData
538 *
539 ***********************************************************************
540 */
541 int
CompareLoadBaselineData(SNAPSHOT_CONTEXT * psBaseline,char * pcError)542 CompareLoadBaselineData(SNAPSHOT_CONTEXT *psBaseline, char *pcError)
543 {
544 const char acRoutine[] = "CompareLoadBaselineData()";
545 char acLocalError[MESSAGE_SIZE] = "";
546 char *pcData = NULL;
547 CMP_PROPERTIES *psProperties = CompareGetPropertiesReference();
548 FILE *pFile = NULL;
549 int i = 0;
550 int n = 0;
551 int iError = 0;
552 int iNodeCount = 0;
553 int iNodeIndex = 0;
554 int iOffset = 0;
555 int *piNodeIndex = NULL;
556 #ifdef WINNT
557 HANDLE hFile = NULL;
558 HANDLE hMemoryMap = NULL;
559 char *pcMessage = NULL;
560 int iFile = 0;
561 #endif
562
563 /*-
564 *********************************************************************
565 *
566 * Conditionally create a file to serve as the backing for a memory
567 * map. For WINX platforms, convert the native handle into a FILE
568 * pointer so that common code can be used when writing data to the
569 * file. Next, unlink the file. This will fail for WINX systems, but
570 * that should not be an issue because a second unlink is attempted
571 * when the program executes its final stage.
572 *
573 *********************************************************************
574 */
575 if (psProperties->iMemoryMapFile)
576 {
577 #ifdef WINNT
578 hFile = CreateFile
579 (
580 psProperties->pcMemoryMapFile,
581 GENERIC_READ | GENERIC_WRITE,
582 0,
583 NULL,
584 CREATE_NEW,
585 FILE_ATTRIBUTE_NORMAL,
586 NULL
587 );
588 if (hFile == INVALID_HANDLE_VALUE)
589 {
590 ErrorFormatWinxError(GetLastError(), &pcMessage);
591 snprintf(pcError, MESSAGE_SIZE, "%s: CreateFile(): %s", acRoutine, pcMessage);
592 return ER;
593 }
594 iFile = _open_osfhandle((intptr_t) hFile, 0);
595 if (iFile == ER)
596 {
597 snprintf(pcError, MESSAGE_SIZE, "%s: open_osfhandle(): Handle association failed.", acRoutine);
598 return ER;
599 }
600 pFile = fdopen(iFile, "wb+");
601 if (pFile == NULL)
602 {
603 snprintf(pcError, MESSAGE_SIZE, "%s: fdopen(): %s", acRoutine, strerror(errno));
604 return ER;
605 }
606 #else
607 umask(077);
608 pFile = fopen(psProperties->pcMemoryMapFile, "wb+");
609 if (pFile == NULL)
610 {
611 snprintf(pcError, MESSAGE_SIZE, "%s: fopen(): %s", acRoutine, strerror(errno));
612 return ER;
613 }
614 #endif
615 unlink(psProperties->pcMemoryMapFile);
616 }
617
618 /*-
619 *********************************************************************
620 *
621 * Read and process baseline data. If the read returns NULL, it
622 * could mean an error has occured or EOF was reached. If a record
623 * fails to parse (compressed files only), set a flag so that the
624 * next read will automatically skip all records up to the next
625 * checkpoint.
626 *
627 *********************************************************************
628 */
629 while (DecodeReadLine(psBaseline, acLocalError) != NULL)
630 {
631 psBaseline->sDecodeStats.ulAnalyzed++;
632 iError = DecodeParseRecord(psBaseline, acLocalError);
633 if (iError != ER_OK)
634 {
635 if (psBaseline->iCompressed)
636 {
637 psBaseline->iSkipToNext = TRUE;
638 }
639 psBaseline->sDecodeStats.ulSkipped++;
640 continue;
641 }
642 psBaseline->sDecodeStats.ulDecoded++;
643
644 /*-
645 *******************************************************************
646 *
647 * Allocate a block of memory to hold this record. Then, join the
648 * fields to form a single record. If a memory map file is being
649 * used, this memory is freed as soon as it has been written out
650 * the file. Otherwise it is freed later in CompareFreeNodeData().
651 *
652 *******************************************************************
653 */
654 for (i = n = 0; i < psBaseline->iFieldCount; i++)
655 {
656 n += strlen(psBaseline->psCurrRecord->ppcFields[psBaseline->aiIndex2Map[i]]);
657 }
658 pcData = malloc(n + i + 1); /* The value for i represents the number of delimiters needed. */
659 if (pcData == NULL)
660 {
661 snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): File = [%s], Line = [%d]: %s", acRoutine, psBaseline->pcFile, psBaseline->iLineNumber, strerror(errno));
662 return ER;
663 }
664 for (i = n = 0; i < psBaseline->iFieldCount; i++)
665 {
666 n += sprintf(&pcData[n], "%s%s", (i > 0) ? DECODE_SEPARATOR_S : "", psBaseline->psCurrRecord->ppcFields[psBaseline->aiIndex2Map[i]]);
667 }
668
669 /*-
670 *******************************************************************
671 *
672 * Conditionally write this record out to the backing file.
673 *
674 *******************************************************************
675 */
676 if (psProperties->iMemoryMapFile)
677 {
678 iError = SupportWriteData(pFile, pcData, n + 1, acLocalError);
679 if (iError != ER_OK)
680 {
681 snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
682 return ER;
683 }
684 free(pcData);
685 }
686
687 /*-
688 *******************************************************************
689 *
690 * Check node count, and allocate more, if necessary.
691 *
692 *******************************************************************
693 */
694 if (iNodeIndex >= iNodeCount)
695 {
696 iNodeCount += CMP_NODE_REQUEST_COUNT;
697 psProperties->psBaselineNodes = (CMP_NODE *) realloc(psProperties->psBaselineNodes, (iNodeCount * sizeof(CMP_NODE)));
698 if (psProperties->psBaselineNodes == NULL)
699 {
700 snprintf(pcError, MESSAGE_SIZE, "%s: realloc(): File = [%s], Line = [%d]: %s", acRoutine, psBaseline->pcFile, psBaseline->iLineNumber, strerror(errno));
701 return ER;
702 }
703 }
704
705 /*-
706 *******************************************************************
707 *
708 * Insert a new node. Abort on a collision.
709 *
710 *******************************************************************
711 */
712 piNodeIndex = CompareGetNodeIndexReference(psBaseline->psCurrRecord->aucHash, psProperties->aiBaselineKeys, psProperties->psBaselineNodes);
713 if (piNodeIndex == NULL)
714 {
715 snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Line = [%d]: Hash collision. Check for duplicate filenames.", acRoutine, psBaseline->pcFile, psBaseline->iLineNumber);
716 return ER;
717 }
718 else
719 {
720 *piNodeIndex = iNodeIndex;
721 psProperties->psBaselineNodes[*piNodeIndex].iNextIndex = -1;
722 memcpy(psProperties->psBaselineNodes[*piNodeIndex].aucHash, psBaseline->psCurrRecord->aucHash, MD5_HASH_SIZE);
723 /* FIXME See TODO list. */
724 if (psProperties->iMemoryMapFile)
725 {
726 psProperties->psBaselineNodes[*piNodeIndex].pcData = NULL;
727 psProperties->psBaselineNodes[*piNodeIndex].iLineNumber = psBaseline->iLineNumber;
728 psProperties->psBaselineNodes[*piNodeIndex].iOffset = iOffset;
729 iOffset += n + 1;
730 }
731 else
732 {
733 psProperties->psBaselineNodes[*piNodeIndex].pcData = pcData;
734 psProperties->psBaselineNodes[*piNodeIndex].iLineNumber = psBaseline->iLineNumber;
735 psProperties->psBaselineNodes[*piNodeIndex].iOffset = 0;
736 }
737 iNodeIndex++;
738 }
739 }
740 if (ferror(psBaseline->pFile))
741 {
742 snprintf(pcError, MESSAGE_SIZE, "%s: File = [%s], Line = [%d]: %s", acRoutine, psBaseline->pcFile, psBaseline->iLineNumber, acLocalError);
743 psBaseline->sDecodeStats.ulSkipped++;
744 return ER;
745 }
746
747 /*-
748 *********************************************************************
749 *
750 * Conditionally memory map the backing file.
751 *
752 *********************************************************************
753 */
754 if (psProperties->iMemoryMapFile)
755 {
756 psProperties->iMemoryMapSize = iOffset;
757 #ifdef WINNT
758 hMemoryMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, 0);
759 if (hMemoryMap == NULL)
760 {
761 ErrorFormatWinxError(GetLastError(), &pcMessage);
762 snprintf(pcError, MESSAGE_SIZE, "%s: CreateFileMapping(): %s", acRoutine, pcMessage);
763 return ER;
764 }
765 psProperties->pvMemoryMap = MapViewOfFile(hMemoryMap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
766 if (psProperties->pvMemoryMap == NULL)
767 {
768 ErrorFormatWinxError(GetLastError(), &pcMessage);
769 snprintf(pcError, MESSAGE_SIZE, "%s: MapViewOfFile(): %s", acRoutine, pcMessage);
770 return ER;
771 }
772 CloseHandle(hMemoryMap);
773 CloseHandle(hFile);
774 #else
775 psProperties->pvMemoryMap = mmap(0, psProperties->iMemoryMapSize, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(pFile), 0);
776 #if defined(FTimes_HPUX) && !defined(MAP_FAILED)
777 #define MAP_FAILED ((void *)-1)
778 #endif
779 if (psProperties->pvMemoryMap == MAP_FAILED)
780 {
781 snprintf(pcError, MESSAGE_SIZE, "%s: mmap(): %s", acRoutine, strerror(errno));
782 return ER;
783 }
784 #endif
785 fclose(pFile);
786 CompareSetNodeData(psProperties->aiBaselineKeys, psProperties->psBaselineNodes, psProperties->pvMemoryMap);
787 }
788
789 return ER_OK;
790 }
791
792
793 /*-
794 ***********************************************************************
795 *
796 * CompareNewProperties
797 *
798 ***********************************************************************
799 */
800 CMP_PROPERTIES *
CompareNewProperties(char * pcError)801 CompareNewProperties(char *pcError)
802 {
803 const char acRoutine[] = "CompareNewProperties()";
804 CMP_PROPERTIES *psProperties = NULL;
805 int i = 0;
806
807 /*
808 *********************************************************************
809 *
810 * Allocate and clear memory for the properties structure.
811 *
812 *********************************************************************
813 */
814 psProperties = (CMP_PROPERTIES *) calloc(sizeof(CMP_PROPERTIES), 1);
815 if (psProperties == NULL)
816 {
817 snprintf(pcError, MESSAGE_SIZE, "%s: calloc(): %s", acRoutine, strerror(errno));
818 return NULL;
819 }
820
821 /*
822 *********************************************************************
823 *
824 * Initialize BaselineKeys and SnapshotKeys variables.
825 *
826 *********************************************************************
827 */
828 for (i = 0; i < CMP_MODULUS; i++)
829 {
830 psProperties->aiBaselineKeys[i] = -1;
831 }
832
833 /*-
834 *********************************************************************
835 *
836 * Initialize NewLine variable.
837 *
838 *********************************************************************
839 */
840 #ifdef WIN32
841 strncpy(psProperties->acNewLine, CRLF, NEWLINE_LENGTH);
842 #else
843 strncpy(psProperties->acNewLine, LF, NEWLINE_LENGTH);
844 #endif
845
846 return psProperties;
847 }
848
849
850 /*-
851 ***********************************************************************
852 *
853 * CompareSetNewLine
854 *
855 ***********************************************************************
856 */
857 void
CompareSetNewLine(char * pcNewLine)858 CompareSetNewLine(char *pcNewLine)
859 {
860 strncpy(gpsCmpProperties->acNewLine, (strcmp(pcNewLine, CRLF) == 0) ? CRLF : LF, NEWLINE_LENGTH);
861 }
862
863
864 /*-
865 ***********************************************************************
866 *
867 * CompareSetNodeData
868 *
869 ***********************************************************************
870 */
871 void
CompareSetNodeData(int * piKeys,CMP_NODE * psNodes,void * pvBaseAddress)872 CompareSetNodeData(int *piKeys, CMP_NODE *psNodes, void *pvBaseAddress)
873 {
874 int iKeysIndex = 0;
875 int iNodeIndex = 0;
876
877 /*-
878 *********************************************************************
879 *
880 * The purpose of this routine is to initialize all pcData members.
881 * This step must be done after the backing file has been built and
882 * mapped into memory.
883 *
884 *********************************************************************
885 */
886 for (iKeysIndex = 0; iKeysIndex < CMP_MODULUS; iKeysIndex++)
887 {
888 iNodeIndex = piKeys[iKeysIndex];
889 while (iNodeIndex != -1)
890 {
891 psNodes[iNodeIndex].pcData = (char *) pvBaseAddress + psNodes[iNodeIndex].iOffset;
892 iNodeIndex = psNodes[iNodeIndex].iNextIndex;
893 }
894 }
895 }
896
897
898 /*-
899 ***********************************************************************
900 *
901 * CompareSetOutputStream
902 *
903 ***********************************************************************
904 */
905 void
CompareSetOutputStream(FILE * pFile)906 CompareSetOutputStream(FILE *pFile)
907 {
908 gpsCmpProperties->pFileOut = pFile;
909 }
910
911
912 /*-
913 ***********************************************************************
914 *
915 * CompareSetPropertiesReference
916 *
917 ***********************************************************************
918 */
919 void
CompareSetPropertiesReference(CMP_PROPERTIES * psProperties)920 CompareSetPropertiesReference(CMP_PROPERTIES *psProperties)
921 {
922 gpsCmpProperties = psProperties;
923 }
924
925
926 /*-
927 ***********************************************************************
928 *
929 * CompareWriteHeader
930 *
931 ***********************************************************************
932 */
933 int
CompareWriteHeader(FILE * pFile,char * pcNewLine,char * pcError)934 CompareWriteHeader(FILE *pFile, char *pcNewLine, char *pcError)
935 {
936 const char acRoutine[] = "CompareWriteHeader()";
937 char acHeader[FTIMES_MAX_LINE] = { 0 };
938 char acLocalError[MESSAGE_SIZE] = "";
939 int iError = 0;
940 int iIndex = 0;
941
942 iIndex = sprintf(acHeader, "category|name|changed|unknown|records%s", pcNewLine);
943
944 iError = SupportWriteData(pFile, acHeader, iIndex, acLocalError);
945 if (iError != ER_OK)
946 {
947 snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
948 return iError;
949 }
950
951 return ER_OK;
952 }
953
954
955 /*-
956 ***********************************************************************
957 *
958 * CompareWriteRecord
959 *
960 ***********************************************************************
961 */
962 int
CompareWriteRecord(CMP_PROPERTIES * psProperties,CMP_DATA * psData,char * pcError)963 CompareWriteRecord(CMP_PROPERTIES *psProperties, CMP_DATA *psData, char *pcError)
964 {
965 const char acRoutine[] = "CompareWriteRecord()";
966 static char *pcOutput = NULL;
967 static int iMaskTableLength = 0;
968 static MASK_B2S_TABLE *pasMaskTable = NULL;
969 char acLocalError[MESSAGE_SIZE] = "";
970 char *pc = NULL;
971 int i = 0;
972 int iError = 0;
973 int iFirst = 0;
974 int iIndex = 0;
975 unsigned long ul = 0;
976
977 /*-
978 *********************************************************************
979 *
980 * Allocate enough memory to hold one complete record, but only do
981 * this operation once. Note that this memory is never freed.
982 *
983 * category 1
984 * name CMP_MAX_LINE
985 * changed (iMaskTableLength * (MASK_NAME_SIZE))
986 * unknown (iMaskTableLength * (MASK_NAME_SIZE))
987 * records (2 * (FTIMES_MAX_32BIT_SIZE))
988 * |'s 3
989 * newline 2
990 *
991 *********************************************************************
992 */
993 if (pcOutput == NULL)
994 {
995 iMaskTableLength = MaskGetTableLength(MASK_RUNMODE_TYPE_CMP);
996 pasMaskTable = MaskGetTableReference(MASK_RUNMODE_TYPE_CMP);
997 pcOutput = malloc(CMP_MAX_LINE + (2 * (iMaskTableLength * (MASK_NAME_SIZE))) + (2 * (FTIMES_MAX_32BIT_SIZE)) + 6);
998 if (pcOutput == NULL)
999 {
1000 snprintf(pcError, MESSAGE_SIZE, "%s: malloc(): %s", acRoutine, strerror(errno));
1001 return ER;
1002 }
1003 }
1004
1005 /*-
1006 *********************************************************************
1007 *
1008 * Category = category
1009 *
1010 *********************************************************************
1011 */
1012 switch (psData->cCategory)
1013 {
1014 case 'C': /* changed */
1015 case 'M': /* missing */
1016 case 'N': /* new */
1017 case 'U': /* unknown */
1018 case 'X': /* both changed and unknown */
1019 pcOutput[0] = psData->cCategory;
1020 break;
1021 default:
1022 snprintf(pcError, MESSAGE_SIZE, "%s: Category = [%c] != [C|M|N|U|X]: That shouldn't happen.", acRoutine, psData->cCategory);
1023 return ER;
1024 break;
1025 }
1026 iIndex = 1;
1027
1028 /*-
1029 *********************************************************************
1030 *
1031 * Name = name
1032 *
1033 *********************************************************************
1034 */
1035 pc = strstr(&psData->pcRecord[1], "\"");
1036 if (psData->pcRecord[0] != '"' || pc == NULL)
1037 {
1038 snprintf(pcError, MESSAGE_SIZE, "%s: Name = [%s]: Name is not quoted. That shouldn't happen.", acRoutine, psData->pcRecord);
1039 return ER;
1040 }
1041 pcOutput[iIndex++] = CMP_SEPARATOR_C;
1042 for (i = 0; i <= pc - psData->pcRecord; i++)
1043 {
1044 pcOutput[iIndex++] = psData->pcRecord[i];
1045 }
1046
1047 /*-
1048 *********************************************************************
1049 *
1050 * Changed, Unknown, Cross = changed, unknown, cross
1051 *
1052 *********************************************************************
1053 */
1054 if (psData->cCategory == 'C' || psData->cCategory == 'U' || psData->cCategory == 'X')
1055 {
1056 pcOutput[iIndex++] = CMP_SEPARATOR_C;
1057 for (i = 0, iFirst = 0; i < iMaskTableLength; i++)
1058 {
1059 ul = 1 << i;
1060 if (MASK_BIT_IS_SET(psData->ulChangedMask, ul))
1061 {
1062 iIndex += sprintf(&pcOutput[iIndex], "%s%s", (iFirst++ > 0) ? "," : "", pasMaskTable[i].acName);
1063 }
1064 }
1065 pcOutput[iIndex++] = CMP_SEPARATOR_C;
1066 for (i = 0, iFirst = 0; i < iMaskTableLength; i++)
1067 {
1068 ul = 1 << i;
1069 if (MASK_BIT_IS_SET(psData->ulUnknownMask, ul))
1070 {
1071 iIndex += sprintf(&pcOutput[iIndex], "%s%s", (iFirst++ > 0) ? "," : "", pasMaskTable[i].acName);
1072 }
1073 }
1074 }
1075 else
1076 {
1077 pcOutput[iIndex++] = CMP_SEPARATOR_C;
1078 pcOutput[iIndex++] = CMP_SEPARATOR_C;
1079 }
1080
1081 /*-
1082 *********************************************************************
1083 *
1084 * Record numbers.
1085 *
1086 *********************************************************************
1087 */
1088 pcOutput[iIndex++] = CMP_SEPARATOR_C;
1089 iIndex += sprintf(&pcOutput[iIndex], "%d,%d", psData->iBaselineRecord, psData->iSnapshotRecord);
1090
1091 /*-
1092 *********************************************************************
1093 *
1094 * Newline
1095 *
1096 *********************************************************************
1097 */
1098 iIndex += sprintf(&pcOutput[iIndex], "%s", psProperties->acNewLine);
1099
1100 /*-
1101 *********************************************************************
1102 *
1103 * Write the output data.
1104 *
1105 *********************************************************************
1106 */
1107 iError = SupportWriteData(psProperties->pFileOut, pcOutput, iIndex, acLocalError);
1108 if (iError != ER_OK)
1109 {
1110 snprintf(pcError, MESSAGE_SIZE, "%s: %s", acRoutine, acLocalError);
1111 return iError;
1112 }
1113
1114 return ER_OK;
1115 }
1116