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