Home | History | Annotate | Download | only in codesighs
      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 
    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 
    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 
    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 
    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 
    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 
    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 
   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 
   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 
   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 
   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