1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
13 #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
14 #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
17 typedef struct __struct_Options
19 ** Options to control how we perform.
21 ** mProgramName Used in help text.
22 ** mInput File to read for input.
24 ** mInputName Name of the file.
25 ** mOutput Output file, append.
27 ** mOutputName Name of the file.
28 ** mHelp Whether or not help should be shown.
29 ** mSummaryOnly Only output a signle line.
30 ** mZeroDrift Output zero drift data.
31 ** mNegation Perform negation heuristics on the symbol drifts.
34 const char* mProgramName
;
47 typedef struct __struct_Switch
49 ** Command line options.
52 const char* mLongName
;
53 const char* mShortName
;
56 const char* mDescription
;
60 #define DESC_NEWLINE "\n\t\t"
62 static Switch gInputSwitch
= {"--input", "-i", 1, NULL
, "Specify input file." DESC_NEWLINE
"stdin is default."};
63 static Switch gOutputSwitch
= {"--output", "-o", 1, NULL
, "Specify output file." DESC_NEWLINE
"Appends if file exists." DESC_NEWLINE
"stdout is default."};
64 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."};
65 static Switch gZeroDriftSwitch
= {"--zerodrift", "-z", 0, NULL
, "Output zero drift data." DESC_NEWLINE
"Reports symbol changes even when there is no net drift."};
66 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."};
67 static Switch gHelpSwitch
= {"--help", "-h", 0, NULL
, "Information on usage."};
69 static Switch
* gSwitches
[] = {
79 typedef struct __struct_SizeComposition
81 ** Used to keep which parts positive and negative resulted in the total.
90 typedef struct __struct_SizeStats
92 ** Keep track of sizes.
93 ** Use signed integers so that negatives are valid, in which case we shrunk.
97 SizeComposition mCodeComposition
;
100 SizeComposition mDataComposition
;
105 typedef enum __enum_SegmentClass
107 ** What type of data a segment holds.
116 typedef struct __struct_SymbolStats
118 ** Symbol level stats.
127 typedef struct __struct_ObjectStats
129 ** Object level stats.
134 SizeComposition mComposition
;
135 SymbolStats
* mSymbols
;
136 unsigned mSymbolCount
;
141 typedef struct __struct_SegmentStats
143 ** Segment level stats.
149 SizeComposition mComposition
;
150 ObjectStats
* mObjects
;
151 unsigned mObjectCount
;
156 typedef struct __struct_ModuleStats
158 ** Module level stats.
163 SegmentStats
* mSegments
;
164 unsigned mSegmentCount
;
169 static int moduleCompare(const void* in1
, const void* in2
)
176 ModuleStats
* one
= (ModuleStats
*)in1
;
177 ModuleStats
* two
= (ModuleStats
*)in2
;
179 int oneSize
= (one
->mSize
.mCode
+ one
->mSize
.mData
);
180 int twoSize
= (two
->mSize
.mCode
+ two
->mSize
.mData
);
182 if(oneSize
< twoSize
)
186 else if(oneSize
> twoSize
)
192 retval
= strcmp(one
->mModule
, two
->mModule
);
193 if(0 > oneSize
&& 0 > twoSize
)
203 static int segmentCompare(const void* in1
, const void* in2
)
210 SegmentStats
* one
= (SegmentStats
*)in1
;
211 SegmentStats
* two
= (SegmentStats
*)in2
;
213 if(one
->mSize
< two
->mSize
)
217 else if(one
->mSize
> two
->mSize
)
223 retval
= strcmp(one
->mSegment
, two
->mSegment
);
224 if(0 > one
->mSize
&& 0 > two
->mSize
)
234 static int objectCompare(const void* in1
, const void* in2
)
241 ObjectStats
* one
= (ObjectStats
*)in1
;
242 ObjectStats
* two
= (ObjectStats
*)in2
;
244 if(one
->mSize
< two
->mSize
)
248 else if(one
->mSize
> two
->mSize
)
254 retval
= strcmp(one
->mObject
, two
->mObject
);
255 if(0 > one
->mSize
&& 0 > two
->mSize
)
265 static int symbolCompare(const void* in1
, const void* in2
)
272 SymbolStats
* one
= (SymbolStats
*)in1
;
273 SymbolStats
* two
= (SymbolStats
*)in2
;
275 if(one
->mSize
< two
->mSize
)
279 else if(one
->mSize
> two
->mSize
)
285 retval
= strcmp(one
->mSymbol
, two
->mSymbol
);
286 if(0 > one
->mSize
&& 0 > two
->mSize
)
296 void trimWhite(char* inString
)
298 ** Remove any whitespace from the end of the string.
301 int len
= strlen(inString
);
307 if(isspace(*(inString
+ len
)))
309 *(inString
+ len
) = '\0';
319 int difftool(Options
* inOptions
)
321 ** Read a diff file and spit out relevant information.
325 char lineBuffer
[0x500];
327 ModuleStats
* modules
= NULL
;
328 unsigned moduleCount
= 0;
329 unsigned moduleLoop
= 0;
330 ModuleStats
* theModule
= NULL
;
331 unsigned segmentLoop
= 0;
332 SegmentStats
* theSegment
= NULL
;
333 unsigned objectLoop
= 0;
334 ObjectStats
* theObject
= NULL
;
335 unsigned symbolLoop
= 0;
336 SymbolStats
* theSymbol
= NULL
;
337 unsigned allSymbolCount
= 0;
339 memset(&overall
, 0, sizeof(overall
));
342 ** Read the entire diff file.
343 ** We're only interested in lines beginning with < or >
345 while(0 == retval
&& NULL
!= fgets(lineBuffer
, sizeof(lineBuffer
), inOptions
->mInput
))
347 trimWhite(lineBuffer
);
349 if(('<' == lineBuffer
[0] || '>' == lineBuffer
[0]) && ' ' == lineBuffer
[1])
352 char* theLine
= &lineBuffer
[2];
355 #define SEGCLASS_CHARS 15
356 char segClass
[SEGCLASS_CHARS
+ 1];
357 #define SCOPE_CHARS 15
358 char scope
[SCOPE_CHARS
+ 1];
359 #define MODULE_CHARS 255
360 char module
[MODULE_CHARS
+ 1];
361 #define SEGMENT_CHARS 63
362 char segment
[SEGMENT_CHARS
+ 1];
363 #define OBJECT_CHARS 255
364 char object
[OBJECT_CHARS
+ 1];
368 ** Figure out if the line adds or subtracts from something.
370 if('>' == lineBuffer
[0])
377 ** Scan the line for information.
380 #define STRINGIFY(s_) STRINGIFY2(s_)
381 #define STRINGIFY2(s_) #s_
383 scanRes
= sscanf(theLine
,
384 "%x\t%" STRINGIFY(SEGCLASS_CHARS
) "s\t%"
385 STRINGIFY(SCOPE_CHARS
) "s\t%" STRINGIFY(MODULE_CHARS
)
386 "s\t%" STRINGIFY(SEGMENT_CHARS
) "s\t%"
387 STRINGIFY(OBJECT_CHARS
) "s\t",
397 SegmentClass segmentClass
= DATA
;
399 symbol
= strrchr(theLine
, '\t') + 1;
401 if(0 == strcmp(segClass
, "CODE"))
405 else if(0 == strcmp(segClass
, "DATA"))
412 ERROR_REPORT(retval
, segClass
, "Unable to determine segment class.");
417 unsigned moduleIndex
= 0;
420 ** Find, in succession, the following things:
425 ** Failure to find any one of these means to create it.
428 for(moduleIndex
= 0; moduleIndex
< moduleCount
; moduleIndex
++)
430 if(0 == strcmp(modules
[moduleIndex
].mModule
, module
))
436 if(moduleIndex
== moduleCount
)
440 moved
= realloc(modules
, sizeof(ModuleStats
) * (1 + moduleCount
));
443 modules
= (ModuleStats
*)moved
;
445 memset(modules
+ moduleIndex
, 0, sizeof(ModuleStats
));
447 modules
[moduleIndex
].mModule
= strdup(module
);
448 if(NULL
== modules
[moduleIndex
].mModule
)
451 ERROR_REPORT(retval
, module
, "Unable to duplicate string.");
457 ERROR_REPORT(retval
, inOptions
->mProgramName
, "Unable to increase module array.");
463 unsigned segmentIndex
= 0;
464 theModule
= (modules
+ moduleIndex
);
466 for(segmentIndex
= 0; segmentIndex
< theModule
->mSegmentCount
; segmentIndex
++)
468 if(0 == strcmp(segment
, theModule
->mSegments
[segmentIndex
].mSegment
))
474 if(segmentIndex
== theModule
->mSegmentCount
)
478 moved
= realloc(theModule
->mSegments
, sizeof(SegmentStats
) * (theModule
->mSegmentCount
+ 1));
481 theModule
->mSegments
= (SegmentStats
*)moved
;
482 theModule
->mSegmentCount
++;
483 memset(theModule
->mSegments
+ segmentIndex
, 0, sizeof(SegmentStats
));
485 theModule
->mSegments
[segmentIndex
].mClass
= segmentClass
;
486 theModule
->mSegments
[segmentIndex
].mSegment
= strdup(segment
);
487 if(NULL
== theModule
->mSegments
[segmentIndex
].mSegment
)
490 ERROR_REPORT(retval
, segment
, "Unable to duplicate string.");
496 ERROR_REPORT(retval
, inOptions
->mProgramName
, "Unable to increase segment array.");
502 unsigned objectIndex
= 0;
503 theSegment
= (theModule
->mSegments
+ segmentIndex
);
505 for(objectIndex
= 0; objectIndex
< theSegment
->mObjectCount
; objectIndex
++)
507 if(0 == strcmp(object
, theSegment
->mObjects
[objectIndex
].mObject
))
513 if(objectIndex
== theSegment
->mObjectCount
)
517 moved
= realloc(theSegment
->mObjects
, sizeof(ObjectStats
) * (1 + theSegment
->mObjectCount
));
520 theSegment
->mObjects
= (ObjectStats
*)moved
;
521 theSegment
->mObjectCount
++;
522 memset(theSegment
->mObjects
+ objectIndex
, 0, sizeof(ObjectStats
));
524 theSegment
->mObjects
[objectIndex
].mObject
= strdup(object
);
525 if(NULL
== theSegment
->mObjects
[objectIndex
].mObject
)
528 ERROR_REPORT(retval
, object
, "Unable to duplicate string.");
534 ERROR_REPORT(retval
, inOptions
->mProgramName
, "Unable to increase object array.");
540 unsigned symbolIndex
= 0;
541 theObject
= (theSegment
->mObjects
+ objectIndex
);
543 for(symbolIndex
= 0; symbolIndex
< theObject
->mSymbolCount
; symbolIndex
++)
545 if(0 == strcmp(symbol
, theObject
->mSymbols
[symbolIndex
].mSymbol
))
551 if(symbolIndex
== theObject
->mSymbolCount
)
555 moved
= realloc(theObject
->mSymbols
, sizeof(SymbolStats
) * (1 + theObject
->mSymbolCount
));
558 theObject
->mSymbols
= (SymbolStats
*)moved
;
559 theObject
->mSymbolCount
++;
561 memset(theObject
->mSymbols
+ symbolIndex
, 0, sizeof(SymbolStats
));
563 theObject
->mSymbols
[symbolIndex
].mSymbol
= strdup(symbol
);
564 if(NULL
== theObject
->mSymbols
[symbolIndex
].mSymbol
)
567 ERROR_REPORT(retval
, symbol
, "Unable to duplicate string.");
573 ERROR_REPORT(retval
, inOptions
->mProgramName
, "Unable to increase symbol array.");
579 theSymbol
= (theObject
->mSymbols
+ symbolIndex
);
582 ** Update our various totals.
586 if(CODE
== segmentClass
)
588 overall
.mCode
+= size
;
589 theModule
->mSize
.mCode
+= size
;
591 else if(DATA
== segmentClass
)
593 overall
.mData
+= size
;
594 theModule
->mSize
.mData
+= size
;
597 theSegment
->mSize
+= size
;
598 theObject
->mSize
+= size
;
599 theSymbol
->mSize
+= size
;
603 if(CODE
== segmentClass
)
605 overall
.mCode
-= size
;
606 theModule
->mSize
.mCode
-= size
;
608 else if(DATA
== segmentClass
)
610 overall
.mData
-= size
;
611 theModule
->mSize
.mData
-= size
;
614 theSegment
->mSize
-= size
;
615 theObject
->mSize
-= size
;
616 theSymbol
->mSize
-= size
;
627 ERROR_REPORT(retval
, inOptions
->mInputName
, "Unable to scan line data.");
632 if(0 == retval
&& 0 != ferror(inOptions
->mInput
))
635 ERROR_REPORT(retval
, inOptions
->mInputName
, "Unable to read file.");
639 ** Next, it is time to perform revisionist history of sorts.
640 ** If the negation switch is in play, we perfrom the following
643 ** For each section, find size changes which have an equal and
644 ** opposite change, and set them both to zero.
645 ** However, you can only do this if the number of negating changes
646 ** is even, as if it is odd, then any one of the many could be
647 ** at fault for the actual change.
649 ** This orginally exists to make the win32 codesighs reports more
650 ** readable/meaningful.
652 if(0 == retval
&& 0 != inOptions
->mNegation
)
654 ObjectStats
** objArray
= NULL
;
655 SymbolStats
** symArray
= NULL
;
658 ** Create arrays big enough to hold all symbols.
659 ** As well as an array to keep the owning object at the same index.
660 ** We will keep the object around as we may need to modify the size.
662 objArray
= (ObjectStats
**)malloc(allSymbolCount
* sizeof(ObjectStats
*));
663 symArray
= (SymbolStats
**)malloc(allSymbolCount
* sizeof(SymbolStats
*));
664 if(NULL
== objArray
|| NULL
== symArray
)
667 ERROR_REPORT(retval
, inOptions
->mProgramName
, "Unable to allocate negation array memory.");
671 unsigned arrayCount
= 0;
672 unsigned arrayLoop
= 0;
675 ** Go through and perform the steps on each section/segment.
677 for(moduleLoop
= 0; moduleLoop
< moduleCount
; moduleLoop
++)
679 theModule
= modules
+ moduleLoop
;
681 for(segmentLoop
= 0; segmentLoop
< theModule
->mSegmentCount
; segmentLoop
++)
683 theSegment
= theModule
->mSegments
+ segmentLoop
;
686 ** Collect all symbols under this section.
687 ** The symbols are spread out between all the objects,
688 ** so keep track of both independently at the
693 for(objectLoop
= 0; objectLoop
< theSegment
->mObjectCount
; objectLoop
++)
695 theObject
= theSegment
->mObjects
+ objectLoop
;
697 for(symbolLoop
= 0; symbolLoop
< theObject
->mSymbolCount
; symbolLoop
++)
699 theSymbol
= theObject
->mSymbols
+ symbolLoop
;
701 objArray
[arrayCount
] = theObject
;
702 symArray
[arrayCount
] = theSymbol
;
708 ** Now that we have a list of symbols, go through each
709 ** and see if there is a chance of negation.
711 for(arrayLoop
= 0; arrayLoop
< arrayCount
; arrayLoop
++)
714 ** If the item is NULL, it was already negated.
715 ** Don't do this for items with a zero size.
717 if(NULL
!= symArray
[arrayLoop
] && 0 != symArray
[arrayLoop
]->mSize
)
719 unsigned identicalValues
= 0;
720 unsigned oppositeValues
= 0;
721 unsigned lookLoop
= 0;
722 const int lookingFor
= symArray
[arrayLoop
]->mSize
;
725 ** Count the number of items with this value.
726 ** Count the number of items with the opposite equal value.
727 ** If they are equal, go through and negate all sizes.
729 for(lookLoop
= arrayLoop
; lookLoop
< arrayCount
; lookLoop
++)
732 ** Skip negated items.
733 ** Skip zero length items.
735 if(NULL
== symArray
[lookLoop
] || 0 == symArray
[lookLoop
]->mSize
)
740 if(lookingFor
== symArray
[lookLoop
]->mSize
)
744 else if((-1 * lookingFor
) == symArray
[lookLoop
]->mSize
)
750 if(0 != identicalValues
&& identicalValues
== oppositeValues
)
752 unsigned negationLoop
= 0;
754 for(negationLoop
= arrayLoop
; 0 != identicalValues
|| 0 != oppositeValues
; negationLoop
++)
757 ** Skip negated items.
758 ** Skip zero length items.
760 if(NULL
== symArray
[negationLoop
] || 0 == symArray
[negationLoop
]->mSize
)
766 ** Negate any size matches.
767 ** Reflect the change in the object as well.
770 if(lookingFor
== symArray
[negationLoop
]->mSize
)
772 objArray
[negationLoop
]->mSize
-= lookingFor
;
773 symArray
[negationLoop
]->mSize
= 0;
774 symArray
[negationLoop
] = NULL
;
778 else if((-1 * lookingFor
) == symArray
[negationLoop
]->mSize
)
780 objArray
[negationLoop
]->mSize
+= lookingFor
;
781 symArray
[negationLoop
]->mSize
= 0;
782 symArray
[negationLoop
] = NULL
;
800 ** If all went well, time to report.
805 ** Loop through our data once more, so that the symbols can
806 ** propigate their changes upwards in a positive/negative
808 ** This will help give the composite change more meaning.
810 for(moduleLoop
= 0; moduleLoop
< moduleCount
; moduleLoop
++)
812 theModule
= modules
+ moduleLoop
;
815 ** Skip if there is zero drift, or no net change.
817 if(0 == inOptions
->mZeroDrift
&& 0 == (theModule
->mSize
.mCode
+ theModule
->mSize
.mData
))
822 for(segmentLoop
= 0; segmentLoop
< theModule
->mSegmentCount
; segmentLoop
++)
824 theSegment
= theModule
->mSegments
+ segmentLoop
;
827 ** Skip if there is zero drift, or no net change.
829 if(0 == inOptions
->mZeroDrift
&& 0 == theSegment
->mSize
)
834 for(objectLoop
= 0; objectLoop
< theSegment
->mObjectCount
; objectLoop
++)
836 theObject
= theSegment
->mObjects
+ objectLoop
;
839 ** Skip if there is zero drift, or no net change.
841 if(0 == inOptions
->mZeroDrift
&& 0 == theObject
->mSize
)
846 for(symbolLoop
= 0; symbolLoop
< theObject
->mSymbolCount
; symbolLoop
++)
848 theSymbol
= theObject
->mSymbols
+ symbolLoop
;
851 ** Propagate the composition all the way to the top.
852 ** Sizes of zero change are skipped.
854 if(0 < theSymbol
->mSize
)
856 theObject
->mComposition
.mPositive
+= theSymbol
->mSize
;
857 theSegment
->mComposition
.mPositive
+= theSymbol
->mSize
;
858 if(CODE
== theSegment
->mClass
)
860 overall
.mCodeComposition
.mPositive
+= theSymbol
->mSize
;
861 theModule
->mSize
.mCodeComposition
.mPositive
+= theSymbol
->mSize
;
863 else if(DATA
== theSegment
->mClass
)
865 overall
.mDataComposition
.mPositive
+= theSymbol
->mSize
;
866 theModule
->mSize
.mDataComposition
.mPositive
+= theSymbol
->mSize
;
869 else if(0 > theSymbol
->mSize
)
871 theObject
->mComposition
.mNegative
+= theSymbol
->mSize
;
872 theSegment
->mComposition
.mNegative
+= theSymbol
->mSize
;
873 if(CODE
== theSegment
->mClass
)
875 overall
.mCodeComposition
.mNegative
+= theSymbol
->mSize
;
876 theModule
->mSize
.mCodeComposition
.mNegative
+= theSymbol
->mSize
;
878 else if(DATA
== theSegment
->mClass
)
880 overall
.mDataComposition
.mNegative
+= theSymbol
->mSize
;
881 theModule
->mSize
.mDataComposition
.mNegative
+= theSymbol
->mSize
;
890 if(inOptions
->mSummaryOnly
)
892 fprintf(inOptions
->mOutput
, "%+d (%+d/%+d)\n", overall
.mCode
+ overall
.mData
, overall
.mCodeComposition
.mPositive
+ overall
.mDataComposition
.mPositive
, overall
.mCodeComposition
.mNegative
+ overall
.mDataComposition
.mNegative
);
896 fprintf(inOptions
->mOutput
, "Overall Change in Size\n");
897 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
);
898 fprintf(inOptions
->mOutput
, "\tCode:\t%+11d (%+d/%+d)\n", overall
.mCode
, overall
.mCodeComposition
.mPositive
, overall
.mCodeComposition
.mNegative
);
899 fprintf(inOptions
->mOutput
, "\tData:\t%+11d (%+d/%+d)\n", overall
.mData
, overall
.mDataComposition
.mPositive
, overall
.mDataComposition
.mNegative
);
903 ** Check what else we should output.
905 if(0 == inOptions
->mSummaryOnly
&& NULL
!= modules
&& moduleCount
)
907 const char* segmentType
= NULL
;
910 ** We're going to sort everything.
912 qsort(modules
, moduleCount
, sizeof(ModuleStats
), moduleCompare
);
913 for(moduleLoop
= 0; moduleLoop
< moduleCount
; moduleLoop
++)
915 theModule
= modules
+ moduleLoop
;
917 qsort(theModule
->mSegments
, theModule
->mSegmentCount
, sizeof(SegmentStats
), segmentCompare
);
919 for(segmentLoop
= 0; segmentLoop
< theModule
->mSegmentCount
; segmentLoop
++)
921 theSegment
= theModule
->mSegments
+ segmentLoop
;
923 qsort(theSegment
->mObjects
, theSegment
->mObjectCount
, sizeof(ObjectStats
), objectCompare
);
925 for(objectLoop
= 0; objectLoop
< theSegment
->mObjectCount
; objectLoop
++)
927 theObject
= theSegment
->mObjects
+ objectLoop
;
929 qsort(theObject
->mSymbols
, theObject
->mSymbolCount
, sizeof(SymbolStats
), symbolCompare
);
935 ** Loop through for output.
937 for(moduleLoop
= 0; moduleLoop
< moduleCount
; moduleLoop
++)
939 theModule
= modules
+ moduleLoop
;
942 ** Skip if there is zero drift, or no net change.
944 if(0 == inOptions
->mZeroDrift
&& 0 == (theModule
->mSize
.mCode
+ theModule
->mSize
.mData
))
949 fprintf(inOptions
->mOutput
, "\n");
950 fprintf(inOptions
->mOutput
, "%s\n", theModule
->mModule
);
951 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
);
952 fprintf(inOptions
->mOutput
, "\tCode:\t%+11d (%+d/%+d)\n", theModule
->mSize
.mCode
, theModule
->mSize
.mCodeComposition
.mPositive
, theModule
->mSize
.mCodeComposition
.mNegative
);
953 fprintf(inOptions
->mOutput
, "\tData:\t%+11d (%+d/%+d)\n", theModule
->mSize
.mData
, theModule
->mSize
.mDataComposition
.mPositive
, theModule
->mSize
.mDataComposition
.mNegative
);
955 for(segmentLoop
= 0; segmentLoop
< theModule
->mSegmentCount
; segmentLoop
++)
957 theSegment
= theModule
->mSegments
+ segmentLoop
;
960 ** Skip if there is zero drift, or no net change.
962 if(0 == inOptions
->mZeroDrift
&& 0 == theSegment
->mSize
)
967 if(CODE
== theSegment
->mClass
)
969 segmentType
= "CODE";
971 else if(DATA
== theSegment
->mClass
)
973 segmentType
= "DATA";
976 fprintf(inOptions
->mOutput
, "\t%+11d (%+d/%+d)\t%s (%s)\n", theSegment
->mSize
, theSegment
->mComposition
.mPositive
, theSegment
->mComposition
.mNegative
, theSegment
->mSegment
, segmentType
);
978 for(objectLoop
= 0; objectLoop
< theSegment
->mObjectCount
; objectLoop
++)
980 theObject
= theSegment
->mObjects
+ objectLoop
;
983 ** Skip if there is zero drift, or no net change.
985 if(0 == inOptions
->mZeroDrift
&& 0 == theObject
->mSize
)
990 fprintf(inOptions
->mOutput
, "\t\t%+11d (%+d/%+d)\t%s\n", theObject
->mSize
, theObject
->mComposition
.mPositive
, theObject
->mComposition
.mNegative
, theObject
->mObject
);
992 for(symbolLoop
= 0; symbolLoop
< theObject
->mSymbolCount
; symbolLoop
++)
994 theSymbol
= theObject
->mSymbols
+ symbolLoop
;
997 ** Skip if there is zero drift, or no net change.
999 if(0 == inOptions
->mZeroDrift
&& 0 == theSymbol
->mSize
)
1004 fprintf(inOptions
->mOutput
, "\t\t\t%+11d\t%s\n", theSymbol
->mSize
, theSymbol
->mSymbol
);
1015 for(moduleLoop
= 0; moduleLoop
< moduleCount
; moduleLoop
++)
1017 theModule
= modules
+ moduleLoop
;
1019 for(segmentLoop
= 0; segmentLoop
< theModule
->mSegmentCount
; segmentLoop
++)
1021 theSegment
= theModule
->mSegments
+ segmentLoop
;
1023 for(objectLoop
= 0; objectLoop
< theSegment
->mObjectCount
; objectLoop
++)
1025 theObject
= theSegment
->mObjects
+ objectLoop
;
1027 for(symbolLoop
= 0; symbolLoop
< theObject
->mSymbolCount
; symbolLoop
++)
1029 theSymbol
= theObject
->mSymbols
+ symbolLoop
;
1031 CLEANUP(theSymbol
->mSymbol
);
1034 CLEANUP(theObject
->mSymbols
);
1035 CLEANUP(theObject
->mObject
);
1038 CLEANUP(theSegment
->mObjects
);
1039 CLEANUP(theSegment
->mSegment
);
1042 CLEANUP(theModule
->mSegments
);
1043 CLEANUP(theModule
->mModule
);
1051 int initOptions(Options
* outOptions
, int inArgc
, char** inArgv
)
1053 ** returns int 0 if successful.
1060 const int switchCount
= sizeof(gSwitches
) / sizeof(gSwitches
[0]);
1061 Switch
* current
= NULL
;
1064 ** Set any defaults.
1066 memset(outOptions
, 0, sizeof(Options
));
1067 outOptions
->mProgramName
= inArgv
[0];
1068 outOptions
->mInput
= stdin
;
1069 outOptions
->mInputName
= strdup("stdin");
1070 outOptions
->mOutput
= stdout
;
1071 outOptions
->mOutputName
= strdup("stdout");
1073 if(NULL
== outOptions
->mOutputName
|| NULL
== outOptions
->mInputName
)
1076 ERROR_REPORT(retval
, "stdin/stdout", "Unable to strdup.");
1080 ** Go through and attempt to do the right thing.
1082 for(loop
= 1; loop
< inArgc
&& 0 == retval
; loop
++)
1087 for(switchLoop
= 0; switchLoop
< switchCount
&& 0 == retval
; switchLoop
++)
1089 if(0 == strcmp(gSwitches
[switchLoop
]->mLongName
, inArgv
[loop
]))
1093 else if(0 == strcmp(gSwitches
[switchLoop
]->mShortName
, inArgv
[loop
]))
1100 if(gSwitches
[switchLoop
]->mHasValue
)
1103 ** Attempt to absorb next option to fullfill value.
1105 if(loop
+ 1 < inArgc
)
1109 current
= gSwitches
[switchLoop
];
1110 current
->mValue
= inArgv
[loop
];
1115 current
= gSwitches
[switchLoop
];
1124 outOptions
->mHelp
= __LINE__
;
1126 ERROR_REPORT(retval
, inArgv
[loop
], "Unknown command line switch.");
1128 else if(NULL
== current
)
1130 outOptions
->mHelp
= __LINE__
;
1132 ERROR_REPORT(retval
, inArgv
[loop
], "Command line switch requires a value.");
1137 ** Do something based on address/swtich.
1139 if(current
== &gInputSwitch
)
1141 CLEANUP(outOptions
->mInputName
);
1142 if(NULL
!= outOptions
->mInput
&& stdin
!= outOptions
->mInput
)
1144 fclose(outOptions
->mInput
);
1145 outOptions
->mInput
= NULL
;
1148 outOptions
->mInput
= fopen(current
->mValue
, "r");
1149 if(NULL
== outOptions
->mInput
)
1152 ERROR_REPORT(retval
, current
->mValue
, "Unable to open input file.");
1156 outOptions
->mInputName
= strdup(current
->mValue
);
1157 if(NULL
== outOptions
->mInputName
)
1160 ERROR_REPORT(retval
, current
->mValue
, "Unable to strdup.");
1164 else if(current
== &gOutputSwitch
)
1166 CLEANUP(outOptions
->mOutputName
);
1167 if(NULL
!= outOptions
->mOutput
&& stdout
!= outOptions
->mOutput
)
1169 fclose(outOptions
->mOutput
);
1170 outOptions
->mOutput
= NULL
;
1173 outOptions
->mOutput
= fopen(current
->mValue
, "a");
1174 if(NULL
== outOptions
->mOutput
)
1177 ERROR_REPORT(retval
, current
->mValue
, "Unable to open output file.");
1181 outOptions
->mOutputName
= strdup(current
->mValue
);
1182 if(NULL
== outOptions
->mOutputName
)
1185 ERROR_REPORT(retval
, current
->mValue
, "Unable to strdup.");
1189 else if(current
== &gHelpSwitch
)
1191 outOptions
->mHelp
= __LINE__
;
1193 else if(current
== &gSummarySwitch
)
1195 outOptions
->mSummaryOnly
= __LINE__
;
1197 else if(current
== &gZeroDriftSwitch
)
1199 outOptions
->mZeroDrift
= __LINE__
;
1201 else if(current
== &gNegationSwitch
)
1203 outOptions
->mNegation
= __LINE__
;
1208 ERROR_REPORT(retval
, current
->mLongName
, "No handler for command line switch.");
1217 void cleanOptions(Options
* inOptions
)
1219 ** Clean up any open handles.
1222 CLEANUP(inOptions
->mInputName
);
1223 if(NULL
!= inOptions
->mInput
&& stdin
!= inOptions
->mInput
)
1225 fclose(inOptions
->mInput
);
1227 CLEANUP(inOptions
->mOutputName
);
1228 if(NULL
!= inOptions
->mOutput
&& stdout
!= inOptions
->mOutput
)
1230 fclose(inOptions
->mOutput
);
1233 memset(inOptions
, 0, sizeof(Options
));
1237 void showHelp(Options
* inOptions
)
1239 ** Show some simple help text on usage.
1243 const int switchCount
= sizeof(gSwitches
) / sizeof(gSwitches
[0]);
1244 const char* valueText
= NULL
;
1246 printf("usage:\t%s [arguments]\n", inOptions
->mProgramName
);
1248 printf("arguments:\n");
1250 for(loop
= 0; loop
< switchCount
; loop
++)
1252 if(gSwitches
[loop
]->mHasValue
)
1254 valueText
= " <value>";
1261 printf("\t%s%s\n", gSwitches
[loop
]->mLongName
, valueText
);
1262 printf("\t %s%s", gSwitches
[loop
]->mShortName
, valueText
);
1263 printf(DESC_NEWLINE
"%s\n\n", gSwitches
[loop
]->mDescription
);
1266 printf("This tool takes the diff of two sorted tsv files to form a summary report\n");
1267 printf("of code and data size changes which is hoped to be human readable.\n");
1271 int main(int inArgc
, char** inArgv
)
1276 retval
= initOptions(&options
, inArgc
, inArgv
);
1281 else if(0 == retval
)
1283 retval
= difftool(&options
);
1286 cleanOptions(&options
);