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
51typedef 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}
78Options;
79
80
81typedef 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}
92Switch;
93
94#define DESC_NEWLINE "\n\t\t"
95
96static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
97static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
98static Switch gSummarySwitch = {"--summary", "-s", 0, NULL, "Only output a single line." DESC_NEWLINE "The cumulative size changes." DESC_NEWLINE "Overrides all other output options."};
99static Switch gZeroDriftSwitch = {"--zerodrift", "-z", 0, NULL, "Output zero drift data." DESC_NEWLINE "Reports symbol changes even when there is no net drift."};
100static 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."};
101static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
102
103static Switch* gSwitches[] = {
104        &gInputSwitch,
105        &gOutputSwitch,
106        &gSummarySwitch,
107        &gZeroDriftSwitch,
108        &gNegationSwitch,
109        &gHelpSwitch
110};
111
112
113typedef 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}
121SizeComposition;
122
123
124typedef 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}
136SizeStats;
137
138
139typedef enum __enum_SegmentClass
140/*
141**  What type of data a segment holds.
142*/
143{
144        CODE,
145        DATA
146}
147SegmentClass;
148
149
150typedef struct __struct_SymbolStats
151/*
152**  Symbol level stats.
153*/
154{
155    char* mSymbol;
156    int mSize;
157}
158SymbolStats;
159
160
161typedef 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}
172ObjectStats;
173
174
175typedef 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}
187SegmentStats;
188
189
190typedef struct __struct_ModuleStats
191/*
192**  Module level stats.
193*/
194{
195    char* mModule;
196    SizeStats mSize;
197    SegmentStats* mSegments;
198    unsigned mSegmentCount;
199}
200ModuleStats;
201
202
203static 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
237static 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
268static 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
299static 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
330void 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
353int 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
1073int 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
1239void 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
1259void 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
1293int 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