• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is maptsvdifftool.c code, released
17  * Oct 3, 2002.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 2002
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *   Garrett Arch Blythe, 03-October-2002
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <ctype.h>
46 
47 #define ERROR_REPORT(num, val, msg)   fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
48 #define CLEANUP(ptr)    do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
49 
50 
51 typedef struct __struct_Options
52 /*
53 **  Options to control how we perform.
54 **
55 **  mProgramName    Used in help text.
56 **  mInput          File to read for input.
57 **                  Default is stdin.
58 **  mInputName      Name of the file.
59 **  mOutput         Output file, append.
60 **                  Default is stdout.
61 **  mOutputName     Name of the file.
62 **  mHelp           Whether or not help should be shown.
63 **  mSummaryOnly    Only output a signle line.
64 **  mZeroDrift      Output zero drift data.
65 **  mNegation       Perform negation heuristics on the symbol drifts.
66 */
67 {
68     const char* mProgramName;
69     FILE* mInput;
70     char* mInputName;
71     FILE* mOutput;
72     char* mOutputName;
73     int mHelp;
74     int mSummaryOnly;
75     int mZeroDrift;
76     int mNegation;
77 }
78 Options;
79 
80 
81 typedef struct __struct_Switch
82 /*
83 **  Command line options.
84 */
85 {
86     const char* mLongName;
87     const char* mShortName;
88     int mHasValue;
89     const char* mValue;
90     const char* mDescription;
91 }
92 Switch;
93 
94 #define DESC_NEWLINE "\n\t\t"
95 
96 static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
97 static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
98 static Switch gSummarySwitch = {"--summary", "-s", 0, NULL, "Only output a single line." DESC_NEWLINE "The cumulative size changes." DESC_NEWLINE "Overrides all other output options."};
99 static Switch gZeroDriftSwitch = {"--zerodrift", "-z", 0, NULL, "Output zero drift data." DESC_NEWLINE "Reports symbol changes even when there is no net drift."};
100 static Switch gNegationSwitch = {"--negation", "-n", 0, NULL, "Use negation heuristics." DESC_NEWLINE "When symbol sizes are inferred by offset, order changes cause noise." DESC_NEWLINE "This helps see through the noise by eliminating equal and opposite drifts."};
101 static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
102 
103 static Switch* gSwitches[] = {
104         &gInputSwitch,
105         &gOutputSwitch,
106         &gSummarySwitch,
107         &gZeroDriftSwitch,
108         &gNegationSwitch,
109         &gHelpSwitch
110 };
111 
112 
113 typedef struct __struct_SizeComposition
114 /*
115 **  Used to keep which parts positive and negative resulted in the total.
116 */
117 {
118     int mPositive;
119     int mNegative;
120 }
121 SizeComposition;
122 
123 
124 typedef struct __struct_SizeStats
125 /*
126 **  Keep track of sizes.
127 **  Use signed integers so that negatives are valid, in which case we shrunk.
128 */
129 {
130     int mCode;
131     SizeComposition mCodeComposition;
132 
133     int mData;
134     SizeComposition mDataComposition;
135 }
136 SizeStats;
137 
138 
139 typedef enum __enum_SegmentClass
140 /*
141 **  What type of data a segment holds.
142 */
143 {
144         CODE,
145         DATA
146 }
147 SegmentClass;
148 
149 
150 typedef struct __struct_SymbolStats
151 /*
152 **  Symbol level stats.
153 */
154 {
155     char* mSymbol;
156     int mSize;
157 }
158 SymbolStats;
159 
160 
161 typedef struct __struct_ObjectStats
162 /*
163 **  Object level stats.
164 */
165 {
166     char* mObject;
167     int mSize;
168     SizeComposition mComposition;
169     SymbolStats* mSymbols;
170     unsigned mSymbolCount;
171 }
172 ObjectStats;
173 
174 
175 typedef struct __struct_SegmentStats
176 /*
177 **  Segment level stats.
178 */
179 {
180     char* mSegment;
181     SegmentClass mClass;
182     int mSize;
183     SizeComposition mComposition;
184     ObjectStats* mObjects;
185     unsigned mObjectCount;
186 }
187 SegmentStats;
188 
189 
190 typedef struct __struct_ModuleStats
191 /*
192 **  Module level stats.
193 */
194 {
195     char* mModule;
196     SizeStats mSize;
197     SegmentStats* mSegments;
198     unsigned mSegmentCount;
199 }
200 ModuleStats;
201 
202 
moduleCompare(const void * in1,const void * in2)203 static int moduleCompare(const void* in1, const void* in2)
204 /*
205 **  qsort helper.
206 */
207 {
208     int retval = 0;
209 
210     ModuleStats* one = (ModuleStats*)in1;
211     ModuleStats* two = (ModuleStats*)in2;
212 
213     int oneSize = (one->mSize.mCode + one->mSize.mData);
214     int twoSize = (two->mSize.mCode + two->mSize.mData);
215 
216     if(oneSize < twoSize)
217     {
218         retval = 1;
219     }
220     else if(oneSize > twoSize)
221     {
222         retval = -1;
223     }
224     else
225     {
226         retval = strcmp(one->mModule, two->mModule);
227         if(0 > oneSize && 0 > twoSize)
228         {
229             retval *= -1;
230         }
231     }
232 
233     return retval;
234 }
235 
236 
segmentCompare(const void * in1,const void * in2)237 static int segmentCompare(const void* in1, const void* in2)
238 /*
239 **  qsort helper.
240 */
241 {
242     int retval = 0;
243 
244     SegmentStats* one = (SegmentStats*)in1;
245     SegmentStats* two = (SegmentStats*)in2;
246 
247     if(one->mSize < two->mSize)
248     {
249         retval = 1;
250     }
251     else if(one->mSize > two->mSize)
252     {
253         retval = -1;
254     }
255     else
256     {
257         retval = strcmp(one->mSegment, two->mSegment);
258         if(0 > one->mSize && 0 > two->mSize)
259         {
260             retval *= -1;
261         }
262     }
263 
264     return retval;
265 }
266 
267 
objectCompare(const void * in1,const void * in2)268 static int objectCompare(const void* in1, const void* in2)
269 /*
270 **  qsort helper.
271 */
272 {
273     int retval = 0;
274 
275     ObjectStats* one = (ObjectStats*)in1;
276     ObjectStats* two = (ObjectStats*)in2;
277 
278     if(one->mSize < two->mSize)
279     {
280         retval = 1;
281     }
282     else if(one->mSize > two->mSize)
283     {
284         retval = -1;
285     }
286     else
287     {
288         retval = strcmp(one->mObject, two->mObject);
289         if(0 > one->mSize && 0 > two->mSize)
290         {
291             retval *= -1;
292         }
293     }
294 
295     return retval;
296 }
297 
298 
symbolCompare(const void * in1,const void * in2)299 static int symbolCompare(const void* in1, const void* in2)
300 /*
301 **  qsort helper.
302 */
303 {
304     int retval = 0;
305 
306     SymbolStats* one = (SymbolStats*)in1;
307     SymbolStats* two = (SymbolStats*)in2;
308 
309     if(one->mSize < two->mSize)
310     {
311         retval = 1;
312     }
313     else if(one->mSize > two->mSize)
314     {
315         retval = -1;
316     }
317     else
318     {
319         retval = strcmp(one->mSymbol, two->mSymbol);
320         if(0 > one->mSize && 0 > two->mSize)
321         {
322             retval *= -1;
323         }
324     }
325 
326     return retval;
327 }
328 
329 
trimWhite(char * inString)330 void trimWhite(char* inString)
331 /*
332 **  Remove any whitespace from the end of the string.
333 */
334 {
335     int len = strlen(inString);
336 
337     while(len)
338     {
339         len--;
340 
341         if(isspace(*(inString + len)))
342         {
343             *(inString + len) = '\0';
344         }
345         else
346         {
347             break;
348         }
349     }
350 }
351 
352 
difftool(Options * inOptions)353 int difftool(Options* inOptions)
354 /*
355 **  Read a diff file and spit out relevant information.
356 */
357 {
358     int retval = 0;
359     char lineBuffer[0x500];
360     SizeStats overall;
361     ModuleStats* modules = NULL;
362     unsigned moduleCount = 0;
363     unsigned moduleLoop = 0;
364     ModuleStats* theModule = NULL;
365     unsigned segmentLoop = 0;
366     SegmentStats* theSegment = NULL;
367     unsigned objectLoop = 0;
368     ObjectStats* theObject = NULL;
369     unsigned symbolLoop = 0;
370     SymbolStats* theSymbol = NULL;
371     unsigned allSymbolCount = 0;
372 
373     memset(&overall, 0, sizeof(overall));
374 
375     /*
376     **  Read the entire diff file.
377     **  We're only interested in lines beginning with < or >
378     */
379     while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput))
380     {
381         trimWhite(lineBuffer);
382 
383         if(('<' == lineBuffer[0] || '>' == lineBuffer[0]) && ' ' == lineBuffer[1])
384         {
385             int additive = 0;
386             char* theLine = &lineBuffer[2];
387             int scanRes = 0;
388             int size;
389             char segClass[0x10];
390             char scope[0x10];
391             char module[0x100];
392             char segment[0x40];
393             char object[0x100];
394             char* symbol = NULL;
395 
396             /*
397             **  Figure out if the line adds or subtracts from something.
398             */
399             if('>' == lineBuffer[0])
400             {
401                 additive = __LINE__;
402             }
403 
404 
405             /*
406             **  Scan the line for information.
407             */
408             scanRes = sscanf(theLine,
409                 "%x\t%s\t%s\t%s\t%s\t%s\t",
410                 (unsigned*)&size,
411                 segClass,
412                 scope,
413                 module,
414                 segment,
415                 object);
416 
417             if(6 == scanRes)
418             {
419                 SegmentClass segmentClass = DATA;
420 
421                 symbol = strrchr(theLine, '\t') + 1;
422 
423                 if(0 == strcmp(segClass, "CODE"))
424                 {
425                     segmentClass = CODE;
426                 }
427                 else if(0 == strcmp(segClass, "DATA"))
428                 {
429                     segmentClass = DATA;
430                 }
431                 else
432                 {
433                     retval = __LINE__;
434                     ERROR_REPORT(retval, segClass, "Unable to determine segment class.");
435                 }
436 
437                 if(0 == retval)
438                 {
439                     unsigned moduleIndex = 0;
440 
441                     /*
442                     **  Find, in succession, the following things:
443                     **      the module
444                     **      the segment
445                     **      the object
446                     **      the symbol
447                     **  Failure to find any one of these means to create it.
448                     */
449 
450                     for(moduleIndex = 0; moduleIndex < moduleCount; moduleIndex++)
451                     {
452                         if(0 == strcmp(modules[moduleIndex].mModule, module))
453                         {
454                             break;
455                         }
456                     }
457 
458                     if(moduleIndex == moduleCount)
459                     {
460                         void* moved = NULL;
461 
462                         moved = realloc(modules, sizeof(ModuleStats) * (1 + moduleCount));
463                         if(NULL != moved)
464                         {
465                             modules = (ModuleStats*)moved;
466                             moduleCount++;
467                             memset(modules + moduleIndex, 0, sizeof(ModuleStats));
468 
469                             modules[moduleIndex].mModule = strdup(module);
470                             if(NULL == modules[moduleIndex].mModule)
471                             {
472                                 retval = __LINE__;
473                                 ERROR_REPORT(retval, module, "Unable to duplicate string.");
474                             }
475                         }
476                         else
477                         {
478                             retval = __LINE__;
479                             ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase module array.");
480                         }
481                     }
482 
483                     if(0 == retval)
484                     {
485                         unsigned segmentIndex = 0;
486                         theModule = (modules + moduleIndex);
487 
488                         for(segmentIndex = 0; segmentIndex < theModule->mSegmentCount; segmentIndex++)
489                         {
490                             if(0 == strcmp(segment, theModule->mSegments[segmentIndex].mSegment))
491                             {
492                                 break;
493                             }
494                         }
495 
496                         if(segmentIndex == theModule->mSegmentCount)
497                         {
498                             void* moved = NULL;
499 
500                             moved = realloc(theModule->mSegments, sizeof(SegmentStats) * (theModule->mSegmentCount + 1));
501                             if(NULL != moved)
502                             {
503                                 theModule->mSegments = (SegmentStats*)moved;
504                                 theModule->mSegmentCount++;
505                                 memset(theModule->mSegments + segmentIndex, 0, sizeof(SegmentStats));
506 
507                                 theModule->mSegments[segmentIndex].mClass = segmentClass;
508                                 theModule->mSegments[segmentIndex].mSegment = strdup(segment);
509                                 if(NULL == theModule->mSegments[segmentIndex].mSegment)
510                                 {
511                                     retval = __LINE__;
512                                     ERROR_REPORT(retval, segment, "Unable to duplicate string.");
513                                 }
514                             }
515                             else
516                             {
517                                 retval = __LINE__;
518                                 ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase segment array.");
519                             }
520                         }
521 
522                         if(0 == retval)
523                         {
524                             unsigned objectIndex = 0;
525                             theSegment = (theModule->mSegments + segmentIndex);
526 
527                             for(objectIndex = 0; objectIndex < theSegment->mObjectCount; objectIndex++)
528                             {
529                                 if(0 == strcmp(object, theSegment->mObjects[objectIndex].mObject))
530                                 {
531                                     break;
532                                 }
533                             }
534 
535                             if(objectIndex == theSegment->mObjectCount)
536                             {
537                                 void* moved = NULL;
538 
539                                 moved = realloc(theSegment->mObjects, sizeof(ObjectStats) * (1 + theSegment->mObjectCount));
540                                 if(NULL != moved)
541                                 {
542                                     theSegment->mObjects = (ObjectStats*)moved;
543                                     theSegment->mObjectCount++;
544                                     memset(theSegment->mObjects + objectIndex, 0, sizeof(ObjectStats));
545 
546                                     theSegment->mObjects[objectIndex].mObject = strdup(object);
547                                     if(NULL == theSegment->mObjects[objectIndex].mObject)
548                                     {
549                                         retval = __LINE__;
550                                         ERROR_REPORT(retval, object, "Unable to duplicate string.");
551                                     }
552                                 }
553                                 else
554                                 {
555                                     retval = __LINE__;
556                                     ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase object array.");
557                                 }
558                             }
559 
560                             if(0 == retval)
561                             {
562                                 unsigned symbolIndex = 0;
563                                 theObject = (theSegment->mObjects + objectIndex);
564 
565                                 for(symbolIndex = 0; symbolIndex < theObject->mSymbolCount; symbolIndex++)
566                                 {
567                                     if(0 == strcmp(symbol, theObject->mSymbols[symbolIndex].mSymbol))
568                                     {
569                                         break;
570                                     }
571                                 }
572 
573                                 if(symbolIndex == theObject->mSymbolCount)
574                                 {
575                                     void* moved = NULL;
576 
577                                     moved = realloc(theObject->mSymbols, sizeof(SymbolStats) * (1 + theObject->mSymbolCount));
578                                     if(NULL != moved)
579                                     {
580                                         theObject->mSymbols = (SymbolStats*)moved;
581                                         theObject->mSymbolCount++;
582                                         allSymbolCount++;
583                                         memset(theObject->mSymbols + symbolIndex, 0, sizeof(SymbolStats));
584 
585                                         theObject->mSymbols[symbolIndex].mSymbol = strdup(symbol);
586                                         if(NULL == theObject->mSymbols[symbolIndex].mSymbol)
587                                         {
588                                             retval = __LINE__;
589                                             ERROR_REPORT(retval, symbol, "Unable to duplicate string.");
590                                         }
591                                     }
592                                     else
593                                     {
594                                         retval = __LINE__;
595                                         ERROR_REPORT(retval, inOptions->mProgramName, "Unable to increase symbol array.");
596                                     }
597                                 }
598 
599                                 if(0 == retval)
600                                 {
601                                     theSymbol = (theObject->mSymbols + symbolIndex);
602 
603                                     /*
604                                     **  Update our various totals.
605                                     */
606                                     if(additive)
607                                     {
608                                         if(CODE == segmentClass)
609                                         {
610                                             overall.mCode += size;
611                                             theModule->mSize.mCode += size;
612                                         }
613                                         else if(DATA == segmentClass)
614                                         {
615                                             overall.mData += size;
616                                             theModule->mSize.mData += size;
617                                         }
618 
619                                         theSegment->mSize += size;
620                                         theObject->mSize += size;
621                                         theSymbol->mSize += size;
622                                     }
623                                     else
624                                     {
625                                         if(CODE == segmentClass)
626                                         {
627                                             overall.mCode -= size;
628                                             theModule->mSize.mCode -= size;
629                                         }
630                                         else if(DATA == segmentClass)
631                                         {
632                                             overall.mData -= size;
633                                             theModule->mSize.mData -= size;
634                                         }
635 
636                                         theSegment->mSize -= size;
637                                         theObject->mSize -= size;
638                                         theSymbol->mSize -= size;
639                                     }
640                                 }
641                             }
642                         }
643                     }
644                 }
645             }
646             else
647             {
648                 retval = __LINE__;
649                 ERROR_REPORT(retval, inOptions->mInputName, "Unable to scan line data.");
650             }
651         }
652     }
653 
654     if(0 == retval && 0 != ferror(inOptions->mInput))
655     {
656         retval = __LINE__;
657         ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file.");
658     }
659 
660     /*
661     **  Next, it is time to perform revisionist history of sorts.
662     **  If the negation switch is in play, we perfrom the following
663     **      aggressive steps:
664     **
665     **  For each section, find size changes which have an equal and
666     **      opposite change, and set them both to zero.
667     **  However, you can only do this if the number of negating changes
668     **      is even, as if it is odd, then any one of the many could be
669     **      at fault for the actual change.
670     **
671     **  This orginally exists to make the win32 codesighs reports more
672     **      readable/meaningful.
673     */
674     if(0 == retval && 0 != inOptions->mNegation)
675     {
676         ObjectStats** objArray = NULL;
677         SymbolStats** symArray = NULL;
678 
679         /*
680         **  Create arrays big enough to hold all symbols.
681         **  As well as an array to keep the owning object at the same index.
682         **  We will keep the object around as we may need to modify the size.
683         */
684         objArray = (ObjectStats**)malloc(allSymbolCount * sizeof(ObjectStats*));
685         symArray = (SymbolStats**)malloc(allSymbolCount * sizeof(SymbolStats*));
686         if(NULL == objArray || NULL == symArray)
687         {
688             retval = __LINE__;
689             ERROR_REPORT(retval, inOptions->mProgramName, "Unable to allocate negation array memory.");
690         }
691         else
692         {
693             unsigned arrayCount = 0;
694             unsigned arrayLoop = 0;
695 
696             /*
697             **  Go through and perform the steps on each section/segment.
698             */
699             for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++)
700             {
701                 theModule = modules + moduleLoop;
702 
703                 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++)
704                 {
705                     theSegment = theModule->mSegments + segmentLoop;
706 
707                     /*
708                     **  Collect all symbols under this section.
709                     **  The symbols are spread out between all the objects,
710                     **      so keep track of both independently at the
711                     **      same index.
712                     */
713                     arrayCount = 0;
714 
715                     for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++)
716                     {
717                         theObject = theSegment->mObjects + objectLoop;
718 
719                         for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++)
720                         {
721                             theSymbol = theObject->mSymbols + symbolLoop;
722 
723                             objArray[arrayCount] = theObject;
724                             symArray[arrayCount] = theSymbol;
725                             arrayCount++;
726                         }
727                     }
728 
729                     /*
730                     **  Now that we have a list of symbols, go through each
731                     **      and see if there is a chance of negation.
732                     */
733                     for(arrayLoop = 0; arrayLoop < arrayCount; arrayLoop++)
734                     {
735                         /*
736                         **  If the item is NULL, it was already negated.
737                         **  Don't do this for items with a zero size.
738                         */
739                         if(NULL != symArray[arrayLoop] && 0 != symArray[arrayLoop]->mSize)
740                         {
741                             unsigned identicalValues = 0;
742                             unsigned oppositeValues = 0;
743                             unsigned lookLoop = 0;
744                             const int lookingFor = symArray[arrayLoop]->mSize;
745 
746                             /*
747                             **  Count the number of items with this value.
748                             **  Count the number of items with the opposite equal value.
749                             **  If they are equal, go through and negate all sizes.
750                             */
751                             for(lookLoop = arrayLoop; lookLoop < arrayCount; lookLoop++)
752                             {
753                                 /*
754                                 **  Skip negated items.
755                                 **  Skip zero length items.
756                                 */
757                                 if(NULL == symArray[lookLoop] || 0 == symArray[lookLoop]->mSize)
758                                 {
759                                     continue;
760                                 }
761 
762                                 if(lookingFor == symArray[lookLoop]->mSize)
763                                 {
764                                     identicalValues++;
765                                 }
766                                 else if((-1 * lookingFor) == symArray[lookLoop]->mSize)
767                                 {
768                                     oppositeValues++;
769                                 }
770                             }
771 
772                             if(0 != identicalValues && identicalValues == oppositeValues)
773                             {
774                                 unsigned negationLoop = 0;
775 
776                                 for(negationLoop = arrayLoop; 0 != identicalValues || 0 != oppositeValues; negationLoop++)
777                                 {
778                                     /*
779                                     **  Skip negated items.
780                                     **  Skip zero length items.
781                                     */
782                                     if(NULL == symArray[negationLoop] || 0 == symArray[negationLoop]->mSize)
783                                     {
784                                         continue;
785                                     }
786 
787                                     /*
788                                     **  Negate any size matches.
789                                     **  Reflect the change in the object as well.
790                                     **  Clear the symbol.
791                                     */
792                                     if(lookingFor == symArray[negationLoop]->mSize)
793                                     {
794                                         objArray[negationLoop]->mSize -= lookingFor;
795                                         symArray[negationLoop]->mSize = 0;
796                                         symArray[negationLoop] = NULL;
797 
798                                         identicalValues--;
799                                     }
800                                     else if((-1 * lookingFor) == symArray[negationLoop]->mSize)
801                                     {
802                                         objArray[negationLoop]->mSize += lookingFor;
803                                         symArray[negationLoop]->mSize = 0;
804                                         symArray[negationLoop] = NULL;
805 
806                                         oppositeValues--;
807                                     }
808                                 }
809                             }
810                         }
811                     }
812                 }
813             }
814         }
815 
816         CLEANUP(objArray);
817         CLEANUP(symArray);
818     }
819 
820 
821     /*
822     **  If all went well, time to report.
823     */
824     if(0 == retval)
825     {
826         /*
827         **  Loop through our data once more, so that the symbols can
828         **      propigate their changes upwards in a positive/negative
829         **      fashion.
830         **  This will help give the composite change more meaning.
831         */
832         for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++)
833         {
834             theModule = modules + moduleLoop;
835 
836             /*
837             **  Skip if there is zero drift, or no net change.
838             */
839             if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData))
840             {
841                 continue;
842             }
843 
844             for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++)
845             {
846                 theSegment = theModule->mSegments + segmentLoop;
847 
848                 /*
849                 **  Skip if there is zero drift, or no net change.
850                 */
851                 if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize)
852                 {
853                     continue;
854                 }
855 
856                 for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++)
857                 {
858                     theObject = theSegment->mObjects + objectLoop;
859 
860                     /*
861                     **  Skip if there is zero drift, or no net change.
862                     */
863                     if(0 == inOptions->mZeroDrift && 0 == theObject->mSize)
864                     {
865                         continue;
866                     }
867 
868                     for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++)
869                     {
870                         theSymbol = theObject->mSymbols + symbolLoop;
871 
872                         /*
873                         **  Propagate the composition all the way to the top.
874                         **  Sizes of zero change are skipped.
875                         */
876                         if(0 < theSymbol->mSize)
877                         {
878                             theObject->mComposition.mPositive += theSymbol->mSize;
879                             theSegment->mComposition.mPositive += theSymbol->mSize;
880                             if(CODE == theSegment->mClass)
881                             {
882                                 overall.mCodeComposition.mPositive += theSymbol->mSize;
883                                 theModule->mSize.mCodeComposition.mPositive += theSymbol->mSize;
884                             }
885                             else if(DATA == theSegment->mClass)
886                             {
887                                 overall.mDataComposition.mPositive += theSymbol->mSize;
888                                 theModule->mSize.mDataComposition.mPositive += theSymbol->mSize;
889                             }
890                         }
891                         else if(0 > theSymbol->mSize)
892                         {
893                             theObject->mComposition.mNegative += theSymbol->mSize;
894                             theSegment->mComposition.mNegative += theSymbol->mSize;
895                             if(CODE == theSegment->mClass)
896                             {
897                                 overall.mCodeComposition.mNegative += theSymbol->mSize;
898                                 theModule->mSize.mCodeComposition.mNegative += theSymbol->mSize;
899                             }
900                             else if(DATA == theSegment->mClass)
901                             {
902                                 overall.mDataComposition.mNegative += theSymbol->mSize;
903                                 theModule->mSize.mDataComposition.mNegative += theSymbol->mSize;
904                             }
905                         }
906                     }
907                 }
908             }
909         }
910 
911 
912         if(inOptions->mSummaryOnly)
913         {
914             fprintf(inOptions->mOutput, "%+d (%+d/%+d)\n", overall.mCode + overall.mData, overall.mCodeComposition.mPositive + overall.mDataComposition.mPositive, overall.mCodeComposition.mNegative + overall.mDataComposition.mNegative);
915         }
916         else
917         {
918             fprintf(inOptions->mOutput, "Overall Change in Size\n");
919             fprintf(inOptions->mOutput, "\tTotal:\t%+11d (%+d/%+d)\n", overall.mCode + overall.mData, overall.mCodeComposition.mPositive + overall.mDataComposition.mPositive, overall.mCodeComposition.mNegative + overall.mDataComposition.mNegative);
920             fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", overall.mCode, overall.mCodeComposition.mPositive, overall.mCodeComposition.mNegative);
921             fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", overall.mData, overall.mDataComposition.mPositive, overall.mDataComposition.mNegative);
922         }
923 
924         /*
925         **  Check what else we should output.
926         */
927         if(0 == inOptions->mSummaryOnly && NULL != modules && moduleCount)
928         {
929             const char* segmentType = NULL;
930 
931             /*
932             **  We're going to sort everything.
933             */
934             qsort(modules, moduleCount, sizeof(ModuleStats), moduleCompare);
935             for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++)
936             {
937                 theModule = modules + moduleLoop;
938 
939                 qsort(theModule->mSegments, theModule->mSegmentCount, sizeof(SegmentStats), segmentCompare);
940 
941                 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++)
942                 {
943                     theSegment = theModule->mSegments + segmentLoop;
944 
945                     qsort(theSegment->mObjects, theSegment->mObjectCount, sizeof(ObjectStats), objectCompare);
946 
947                     for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++)
948                     {
949                         theObject = theSegment->mObjects + objectLoop;
950 
951                         qsort(theObject->mSymbols, theObject->mSymbolCount, sizeof(SymbolStats), symbolCompare);
952                     }
953                 }
954             }
955 
956             /*
957             **  Loop through for output.
958             */
959             for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++)
960             {
961                 theModule = modules + moduleLoop;
962 
963                 /*
964                 **  Skip if there is zero drift, or no net change.
965                 */
966                 if(0 == inOptions->mZeroDrift && 0 == (theModule->mSize.mCode + theModule->mSize.mData))
967                 {
968                     continue;
969                 }
970 
971                 fprintf(inOptions->mOutput, "\n");
972                 fprintf(inOptions->mOutput, "%s\n", theModule->mModule);
973                 fprintf(inOptions->mOutput, "\tTotal:\t%+11d (%+d/%+d)\n", theModule->mSize.mCode + theModule->mSize.mData, theModule->mSize.mCodeComposition.mPositive + theModule->mSize.mDataComposition.mPositive, theModule->mSize.mCodeComposition.mNegative + theModule->mSize.mDataComposition.mNegative);
974                 fprintf(inOptions->mOutput, "\tCode:\t%+11d (%+d/%+d)\n", theModule->mSize.mCode, theModule->mSize.mCodeComposition.mPositive, theModule->mSize.mCodeComposition.mNegative);
975                 fprintf(inOptions->mOutput, "\tData:\t%+11d (%+d/%+d)\n", theModule->mSize.mData, theModule->mSize.mDataComposition.mPositive, theModule->mSize.mDataComposition.mNegative);
976 
977                 for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++)
978                 {
979                     theSegment = theModule->mSegments + segmentLoop;
980 
981                     /*
982                     **  Skip if there is zero drift, or no net change.
983                     */
984                     if(0 == inOptions->mZeroDrift && 0 == theSegment->mSize)
985                     {
986                         continue;
987                     }
988 
989                     if(CODE == theSegment->mClass)
990                     {
991                         segmentType = "CODE";
992                     }
993                     else if(DATA == theSegment->mClass)
994                     {
995                         segmentType = "DATA";
996                     }
997 
998                     fprintf(inOptions->mOutput, "\t%+11d (%+d/%+d)\t%s (%s)\n", theSegment->mSize, theSegment->mComposition.mPositive, theSegment->mComposition.mNegative, theSegment->mSegment, segmentType);
999 
1000                     for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++)
1001                     {
1002                         theObject = theSegment->mObjects + objectLoop;
1003 
1004                         /*
1005                         **  Skip if there is zero drift, or no net change.
1006                         */
1007                         if(0 == inOptions->mZeroDrift && 0 == theObject->mSize)
1008                         {
1009                             continue;
1010                         }
1011 
1012                         fprintf(inOptions->mOutput, "\t\t%+11d (%+d/%+d)\t%s\n", theObject->mSize, theObject->mComposition.mPositive, theObject->mComposition.mNegative, theObject->mObject);
1013 
1014                         for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++)
1015                         {
1016                             theSymbol = theObject->mSymbols + symbolLoop;
1017 
1018                             /*
1019                             **  Skip if there is zero drift, or no net change.
1020                             */
1021                             if(0 == inOptions->mZeroDrift && 0 == theSymbol->mSize)
1022                             {
1023                                 continue;
1024                             }
1025 
1026                             fprintf(inOptions->mOutput, "\t\t\t%+11d\t%s\n", theSymbol->mSize, theSymbol->mSymbol);
1027                         }
1028                     }
1029                 }
1030             }
1031         }
1032     }
1033 
1034     /*
1035     **  Cleanup time.
1036     */
1037     for(moduleLoop = 0; moduleLoop < moduleCount; moduleLoop++)
1038     {
1039         theModule = modules + moduleLoop;
1040 
1041         for(segmentLoop = 0; segmentLoop < theModule->mSegmentCount; segmentLoop++)
1042         {
1043             theSegment = theModule->mSegments + segmentLoop;
1044 
1045             for(objectLoop = 0; objectLoop < theSegment->mObjectCount; objectLoop++)
1046             {
1047                 theObject = theSegment->mObjects + objectLoop;
1048 
1049                 for(symbolLoop = 0; symbolLoop < theObject->mSymbolCount; symbolLoop++)
1050                 {
1051                     theSymbol = theObject->mSymbols + symbolLoop;
1052 
1053                     CLEANUP(theSymbol->mSymbol);
1054                 }
1055 
1056                 CLEANUP(theObject->mSymbols);
1057                 CLEANUP(theObject->mObject);
1058             }
1059 
1060             CLEANUP(theSegment->mObjects);
1061             CLEANUP(theSegment->mSegment);
1062         }
1063 
1064         CLEANUP(theModule->mSegments);
1065         CLEANUP(theModule->mModule);
1066     }
1067     CLEANUP(modules);
1068 
1069     return retval;
1070 }
1071 
1072 
initOptions(Options * outOptions,int inArgc,char ** inArgv)1073 int initOptions(Options* outOptions, int inArgc, char** inArgv)
1074 /*
1075 **  returns int     0 if successful.
1076 */
1077 {
1078     int retval = 0;
1079     int loop = 0;
1080     int switchLoop = 0;
1081     int match = 0;
1082     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
1083     Switch* current = NULL;
1084 
1085     /*
1086     **  Set any defaults.
1087     */
1088     memset(outOptions, 0, sizeof(Options));
1089     outOptions->mProgramName = inArgv[0];
1090     outOptions->mInput = stdin;
1091     outOptions->mInputName = strdup("stdin");
1092     outOptions->mOutput = stdout;
1093     outOptions->mOutputName = strdup("stdout");
1094 
1095     if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
1096     {
1097         retval = __LINE__;
1098         ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
1099     }
1100 
1101     /*
1102     **  Go through and attempt to do the right thing.
1103     */
1104     for(loop = 1; loop < inArgc && 0 == retval; loop++)
1105     {
1106         match = 0;
1107         current = NULL;
1108 
1109         for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
1110         {
1111             if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
1112             {
1113                 match = __LINE__;
1114             }
1115             else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
1116             {
1117                 match = __LINE__;
1118             }
1119 
1120             if(match)
1121             {
1122                 if(gSwitches[switchLoop]->mHasValue)
1123                 {
1124                     /*
1125                     **  Attempt to absorb next option to fullfill value.
1126                     */
1127                     if(loop + 1 < inArgc)
1128                     {
1129                         loop++;
1130 
1131                         current = gSwitches[switchLoop];
1132                         current->mValue = inArgv[loop];
1133                     }
1134                 }
1135                 else
1136                 {
1137                     current = gSwitches[switchLoop];
1138                 }
1139 
1140                 break;
1141             }
1142         }
1143 
1144         if(0 == match)
1145         {
1146             outOptions->mHelp = __LINE__;
1147             retval = __LINE__;
1148             ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
1149         }
1150         else if(NULL == current)
1151         {
1152             outOptions->mHelp = __LINE__;
1153             retval = __LINE__;
1154             ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
1155         }
1156         else
1157         {
1158             /*
1159             ** Do something based on address/swtich.
1160             */
1161             if(current == &gInputSwitch)
1162             {
1163                 CLEANUP(outOptions->mInputName);
1164                 if(NULL != outOptions->mInput && stdin != outOptions->mInput)
1165                 {
1166                     fclose(outOptions->mInput);
1167                     outOptions->mInput = NULL;
1168                 }
1169 
1170                 outOptions->mInput = fopen(current->mValue, "r");
1171                 if(NULL == outOptions->mInput)
1172                 {
1173                     retval = __LINE__;
1174                     ERROR_REPORT(retval, current->mValue, "Unable to open input file.");
1175                 }
1176                 else
1177                 {
1178                     outOptions->mInputName = strdup(current->mValue);
1179                     if(NULL == outOptions->mInputName)
1180                     {
1181                         retval = __LINE__;
1182                         ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
1183                     }
1184                 }
1185             }
1186             else if(current == &gOutputSwitch)
1187             {
1188                 CLEANUP(outOptions->mOutputName);
1189                 if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
1190                 {
1191                     fclose(outOptions->mOutput);
1192                     outOptions->mOutput = NULL;
1193                 }
1194 
1195                 outOptions->mOutput = fopen(current->mValue, "a");
1196                 if(NULL == outOptions->mOutput)
1197                 {
1198                     retval = __LINE__;
1199                     ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
1200                 }
1201                 else
1202                 {
1203                     outOptions->mOutputName = strdup(current->mValue);
1204                     if(NULL == outOptions->mOutputName)
1205                     {
1206                         retval = __LINE__;
1207                         ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
1208                     }
1209                 }
1210             }
1211             else if(current == &gHelpSwitch)
1212             {
1213                 outOptions->mHelp = __LINE__;
1214             }
1215             else if(current == &gSummarySwitch)
1216             {
1217                 outOptions->mSummaryOnly = __LINE__;
1218             }
1219             else if(current == &gZeroDriftSwitch)
1220             {
1221                 outOptions->mZeroDrift = __LINE__;
1222             }
1223             else if(current == &gNegationSwitch)
1224             {
1225                 outOptions->mNegation = __LINE__;
1226             }
1227             else
1228             {
1229                 retval = __LINE__;
1230                 ERROR_REPORT(retval, current->mLongName, "No handler for command line switch.");
1231             }
1232         }
1233     }
1234 
1235     return retval;
1236 }
1237 
1238 
cleanOptions(Options * inOptions)1239 void cleanOptions(Options* inOptions)
1240 /*
1241 **  Clean up any open handles.
1242 */
1243 {
1244     CLEANUP(inOptions->mInputName);
1245     if(NULL != inOptions->mInput && stdin != inOptions->mInput)
1246     {
1247         fclose(inOptions->mInput);
1248     }
1249     CLEANUP(inOptions->mOutputName);
1250     if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
1251     {
1252         fclose(inOptions->mOutput);
1253     }
1254 
1255     memset(inOptions, 0, sizeof(Options));
1256 }
1257 
1258 
showHelp(Options * inOptions)1259 void showHelp(Options* inOptions)
1260 /*
1261 **  Show some simple help text on usage.
1262 */
1263 {
1264     int loop = 0;
1265     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
1266     const char* valueText = NULL;
1267 
1268     printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
1269     printf("\n");
1270     printf("arguments:\n");
1271 
1272     for(loop = 0; loop < switchCount; loop++)
1273     {
1274         if(gSwitches[loop]->mHasValue)
1275         {
1276             valueText = " <value>";
1277         }
1278         else
1279         {
1280             valueText = "";
1281         }
1282 
1283         printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
1284         printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
1285         printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
1286     }
1287 
1288     printf("This tool takes the diff of two sorted tsv files to form a summary report\n");
1289     printf("of code and data size changes which is hoped to be human readable.\n");
1290 }
1291 
1292 
main(int inArgc,char ** inArgv)1293 int main(int inArgc, char** inArgv)
1294 {
1295     int retval = 0;
1296     Options options;
1297 
1298     retval = initOptions(&options, inArgc, inArgv);
1299     if(options.mHelp)
1300     {
1301         showHelp(&options);
1302     }
1303     else if(0 == retval)
1304     {
1305         retval = difftool(&options);
1306     }
1307 
1308     cleanOptions(&options);
1309     return retval;
1310 }
1311 
1312