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/. */
20 #define DEMANGLE_STATE_NORMAL 0
21 #define DEMANGLE_STATE_QDECODE 1
22 #define DEMANGLE_STATE_PROLOGUE_1 2
23 #define DEMANGLE_STATE_HAVE_TYPE 3
24 #define DEMANGLE_STATE_DEC_LENGTH 4
25 #define DEMANGLE_STATE_HEX_LENGTH 5
26 #define DEMANGLE_STATE_PROLOGUE_SECONDARY 6
27 #define DEMANGLE_STATE_DOLLAR_1 7
28 #define DEMANGLE_STATE_DOLLAR_2 8
29 #define DEMANGLE_STATE_START 9
30 #define DEMANGLE_STATE_STOP 10
31 #define DEMANGLE_SAFE_CHAR(eval) (isprint(eval) ? eval : ' ')
37 #define STRINGIFY(s_) STRINGIFY2(s_)
38 #define STRINGIFY2(s_) #s_
39 #define SYMBOL_BUF_CHARS 511
41 #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
42 #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
45 typedef struct __struct_SymDB_Size
47 ** The size of the symbol.
48 ** The size is nested withing a symbols structures to produce a fast
50 ** The objects are listed in case the client of the symdb needs to
51 ** match the object name in the scenario where multiple symbol
54 ** mSize The size of the symbol in these objects.
55 ** mObjects A list of objects containing said symbol.
56 ** mObjectCount Number of objects.
61 unsigned mObjectCount
;
66 typedef struct __struct_SymDB_Section
68 ** Each section for a symbol has a list of sizes.
69 ** Should there be exactly one size for the symbol, then that
70 ** is the size that should be accepted.
71 ** If there is more than one size, then a match on the object
72 ** should be attempted, held withing each size.
74 ** mName The section name.
75 ** mSizes The varoius sizes of the symbol in this section.
76 ** mSizeCount The number of available sizes.
86 typedef struct __struct_SymDB_Symbol
88 ** Each symbol has at least one section.
89 ** The section indicates what type of symbol a client may be looking for.
90 ** If there is no match on the section, then the client should not trust
93 ** mName The mangled name of the symbol.
94 ** mSections Various sections this symbol belongs to.
95 ** mSectionCount The number of sections.
99 SymDB_Section
* mSections
;
100 unsigned mSectionCount
;
105 #define SYMDB_SYMBOL_GROWBY 0x1000 /* how many sybols to allocate at a time */
108 typedef struct __struct_SymDB_Container
110 ** The symbol DB container object.
111 ** The goal of the symbol DB is to have exactly one SymDB_Symbol for each
112 ** mangled name, no matter how ever many identical mangled names there
114 ** The input is already expected to be well sorted, futher this leads to
115 ** the ability to binary search for symbol name matches.
117 ** mSymbols The symbols.
118 ** mSymbolCount The number of symbols in the DB.
119 ** mSymbolCapacity The number of symbols we can hold (before realloc).
122 SymDB_Symbol
* mSymbols
;
123 unsigned mSymbolCount
;
124 unsigned mSymbolCapacity
;
129 typedef struct __struct_Options
131 ** Options to control how we perform.
133 ** mProgramName Used in help text.
134 ** mInput File to read for input.
136 ** mInputName Name of the file.
137 ** mOutput Output file, append.
138 ** Default is stdout.
139 ** mOutputName Name of the file.
140 ** mHelp Whether or not help should be shown.
141 ** mMatchModules Array of strings which the module name should match.
142 ** mMatchModuleCount Number of items in array.
143 ** mSymDBName Symbol DB filename.
144 ** mBatchMode Batch mode.
145 ** When in batch mode, the input file contains a list of
146 ** map files to process.
147 ** Normally the input file is a single map file itself.
150 const char* mProgramName
;
156 char** mMatchModules
;
157 unsigned mMatchModuleCount
;
159 SymDB_Container
* mSymDB
;
165 typedef struct __struct_Switch
167 ** Command line options.
170 const char* mLongName
;
171 const char* mShortName
;
174 const char* mDescription
;
178 #define DESC_NEWLINE "\n\t\t"
180 static Switch gInputSwitch
= {"--input", "-i", 1, NULL
, "Specify input file." DESC_NEWLINE
"stdin is default."};
181 static Switch gOutputSwitch
= {"--output", "-o", 1, NULL
, "Specify output file." DESC_NEWLINE
"Appends if file exists." DESC_NEWLINE
"stdout is default."};
182 static Switch gHelpSwitch
= {"--help", "-h", 0, NULL
, "Information on usage."};
183 static Switch gMatchModuleSwitch
= {"--match-module", "-mm", 1, NULL
, "Specify a valid module name." DESC_NEWLINE
"Multiple specifications allowed." DESC_NEWLINE
"If a module name does not match one of the names specified then no output will occur."};
184 static Switch gSymDBSwitch
= {"--symdb", "-sdb", 1, NULL
, "Specify a symbol tsv db input file." DESC_NEWLINE
"Such a symdb is produced using the tool msdump2symdb." DESC_NEWLINE
"This allows better symbol size approximations." DESC_NEWLINE
"The symdb file must be pre-sorted."};
185 static Switch gBatchModeSwitch
= {"--batch", "-b", 0, NULL
, "Runs in batch mode." DESC_NEWLINE
"The input file contains a list of map files." DESC_NEWLINE
"Normally the input file is a map file itself." DESC_NEWLINE
"This eliminates reprocessing the symdb for multiple map files."};
187 static Switch
* gSwitches
[] = {
197 typedef struct __struct_MSMap_ReadState
199 ** Keep track of what state we are while reading input.
200 ** This gives the input context in which we absorb the datum.
207 int mHasPreferredLoadAddress
;
210 int mSegmentDataSkippedLine
;
212 int mHasPublicSymbolData
;
213 int mHasPublicSymbolDataSkippedLines
;
217 int mFoundStaticSymbols
;
222 char* skipWhite(char* inScan
)
227 char* retval
= inScan
;
229 while(isspace(*retval
))
237 void trimWhite(char* inString
)
239 ** Remove any whitespace from the end of the string.
242 int len
= strlen(inString
);
248 if(isspace(*(inString
+ len
)))
250 *(inString
+ len
) = '\0';
260 char* lastWord(char* inString
)
262 ** Finds and returns the last word in a string.
263 ** It is assumed no whitespace is at the end of the string.
267 int len
= strlen(inString
);
272 if(isspace(*(inString
+ len
)))
279 return inString
+ len
+ mod
;
283 MSMap_Segment
* getSymbolSection(MSMap_Module
* inModule
, MSMap_Symbol
* inoutSymbol
)
285 ** Perform a lookup for the section of the symbol.
286 ** The function could cache the value.
289 MSMap_Segment
* retval
= NULL
;
291 if(NULL
!= inoutSymbol
->mSection
)
296 retval
= inoutSymbol
->mSection
;
300 unsigned secLoop
= 0;
303 ** Go through sections in module to find the match for the symbol.
305 for(secLoop
= 0; secLoop
< inModule
->mSegmentCount
; secLoop
++)
307 if(inoutSymbol
->mPrefix
== inModule
->mSegments
[secLoop
].mPrefix
)
309 if(inoutSymbol
->mOffset
>= inModule
->mSegments
[secLoop
].mOffset
)
311 if(inoutSymbol
->mOffset
< (inModule
->mSegments
[secLoop
].mOffset
+ inModule
->mSegments
[secLoop
].mLength
))
314 ** We have the section.
316 retval
= &inModule
->mSegments
[secLoop
];
324 ** Cache the value for next time.
326 inoutSymbol
->mSection
= retval
;
333 int readSymDB(const char* inDBName
, SymDB_Container
** outDB
)
335 ** Intialize the symbol DB.
336 ** Only call if the symbol DB should be initialized.
342 ** Initialize out arguments.
349 if(NULL
!= outDB
&& NULL
!= inDBName
)
353 symDB
= fopen(inDBName
, "r");
356 *outDB
= (SymDB_Container
*)calloc(1, sizeof(SymDB_Container
));
361 char* section
= NULL
;
364 unsigned lengthNum
= 0;
365 char* endLength
= NULL
;
368 ** Read the file line by line.
370 while(0 == retval
&& NULL
!= fgets(lineBuf
, sizeof(lineBuf
), symDB
))
375 ** Each line has four arguments. tab separated values (tsv).
382 symbol
= skipWhite(lineBuf
);
386 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
390 section
= strchr(symbol
, '\t');
394 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
400 length
= strchr(section
, '\t');
404 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
410 object
= strchr(length
, '\t');
414 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
421 ** Convert the length into a number.
424 lengthNum
= strtoul(length
, &endLength
, 16);
425 if(0 == errno
&& endLength
!= length
)
427 SymDB_Symbol
* dbSymbol
= NULL
;
428 SymDB_Section
* dbSection
= NULL
;
429 SymDB_Size
* dbSize
= NULL
;
430 char* dbObject
= NULL
;
434 ** Are we looking at the same symbol as last line?
435 ** This assumes the symdb is pre sorted!!!
437 if(0 != (*outDB
)->mSymbolCount
)
439 unsigned index
= (*outDB
)->mSymbolCount
- 1;
441 if(0 == strcmp((*outDB
)->mSymbols
[index
].mName
, symbol
))
443 dbSymbol
= &(*outDB
)->mSymbols
[index
];
448 ** May need to create symbol.
453 ** Could be time to grow the symbol pool.
455 if((*outDB
)->mSymbolCount
>= (*outDB
)->mSymbolCapacity
)
457 moved
= realloc((*outDB
)->mSymbols
, sizeof(SymDB_Symbol
) * ((*outDB
)->mSymbolCapacity
+ SYMDB_SYMBOL_GROWBY
));
460 (*outDB
)->mSymbols
= (SymDB_Symbol
*)moved
;
461 memset(&(*outDB
)->mSymbols
[(*outDB
)->mSymbolCapacity
], 0, sizeof(SymDB_Symbol
) * SYMDB_SYMBOL_GROWBY
);
462 (*outDB
)->mSymbolCapacity
+= SYMDB_SYMBOL_GROWBY
;
467 ERROR_REPORT(retval
, inDBName
, "Unable to grow symbol DB symbol array.");
472 if((*outDB
)->mSymbolCount
< (*outDB
)->mSymbolCapacity
)
474 dbSymbol
= &(*outDB
)->mSymbols
[(*outDB
)->mSymbolCount
];
475 (*outDB
)->mSymbolCount
++;
477 dbSymbol
->mName
= strdup(symbol
);
478 if(NULL
== dbSymbol
->mName
)
481 ERROR_REPORT(retval
, symbol
, "Unable to duplicate string.");
488 ERROR_REPORT(retval
, symbol
, "Unable to grow symbol DB for symbol.");
494 ** Assume we have the symbol.
496 ** Is this the same section as the last section in the symbol?
497 ** This assumes the symdb was presorted!!!!
499 if(0 != dbSymbol
->mSectionCount
)
501 unsigned index
= dbSymbol
->mSectionCount
- 1;
503 if(0 == strcmp(dbSymbol
->mSections
[index
].mName
, section
))
505 dbSection
= &dbSymbol
->mSections
[index
];
510 ** May need to create the section.
512 if(NULL
== dbSection
)
514 moved
= realloc(dbSymbol
->mSections
, sizeof(SymDB_Section
) * (dbSymbol
->mSectionCount
+ 1));
517 dbSymbol
->mSections
= (SymDB_Section
*)moved
;
518 dbSection
= &dbSymbol
->mSections
[dbSymbol
->mSectionCount
];
519 dbSymbol
->mSectionCount
++;
521 memset(dbSection
, 0, sizeof(SymDB_Section
));
523 dbSection
->mName
= strdup(section
);
524 if(NULL
== dbSection
->mName
)
527 ERROR_REPORT(retval
, section
, "Unable to duplicate string.");
534 ERROR_REPORT(retval
, section
, "Unable to grow symbol sections for symbol DB.");
540 ** Assume we have the section.
542 ** Is this the same size as the last size?
543 ** This assumes the symdb was presorted!!!
545 if(0 != dbSection
->mSizeCount
)
547 unsigned index
= dbSection
->mSizeCount
- 1;
549 if(dbSection
->mSizes
[index
].mSize
== lengthNum
)
551 dbSize
= &dbSection
->mSizes
[index
];
556 ** May need to create the size in question.
560 moved
= realloc(dbSection
->mSizes
, sizeof(SymDB_Size
) * (dbSection
->mSizeCount
+ 1));
563 dbSection
->mSizes
= (SymDB_Size
*)moved
;
564 dbSize
= &dbSection
->mSizes
[dbSection
->mSizeCount
];
565 dbSection
->mSizeCount
++;
567 memset(dbSize
, 0, sizeof(SymDB_Size
));
569 dbSize
->mSize
= lengthNum
;
574 ERROR_REPORT(retval
, length
, "Unable to grow symbol section sizes for symbol DB.");
580 ** Assume we have the size.
582 ** We assume a one to one correllation between size and object.
583 ** Always try to add the new object name.
584 ** As the symdb is assumed to be sorted, the object names should also be in order.
586 moved
= realloc(dbSize
->mObjects
, sizeof(char*) * (dbSize
->mObjectCount
+ 1));
589 dbObject
= strdup(object
);
591 dbSize
->mObjects
= (char**)moved
;
592 dbSize
->mObjects
[dbSize
->mObjectCount
] = dbObject
;
593 dbSize
->mObjectCount
++;
598 ERROR_REPORT(retval
, object
, "Unable to duplicate string.");
605 ERROR_REPORT(retval
, object
, "Unable to grow symbol section size objects for symbol DB.");
612 ERROR_REPORT(retval
, length
, "Unable to convert symbol DB length into a number.");
617 if(0 == retval
&& 0 != ferror(symDB
))
620 ERROR_REPORT(retval
, inDBName
, "Unable to read file.");
626 ERROR_REPORT(retval
, inDBName
, "Unable to allocate symbol DB.");
635 ERROR_REPORT(retval
, inDBName
, "Unable to open symbol DB.");
641 ERROR_REPORT(retval
, "(NULL)", "Invalid arguments.");
648 void cleanSymDB(SymDB_Container
** inDB
)
653 if(NULL
!= inDB
&& NULL
!= *inDB
)
655 unsigned symLoop
= 0;
656 unsigned secLoop
= 0;
657 unsigned sizLoop
= 0;
658 unsigned objLoop
= 0;
660 for(symLoop
= 0; symLoop
< (*inDB
)->mSymbolCount
; symLoop
++)
662 for(secLoop
= 0; secLoop
< (*inDB
)->mSymbols
[symLoop
].mSectionCount
; secLoop
++)
664 for(sizLoop
= 0; sizLoop
< (*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizeCount
; sizLoop
++)
666 for(objLoop
= 0; objLoop
< (*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
[sizLoop
].mObjectCount
; objLoop
++)
668 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
[sizLoop
].mObjects
[objLoop
]);
670 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
[sizLoop
].mObjects
);
672 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mName
);
673 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
);
675 CLEANUP((*inDB
)->mSymbols
[symLoop
].mName
);
676 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
);
678 CLEANUP((*inDB
)->mSymbols
);
684 int symDBLookup(const void* inKey
, const void* inItem
)
686 ** bsearch utility routine to find the symbol in the symdb.
690 const char* key
= (const char*)inKey
;
691 const SymDB_Symbol
* symbol
= (const SymDB_Symbol
*)inItem
;
693 retval
= strcmp(key
, symbol
->mName
);
699 int fillSymbolSizeFromDB(Options
* inOptions
, MSMap_Module
* inModule
, MSMap_Symbol
* inoutSymbol
, const char* inMangledName
)
701 ** If we have a symbol DB, attempt to determine the real size of the symbol
703 ** This helps us later in the game to avoid performing size guesses by
710 ** May need to initialize symdb.
712 if(NULL
== inOptions
->mSymDB
&& NULL
!= inOptions
->mSymDBName
)
714 retval
= readSymDB(inOptions
->mSymDBName
, &inOptions
->mSymDB
);
720 if(0 == retval
&& NULL
!= inOptions
->mSymDB
)
727 match
= bsearch(inMangledName
, inOptions
->mSymDB
->mSymbols
, inOptions
->mSymDB
->mSymbolCount
, sizeof(SymDB_Symbol
), symDBLookup
);
730 SymDB_Symbol
* symbol
= (SymDB_Symbol
*)match
;
731 unsigned symDBSize
= 0;
732 MSMap_Segment
* mapSection
= NULL
;
735 ** We found the symbol.
737 ** See if it has the section in question.
739 mapSection
= getSymbolSection(inModule
, inoutSymbol
);
740 if(NULL
!= mapSection
)
742 unsigned secLoop
= 0;
744 for(secLoop
= 0; secLoop
< symbol
->mSectionCount
; secLoop
++)
746 if(0 == strcmp(mapSection
->mSegment
, symbol
->mSections
[secLoop
].mName
))
748 SymDB_Section
* section
= &symbol
->mSections
[secLoop
];
751 ** We have a section match.
752 ** Should there be a single size for the symbol,
753 ** then we just default to that.
754 ** If more than one size, we have to do an
755 ** object match search.
756 ** Should there be no object match, we do nothign.
758 if(1 == section
->mSizeCount
)
760 symDBSize
= section
->mSizes
[0].mSize
;
764 char* mapObject
= NULL
;
767 ** Figure out the map object file name.
769 ** If it doesn't have a .obj in it, not worth continuing.
771 mapObject
= strrchr(inoutSymbol
->mObject
, ':');
772 if(NULL
== mapObject
)
774 mapObject
= inoutSymbol
->mObject
;
778 mapObject
++; /* colon */
781 if(NULL
!= strstr(mapObject
, ".obj"))
783 unsigned sizLoop
= 0;
784 unsigned objLoop
= 0;
785 SymDB_Size
* size
= NULL
;
787 for(sizLoop
= 0; sizLoop
< section
->mSizeCount
; sizLoop
++)
789 size
= §ion
->mSizes
[sizLoop
];
791 for(objLoop
= 0; objLoop
< size
->mObjectCount
; objLoop
++)
793 if(NULL
!= strstr(size
->mObjects
[objLoop
], mapObject
))
796 ** As we matched the object, in a particular section,
797 ** we'll go with this as the number.
799 symDBSize
= size
->mSize
;
805 ** If the object loop broke early, we break too.
807 if(objLoop
< size
->mObjectCount
)
823 inoutSymbol
->mSymDBSize
= symDBSize
;
831 char* symdup(const char* inSymbol
)
833 ** Attempts to demangle the symbol if appropriate.
834 ** Otherwise acts like strdup.
843 if(0 == strncmp("__imp_", inSymbol
, 6))
849 if('?' == inSymbol
[0])
851 char demangleBuf
[0x200];
852 DWORD demangleRes
= 0;
854 demangleRes
= UnDecorateSymbolName(inSymbol
, demangleBuf
, sizeof(demangleBuf
), UNDNAME_COMPLETE
);
857 if (strcmp(demangleBuf
, "`string'") == 0)
860 /* attempt manual demangling of string prefix.. */
862 /* first make sure we have enough space for the
863 updated string - the demangled string will
864 always be shorter than strlen(inSymbol) and the
865 prologue will always be longer than the
866 "string: " that we tack on the front of the string
868 char *curresult
= retval
= malloc(strlen(inSymbol
) + 11);
869 const char *curchar
= inSymbol
;
871 int state
= DEMANGLE_STATE_START
;
873 /* the hex state is for stuff like ?$EA which
874 really means hex value 0x40 */
876 char string_is_unicode
= 0;
878 /* sometimes we get a null-termination before the
879 final @ sign - in that case, remember that
880 we've seen the whole string */
881 int have_null_char
= 0;
883 /* stick our user-readable prefix on */
884 strcpy(curresult
, "string: \"");
889 // process current state
892 /* the Prologue states are divided up so
893 that someday we can try to decode
894 the random letters in between the '@'
895 signs. Also, some strings only have 2
896 prologue '@' signs, so we have to
897 figure out how to distinguish between
898 them at some point. */
899 case DEMANGLE_STATE_START
:
901 state
= DEMANGLE_STATE_PROLOGUE_1
;
902 /* ignore all other states */
905 case DEMANGLE_STATE_PROLOGUE_1
:
909 state
= DEMANGLE_STATE_HAVE_TYPE
;
913 state
= DEMANGLE_STATE_HAVE_TYPE
;
916 /* ignore all other characters */
920 case DEMANGLE_STATE_HAVE_TYPE
:
921 if (*curchar
>= '0' && *curchar
<= '9') {
922 state
= DEMANGLE_STATE_DEC_LENGTH
;
923 } else if (*curchar
>= 'A' && *curchar
<= 'Z') {
924 state
= DEMANGLE_STATE_HEX_LENGTH
;
926 case DEMANGLE_STATE_DEC_LENGTH
:
927 /* decimal lengths don't have the 2nd
931 state
= DEMANGLE_STATE_NORMAL
;
934 case DEMANGLE_STATE_HEX_LENGTH
:
935 /* hex lengths have a 2nd field
936 (though I have no idea what it is for)
939 state
= DEMANGLE_STATE_PROLOGUE_SECONDARY
;
942 case DEMANGLE_STATE_PROLOGUE_SECONDARY
:
944 state
= DEMANGLE_STATE_NORMAL
;
947 case DEMANGLE_STATE_NORMAL
:
950 state
= DEMANGLE_STATE_QDECODE
;
953 state
= DEMANGLE_STATE_STOP
;
956 *curresult
++ = DEMANGLE_SAFE_CHAR(*curchar
);
957 state
= DEMANGLE_STATE_NORMAL
;
963 case DEMANGLE_STATE_QDECODE
:
964 state
= DEMANGLE_STATE_NORMAL
;
966 /* there are certain shortcuts, like
996 /* any other arbitrary ASCII value can
997 be stored by prefixing it with ?$
1000 state
= DEMANGLE_STATE_DOLLAR_1
;
1004 case DEMANGLE_STATE_DOLLAR_1
:
1005 /* first digit of ?$ notation. All digits
1006 are hex, represented starting with the
1007 capital leter 'A' such that 'A' means 0x0,
1008 'B' means 0x1, 'K' means 0xA
1010 hex_state
= (*curchar
- 'A') * 0x10;
1011 state
= DEMANGLE_STATE_DOLLAR_2
;
1014 case DEMANGLE_STATE_DOLLAR_2
:
1015 /* same mechanism as above */
1016 hex_state
+= (*curchar
- 'A');
1018 *curresult
++ = DEMANGLE_SAFE_CHAR(hex_state
);
1025 state
= DEMANGLE_STATE_NORMAL
;
1028 case DEMANGLE_STATE_STOP
:
1035 /* add the appropriate termination depending
1036 if we completed the string or not */
1037 if (!have_null_char
)
1038 strcpy(curresult
, "...\"");
1040 strcpy(curresult
, "\"");
1042 retval
= strdup(demangleBuf
);
1048 ** fall back to normal.
1050 retval
= strdup(inSymbol
);
1053 else if('_' == inSymbol
[0])
1055 retval
= strdup(inSymbol
+ 1);
1059 retval
= strdup(inSymbol
);
1063 ** May need to rewrite the symbol if an import.
1065 if(NULL
!= retval
&& isImport
)
1067 const char importPrefix
[] = "__declspec(dllimport) ";
1068 char importBuf
[0x200];
1071 printRes
= _snprintf(importBuf
, sizeof(importBuf
), "%s%s", importPrefix
, retval
);
1077 retval
= strdup(importBuf
);
1081 #else /* F_DEMANGLE */
1082 retval
= strdup(inSymbol
);
1083 #endif /* F_DEMANGLE */
1089 int readmap(Options
* inOptions
, MSMap_Module
* inModule
)
1091 ** Read the input line by line, adding it to the module.
1095 char lineBuffer
[0x400];
1096 char* current
= NULL
;
1097 MSMap_ReadState fsm
;
1099 int forceContinue
= 0;
1101 memset(&fsm
, 0, sizeof(fsm
));
1104 ** Read the map file line by line.
1105 ** We keep a simple state machine to determine what we're looking at.
1107 while(0 == retval
&& NULL
!= fgets(lineBuffer
, sizeof(lineBuffer
), inOptions
->mInput
))
1112 ** Used to skip anticipated blank lines.
1118 current
= skipWhite(lineBuffer
);
1121 len
= strlen(current
);
1125 if(fsm
.mHasTimestamp
)
1127 if(fsm
.mHasPreferredLoadAddress
)
1129 if(fsm
.mHasSegmentData
)
1131 if(fsm
.mHasPublicSymbolData
)
1133 if(fsm
.mHasEntryPoint
)
1135 if(fsm
.mFoundStaticSymbols
)
1138 ** A blank line means we've reached the end of all static symbols.
1143 ** We're adding a new symbol.
1144 ** Make sure we have room for it.
1146 if(inModule
->mSymbolCapacity
== inModule
->mSymbolCount
)
1150 moved
= realloc(inModule
->mSymbols
, sizeof(MSMap_Symbol
) * (inModule
->mSymbolCapacity
+ MSMAP_SYMBOL_GROWBY
));
1153 inModule
->mSymbolCapacity
+= MSMAP_SYMBOL_GROWBY
;
1154 inModule
->mSymbols
= (MSMap_Symbol
*)moved
;
1159 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to grow symbols.");
1163 if(0 == retval
&& inModule
->mSymbolCapacity
> inModule
->mSymbolCount
)
1165 MSMap_Symbol
* theSymbol
= NULL
;
1168 char symbolBuf
[SYMBOL_BUF_CHARS
+ 1];
1170 index
= inModule
->mSymbolCount
;
1171 inModule
->mSymbolCount
++;
1172 theSymbol
= (inModule
->mSymbols
+ index
);
1174 memset(theSymbol
, 0, sizeof(MSMap_Symbol
));
1175 theSymbol
->mScope
= STATIC
;
1177 scanRes
= sscanf(current
, "%x:%x %" STRINGIFY(SYMBOL_BUF_CHARS
) "s %x", (unsigned*)&(theSymbol
->mPrefix
), (unsigned*)&(theSymbol
->mOffset
), symbolBuf
, (unsigned*)&(theSymbol
->mRVABase
));
1180 theSymbol
->mSymbol
= symdup(symbolBuf
);
1184 if(NULL
!= theSymbol
->mSymbol
)
1186 char *last
= lastWord(current
);
1188 theSymbol
->mObject
= strdup(last
);
1189 if(NULL
== theSymbol
->mObject
)
1192 ERROR_REPORT(retval
, last
, "Unable to copy object name.");
1198 ERROR_REPORT(retval
, symbolBuf
, "Unable to copy symbol name.");
1205 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to scan static symbols.");
1220 ** Static symbols are optional.
1221 ** If no static symbols we're done.
1222 ** Otherwise, set the flag such that it will work more.
1224 if(0 == strcmp(current
, "Static symbols"))
1226 fsm
.mFoundStaticSymbols
= __LINE__
;
1242 scanRes
= sscanf(current
, "entry point at %x:%x", (unsigned*)&(inModule
->mEntryPrefix
), (unsigned*)&(inModule
->mEntryOffset
));
1245 fsm
.mHasEntryPoint
= __LINE__
;
1251 ERROR_REPORT(retval
, current
, "Unable to obtain entry point.");
1258 ** Skip the N lines of public symbol data (column headers).
1260 if(2 <= fsm
.mHasPublicSymbolDataSkippedLines
)
1263 ** A blank line indicates end of public symbols.
1268 ** We're adding a new symbol.
1269 ** Make sure we have room for it.
1271 if(inModule
->mSymbolCapacity
== inModule
->mSymbolCount
)
1275 moved
= realloc(inModule
->mSymbols
, sizeof(MSMap_Symbol
) * (inModule
->mSymbolCapacity
+ MSMAP_SYMBOL_GROWBY
));
1278 inModule
->mSymbolCapacity
+= MSMAP_SYMBOL_GROWBY
;
1279 inModule
->mSymbols
= (MSMap_Symbol
*)moved
;
1284 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to grow symbols.");
1288 if(0 == retval
&& inModule
->mSymbolCapacity
> inModule
->mSymbolCount
)
1290 MSMap_Symbol
* theSymbol
= NULL
;
1293 char symbolBuf
[SYMBOL_BUF_CHARS
+ 1];
1295 index
= inModule
->mSymbolCount
;
1296 inModule
->mSymbolCount
++;
1297 theSymbol
= (inModule
->mSymbols
+ index
);
1299 memset(theSymbol
, 0, sizeof(MSMap_Symbol
));
1300 theSymbol
->mScope
= PUBLIC
;
1302 scanRes
= sscanf(current
, "%x:%x %" STRINGIFY(SYMBOL_BUF_CHARS
) "s %x", (unsigned*)&(theSymbol
->mPrefix
), (unsigned*)&(theSymbol
->mOffset
), symbolBuf
, (unsigned *)&(theSymbol
->mRVABase
));
1305 theSymbol
->mSymbol
= symdup(symbolBuf
);
1307 if(NULL
!= theSymbol
->mSymbol
)
1309 char *last
= lastWord(current
);
1311 theSymbol
->mObject
= strdup(last
);
1312 if(NULL
!= theSymbol
->mObject
)
1315 ** Finally, attempt to lookup the actual size of the symbol
1316 ** if there is a symbol DB available.
1318 retval
= fillSymbolSizeFromDB(inOptions
, inModule
, theSymbol
, symbolBuf
);
1323 ERROR_REPORT(retval
, last
, "Unable to copy object name.");
1329 ERROR_REPORT(retval
, symbolBuf
, "Unable to copy symbol name.");
1335 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to scan public symbols.");
1341 fsm
.mHasPublicSymbolData
= __LINE__
;
1346 fsm
.mHasPublicSymbolDataSkippedLines
++;
1353 ** Skip the first line of segment data (column headers).
1354 ** Mark that we've begun grabbing segement data.
1356 if(fsm
.mSegmentDataSkippedLine
)
1359 ** A blank line means end of the segment data.
1364 ** We're adding a new segment.
1365 ** Make sure we have room for it.
1367 if(inModule
->mSegmentCapacity
== inModule
->mSegmentCount
)
1371 moved
= realloc(inModule
->mSegments
, sizeof(MSMap_Segment
) * (inModule
->mSegmentCapacity
+ MSMAP_SEGMENT_GROWBY
));
1374 inModule
->mSegmentCapacity
+= MSMAP_SEGMENT_GROWBY
;
1375 inModule
->mSegments
= (MSMap_Segment
*)moved
;
1380 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to grow segments.");
1384 if(0 == retval
&& inModule
->mSegmentCapacity
> inModule
->mSegmentCount
)
1386 MSMap_Segment
* theSegment
= NULL
;
1388 #define CLASS_BUF_CHARS 15
1389 char classBuf
[CLASS_BUF_CHARS
+ 1];
1390 #define NAME_BUF_CHARS 31
1391 char nameBuf
[NAME_BUF_CHARS
+ 1];
1394 index
= inModule
->mSegmentCount
;
1395 inModule
->mSegmentCount
++;
1396 theSegment
= (inModule
->mSegments
+ index
);
1398 memset(theSegment
, 0, sizeof(MSMap_Segment
));
1400 scanRes
= sscanf(current
, "%x:%x %xH %" STRINGIFY(NAME_BUF_CHARS
) "s %" STRINGIFY(CLASS_BUF_CHARS
) "s", (unsigned*)&(theSegment
->mPrefix
), (unsigned*)&(theSegment
->mOffset
), (unsigned*)&(theSegment
->mLength
), nameBuf
, classBuf
);
1403 if('.' == nameBuf
[0])
1405 theSegment
->mSegment
= strdup(&nameBuf
[1]);
1409 theSegment
->mSegment
= strdup(nameBuf
);
1412 if(NULL
!= theSegment
->mSegment
)
1414 if(0 == strcmp("DATA", classBuf
))
1416 theSegment
->mClass
= DATA
;
1418 else if(0 == strcmp("CODE", classBuf
))
1420 theSegment
->mClass
= CODE
;
1425 ERROR_REPORT(retval
, classBuf
, "Unrecognized segment class.");
1431 ERROR_REPORT(retval
, nameBuf
, "Unable to copy segment name.");
1437 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to scan segments.");
1443 fsm
.mHasSegmentData
= __LINE__
;
1448 fsm
.mSegmentDataSkippedLine
= __LINE__
;
1457 ** The PLA has a particular format.
1459 scanRes
= sscanf(current
, "Preferred load address is %x", (unsigned*)&(inModule
->mPreferredLoadAddress
));
1462 fsm
.mHasPreferredLoadAddress
= __LINE__
;
1468 ERROR_REPORT(retval
, current
, "Unable to obtain preferred load address.");
1477 ** The timestamp has a particular format.
1479 scanRes
= sscanf(current
, "Timestamp is %x", (unsigned*)&(inModule
->mTimestamp
));
1482 fsm
.mHasTimestamp
= __LINE__
;
1488 ERROR_REPORT(retval
, current
, "Unable to obtain timestamp.");
1495 ** The module is on a line by itself.
1497 inModule
->mModule
= strdup(current
);
1498 if(NULL
!= inModule
->mModule
)
1500 fsm
.mHasModule
= __LINE__
;
1503 if(0 != inOptions
->mMatchModuleCount
)
1505 unsigned matchLoop
= 0;
1508 ** If this module name doesn't match, then bail.
1509 ** Compare in a case sensitive manner, exact match only.
1511 for(matchLoop
= 0; matchLoop
< inOptions
->mMatchModuleCount
; matchLoop
++)
1513 if(0 == strcmp(inModule
->mModule
, inOptions
->mMatchModules
[matchLoop
]))
1519 if(matchLoop
== inOptions
->mMatchModuleCount
)
1522 ** A match did not occur, bail out of read loop.
1523 ** No error, however.
1532 ERROR_REPORT(retval
, current
, "Unable to obtain module.");
1537 if(0 == retval
&& 0 != ferror(inOptions
->mInput
))
1540 ERROR_REPORT(retval
, inOptions
->mInputName
, "Unable to read file.");
1547 static int qsortRVABase(const void* in1
, const void* in2
)
1549 ** qsort callback to sort the symbols by their RVABase.
1552 MSMap_Symbol
* sym1
= (MSMap_Symbol
*)in1
;
1553 MSMap_Symbol
* sym2
= (MSMap_Symbol
*)in2
;
1556 if(sym1
->mRVABase
< sym2
->mRVABase
)
1560 else if(sym1
->mRVABase
> sym2
->mRVABase
)
1569 static int tsvout(Options
* inOptions
, unsigned inSize
, MSMap_SegmentClass inClass
, MSMap_SymbolScope inScope
, const char* inModule
, const char* inSegment
, const char* inObject
, const char* inSymbol
)
1571 ** Output a line of map information separated by tabs.
1572 ** Some items (const char*), if not present, will receive a default value.
1578 ** No need to output on no size.
1579 ** This can happen with zero sized segments,
1580 ** or an imported symbol which has multiple names (one will count).
1584 char objectBuf
[0x100];
1585 const char* symScope
= NULL
;
1586 const char* segClass
= NULL
;
1587 const char* undefined
= "UNDEF";
1590 ** Fill in unspecified values.
1592 if(NULL
== inObject
)
1594 sprintf(objectBuf
, "%s:%s:%s", undefined
, inModule
, inSegment
);
1595 inObject
= objectBuf
;
1597 if(NULL
== inSymbol
)
1599 inSymbol
= inObject
;
1603 ** Convert some enumerations to text.
1615 ERROR_REPORT(retval
, "", "Unable to determine class for output.");
1622 symScope
= "PUBLIC";
1625 symScope
= "STATIC";
1628 symScope
= undefined
;
1632 ERROR_REPORT(retval
, "", "Unable to determine scope for symbol.");
1640 printRes
= fprintf(inOptions
->mOutput
,
1641 "%.8X\t%s\t%s\t%s\t%s\t%s\t%s\n",
1654 ERROR_REPORT(retval
, inOptions
->mOutputName
, "Unable to output tsv data.");
1663 void cleanModule(MSMap_Module
* inModule
)
1667 for(loop
= 0; loop
< inModule
->mSymbolCount
; loop
++)
1669 CLEANUP(inModule
->mSymbols
[loop
].mObject
);
1670 CLEANUP(inModule
->mSymbols
[loop
].mSymbol
);
1672 CLEANUP(inModule
->mSymbols
);
1674 for(loop
= 0; loop
< inModule
->mSegmentCount
; loop
++)
1676 CLEANUP(inModule
->mSegments
[loop
].mSegment
);
1678 CLEANUP(inModule
->mSegments
);
1680 CLEANUP(inModule
->mModule
);
1682 memset(inModule
, 0, sizeof(MSMap_Module
));
1686 int map2tsv(Options
* inOptions
)
1689 ** Output tab separated value data.
1693 MSMap_Module module
;
1695 memset(&module
, 0, sizeof(module
));
1698 ** Read in the map file.
1700 retval
= readmap(inOptions
, &module
);
1703 unsigned symLoop
= 0;
1704 MSMap_Symbol
* symbol
= NULL
;
1705 unsigned secLoop
= 0;
1706 MSMap_Segment
* section
= NULL
;
1708 unsigned dbSize
= 0;
1709 unsigned offsetSize
= 0;
1710 unsigned endOffset
= 0;
1713 ** Quick sort the symbols via RVABase.
1715 qsort(module
.mSymbols
, module
.mSymbolCount
, sizeof(MSMap_Symbol
), qsortRVABase
);
1718 ** Go through all the symbols (in order by sort).
1719 ** Output their sizes.
1721 for(symLoop
= 0; 0 == retval
&& symLoop
< module
.mSymbolCount
; symLoop
++)
1723 symbol
= &module
.mSymbols
[symLoop
];
1724 section
= getSymbolSection(&module
, symbol
);
1729 ** Use the symbol DB size if available.
1731 dbSize
= symbol
->mSymDBSize
;
1734 ** Guess using offsets.
1735 ** Is there a next symbol available? If so, its start offset is the end of this symbol.
1736 ** Otherwise, our section offset + length is the end of this symbol.
1738 ** The trick is, the DB size can not go beyond the offset size, for sanity.
1742 ** Try next symbol, but only if in same section.
1743 ** If still not, use the end of the segment.
1744 ** This implies we were the last symbol in the segment.
1746 if((symLoop
+ 1) < module
.mSymbolCount
)
1748 MSMap_Symbol
* nextSymbol
= NULL
;
1749 MSMap_Segment
* nextSection
= NULL
;
1751 nextSymbol
= &module
.mSymbols
[symLoop
+ 1];
1752 nextSection
= getSymbolSection(&module
, nextSymbol
);
1754 if(section
== nextSection
)
1756 endOffset
= nextSymbol
->mOffset
;
1760 endOffset
= section
->mOffset
+ section
->mLength
;
1765 endOffset
= section
->mOffset
+ section
->mLength
;
1769 ** Can now guess at size.
1771 offsetSize
= endOffset
- symbol
->mOffset
;
1774 ** Now, determine which size to use.
1775 ** This is really a sanity check as well.
1780 if(dbSize
< offsetSize
)
1787 ** Output the symbol with the size.
1789 retval
= tsvout(inOptions
,
1800 ** Make sure we mark this amount of space as used in the section.
1802 section
->mUsed
+= size
;
1806 ** Go through the sections, and those whose length is longer than the
1807 ** amount of space used, output dummy filler values.
1809 for(secLoop
= 0; 0 == retval
&& secLoop
< module
.mSegmentCount
; secLoop
++)
1811 section
= &module
.mSegments
[secLoop
];
1813 if(section
&& section
->mUsed
< section
->mLength
)
1815 retval
= tsvout(inOptions
,
1816 section
->mLength
- section
->mUsed
,
1831 cleanModule(&module
);
1837 int initOptions(Options
* outOptions
, int inArgc
, char** inArgv
)
1839 ** returns int 0 if successful.
1846 const int switchCount
= sizeof(gSwitches
) / sizeof(gSwitches
[0]);
1847 Switch
* current
= NULL
;
1850 ** Set any defaults.
1852 memset(outOptions
, 0, sizeof(Options
));
1853 outOptions
->mProgramName
= inArgv
[0];
1854 outOptions
->mInput
= stdin
;
1855 outOptions
->mInputName
= strdup("stdin");
1856 outOptions
->mOutput
= stdout
;
1857 outOptions
->mOutputName
= strdup("stdout");
1859 if(NULL
== outOptions
->mOutputName
|| NULL
== outOptions
->mInputName
)
1862 ERROR_REPORT(retval
, "stdin/stdout", "Unable to strdup.");
1866 ** Go through and attempt to do the right thing.
1868 for(loop
= 1; loop
< inArgc
&& 0 == retval
; loop
++)
1873 for(switchLoop
= 0; switchLoop
< switchCount
&& 0 == retval
; switchLoop
++)
1875 if(0 == strcmp(gSwitches
[switchLoop
]->mLongName
, inArgv
[loop
]))
1879 else if(0 == strcmp(gSwitches
[switchLoop
]->mShortName
, inArgv
[loop
]))
1886 if(gSwitches
[switchLoop
]->mHasValue
)
1889 ** Attempt to absorb next option to fullfill value.
1891 if(loop
+ 1 < inArgc
)
1895 current
= gSwitches
[switchLoop
];
1896 current
->mValue
= inArgv
[loop
];
1901 current
= gSwitches
[switchLoop
];
1910 outOptions
->mHelp
= __LINE__
;
1912 ERROR_REPORT(retval
, inArgv
[loop
], "Unknown command line switch.");
1914 else if(NULL
== current
)
1916 outOptions
->mHelp
= __LINE__
;
1918 ERROR_REPORT(retval
, inArgv
[loop
], "Command line switch requires a value.");
1923 ** Do something based on address/swtich.
1925 if(current
== &gInputSwitch
)
1927 CLEANUP(outOptions
->mInputName
);
1928 if(NULL
!= outOptions
->mInput
&& stdin
!= outOptions
->mInput
)
1930 fclose(outOptions
->mInput
);
1931 outOptions
->mInput
= NULL
;
1934 outOptions
->mInput
= fopen(current
->mValue
, "r");
1935 if(NULL
== outOptions
->mInput
)
1938 ERROR_REPORT(retval
, current
->mValue
, "Unable to open input file.");
1942 outOptions
->mInputName
= strdup(current
->mValue
);
1943 if(NULL
== outOptions
->mInputName
)
1946 ERROR_REPORT(retval
, current
->mValue
, "Unable to strdup.");
1950 else if(current
== &gOutputSwitch
)
1952 CLEANUP(outOptions
->mOutputName
);
1953 if(NULL
!= outOptions
->mOutput
&& stdout
!= outOptions
->mOutput
)
1955 fclose(outOptions
->mOutput
);
1956 outOptions
->mOutput
= NULL
;
1959 outOptions
->mOutput
= fopen(current
->mValue
, "a");
1960 if(NULL
== outOptions
->mOutput
)
1963 ERROR_REPORT(retval
, current
->mValue
, "Unable to open output file.");
1967 outOptions
->mOutputName
= strdup(current
->mValue
);
1968 if(NULL
== outOptions
->mOutputName
)
1971 ERROR_REPORT(retval
, current
->mValue
, "Unable to strdup.");
1975 else if(current
== &gHelpSwitch
)
1977 outOptions
->mHelp
= __LINE__
;
1979 else if(current
== &gMatchModuleSwitch
)
1984 ** Add the value to the list of allowed module names.
1986 moved
= realloc(outOptions
->mMatchModules
, sizeof(char*) * (outOptions
->mMatchModuleCount
+ 1));
1989 outOptions
->mMatchModules
= (char**)moved
;
1990 outOptions
->mMatchModules
[outOptions
->mMatchModuleCount
] = strdup(current
->mValue
);
1991 if(NULL
!= outOptions
->mMatchModules
[outOptions
->mMatchModuleCount
])
1993 outOptions
->mMatchModuleCount
++;
1998 ERROR_REPORT(retval
, current
->mValue
, "Unable to duplicate string.");
2004 ERROR_REPORT(retval
, current
->mValue
, "Unable to allocate space for string.");
2007 else if(current
== &gSymDBSwitch
)
2009 CLEANUP(outOptions
->mSymDBName
);
2010 outOptions
->mSymDBName
= strdup(current
->mValue
);
2011 if(NULL
== outOptions
->mSymDBName
)
2014 ERROR_REPORT(retval
, current
->mValue
, "Unable to duplicate symbol db name.");
2017 else if(current
== &gBatchModeSwitch
)
2019 outOptions
->mBatchMode
= __LINE__
;
2024 ERROR_REPORT(retval
, current
->mLongName
, "No handler for command line switch.");
2033 void cleanOptions(Options
* inOptions
)
2035 ** Clean up any open handles, et. al.
2038 CLEANUP(inOptions
->mInputName
);
2039 if(NULL
!= inOptions
->mInput
&& stdin
!= inOptions
->mInput
)
2041 fclose(inOptions
->mInput
);
2043 CLEANUP(inOptions
->mOutputName
);
2044 if(NULL
!= inOptions
->mOutput
&& stdout
!= inOptions
->mOutput
)
2046 fclose(inOptions
->mOutput
);
2048 while(0 != inOptions
->mMatchModuleCount
)
2050 inOptions
->mMatchModuleCount
--;
2051 CLEANUP(inOptions
->mMatchModules
[inOptions
->mMatchModuleCount
]);
2053 CLEANUP(inOptions
->mMatchModules
);
2055 cleanSymDB(&inOptions
->mSymDB
);
2057 memset(inOptions
, 0, sizeof(Options
));
2061 void showHelp(Options
* inOptions
)
2063 ** Show some simple help text on usage.
2067 const int switchCount
= sizeof(gSwitches
) / sizeof(gSwitches
[0]);
2068 const char* valueText
= NULL
;
2070 printf("usage:\t%s [arguments]\n", inOptions
->mProgramName
);
2072 printf("arguments:\n");
2074 for(loop
= 0; loop
< switchCount
; loop
++)
2076 if(gSwitches
[loop
]->mHasValue
)
2078 valueText
= " <value>";
2085 printf("\t%s%s\n", gSwitches
[loop
]->mLongName
, valueText
);
2086 printf("\t %s%s", gSwitches
[loop
]->mShortName
, valueText
);
2087 printf(DESC_NEWLINE
"%s\n\n", gSwitches
[loop
]->mDescription
);
2090 printf("This tool normalizes MS linker .map files for use by other tools.\n");
2094 int batchMode(Options
* inOptions
)
2096 ** Batch mode means that the input file is actually a list of map files.
2097 ** We simply swap out our input file names while we do this.
2101 char lineBuf
[0x400];
2102 FILE* realInput
= NULL
;
2103 char* realInputName
= NULL
;
2104 FILE* mapFile
= NULL
;
2107 realInput
= inOptions
->mInput
;
2108 realInputName
= inOptions
->mInputName
;
2110 while(0 == retval
&& NULL
!= fgets(lineBuf
, sizeof(lineBuf
), realInput
))
2115 ** Skip/allow blank lines.
2117 if('\0' == lineBuf
[0])
2123 ** Override what we believe to be the input for this line.
2125 inOptions
->mInputName
= lineBuf
;
2126 inOptions
->mInput
= fopen(lineBuf
, "r");
2127 if(NULL
!= inOptions
->mInput
)
2134 mapRes
= map2tsv(inOptions
);
2137 ** We report the first error that we encounter, but we continue.
2138 ** This is batch mode after all.
2146 ** Close the input file.
2148 fclose(inOptions
->mInput
);
2153 ERROR_REPORT(retval
, lineBuf
, "Unable to open map file.");
2158 if(0 == retval
&& 0 != ferror(realInput
))
2161 ERROR_REPORT(retval
, realInputName
, "Unable to read file.");
2165 ** Restore what we've swapped.
2167 inOptions
->mInput
= realInput
;
2168 inOptions
->mInputName
= realInputName
;
2171 ** Report first map file error if there were no other operational
2183 int main(int inArgc
, char** inArgv
)
2188 retval
= initOptions(&options
, inArgc
, inArgv
);
2193 else if(0 == retval
)
2195 if(options
.mBatchMode
)
2197 retval
= batchMode(&options
);
2201 retval
= map2tsv(&options
);
2205 cleanOptions(&options
);