1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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
16 * The Original Code is msmap2tsv.c code, released
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.
25 * Garrett Arch Blythe, 03-October-2002
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.
39 * ***** END LICENSE BLOCK ***** */
54 #define DEMANGLE_STATE_NORMAL 0
55 #define DEMANGLE_STATE_QDECODE 1
56 #define DEMANGLE_STATE_PROLOGUE_1 2
57 #define DEMANGLE_STATE_HAVE_TYPE 3
58 #define DEMANGLE_STATE_DEC_LENGTH 4
59 #define DEMANGLE_STATE_HEX_LENGTH 5
60 #define DEMANGLE_STATE_PROLOGUE_SECONDARY 6
61 #define DEMANGLE_STATE_DOLLAR_1 7
62 #define DEMANGLE_STATE_DOLLAR_2 8
63 #define DEMANGLE_STATE_START 9
64 #define DEMANGLE_STATE_STOP 10
65 #define DEMANGLE_SAFE_CHAR(eval) (isprint(eval) ? eval : ' ')
72 #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
73 #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
76 typedef struct __struct_SymDB_Size
78 ** The size of the symbol.
79 ** The size is nested withing a symbols structures to produce a fast
81 ** The objects are listed in case the client of the symdb needs to
82 ** match the object name in the scenario where multiple symbol
85 ** mSize The size of the symbol in these objects.
86 ** mObjects A list of objects containing said symbol.
87 ** mObjectCount Number of objects.
92 unsigned mObjectCount
;
97 typedef struct __struct_SymDB_Section
99 ** Each section for a symbol has a list of sizes.
100 ** Should there be exactly one size for the symbol, then that
101 ** is the size that should be accepted.
102 ** If there is more than one size, then a match on the object
103 ** should be attempted, held withing each size.
105 ** mName The section name.
106 ** mSizes The varoius sizes of the symbol in this section.
107 ** mSizeCount The number of available sizes.
117 typedef struct __struct_SymDB_Symbol
119 ** Each symbol has at least one section.
120 ** The section indicates what type of symbol a client may be looking for.
121 ** If there is no match on the section, then the client should not trust
124 ** mName The mangled name of the symbol.
125 ** mSections Various sections this symbol belongs to.
126 ** mSectionCount The number of sections.
130 SymDB_Section
* mSections
;
131 unsigned mSectionCount
;
136 #define SYMDB_SYMBOL_GROWBY 0x1000 /* how many sybols to allocate at a time */
139 typedef struct __struct_SymDB_Container
141 ** The symbol DB container object.
142 ** The goal of the symbol DB is to have exactly one SymDB_Symbol for each
143 ** mangled name, no matter how ever many identical mangled names there
145 ** The input is already expected to be well sorted, futher this leads to
146 ** the ability to binary search for symbol name matches.
148 ** mSymbols The symbols.
149 ** mSymbolCount The number of symbols in the DB.
150 ** mSymbolCapacity The number of symbols we can hold (before realloc).
153 SymDB_Symbol
* mSymbols
;
154 unsigned mSymbolCount
;
155 unsigned mSymbolCapacity
;
160 typedef struct __struct_Options
162 ** Options to control how we perform.
164 ** mProgramName Used in help text.
165 ** mInput File to read for input.
167 ** mInputName Name of the file.
168 ** mOutput Output file, append.
169 ** Default is stdout.
170 ** mOutputName Name of the file.
171 ** mHelp Whether or not help should be shown.
172 ** mMatchModules Array of strings which the module name should match.
173 ** mMatchModuleCount Number of items in array.
174 ** mSymDBName Symbol DB filename.
175 ** mBatchMode Batch mode.
176 ** When in batch mode, the input file contains a list of
177 ** map files to process.
178 ** Normally the input file is a single map file itself.
181 const char* mProgramName
;
187 char** mMatchModules
;
188 unsigned mMatchModuleCount
;
190 SymDB_Container
* mSymDB
;
196 typedef struct __struct_Switch
198 ** Command line options.
201 const char* mLongName
;
202 const char* mShortName
;
205 const char* mDescription
;
209 #define DESC_NEWLINE "\n\t\t"
211 static Switch gInputSwitch
= {"--input", "-i", 1, NULL
, "Specify input file." DESC_NEWLINE
"stdin is default."};
212 static Switch gOutputSwitch
= {"--output", "-o", 1, NULL
, "Specify output file." DESC_NEWLINE
"Appends if file exists." DESC_NEWLINE
"stdout is default."};
213 static Switch gHelpSwitch
= {"--help", "-h", 0, NULL
, "Information on usage."};
214 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."};
215 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."};
216 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."};
218 static Switch
* gSwitches
[] = {
228 typedef struct __struct_MSMap_ReadState
230 ** Keep track of what state we are while reading input.
231 ** This gives the input context in which we absorb the datum.
238 int mHasPreferredLoadAddress
;
241 int mSegmentDataSkippedLine
;
243 int mHasPublicSymbolData
;
244 int mHasPublicSymbolDataSkippedLines
;
248 int mFoundStaticSymbols
;
253 char* skipWhite(char* inScan
)
258 char* retval
= inScan
;
260 while(isspace(*retval
))
268 void trimWhite(char* inString
)
270 ** Remove any whitespace from the end of the string.
273 int len
= strlen(inString
);
279 if(isspace(*(inString
+ len
)))
281 *(inString
+ len
) = '\0';
291 char* lastWord(char* inString
)
293 ** Finds and returns the last word in a string.
294 ** It is assumed no whitespace is at the end of the string.
298 int len
= strlen(inString
);
303 if(isspace(*(inString
+ len
)))
310 return inString
+ len
+ mod
;
314 MSMap_Segment
* getSymbolSection(MSMap_Module
* inModule
, MSMap_Symbol
* inoutSymbol
)
316 ** Perform a lookup for the section of the symbol.
317 ** The function could cache the value.
320 MSMap_Segment
* retval
= NULL
;
322 if(NULL
!= inoutSymbol
->mSection
)
327 retval
= inoutSymbol
->mSection
;
331 unsigned secLoop
= 0;
334 ** Go through sections in module to find the match for the symbol.
336 for(secLoop
= 0; secLoop
< inModule
->mSegmentCount
; secLoop
++)
338 if(inoutSymbol
->mPrefix
== inModule
->mSegments
[secLoop
].mPrefix
)
340 if(inoutSymbol
->mOffset
>= inModule
->mSegments
[secLoop
].mOffset
)
342 if(inoutSymbol
->mOffset
< (inModule
->mSegments
[secLoop
].mOffset
+ inModule
->mSegments
[secLoop
].mLength
))
345 ** We have the section.
347 retval
= &inModule
->mSegments
[secLoop
];
355 ** Cache the value for next time.
357 inoutSymbol
->mSection
= retval
;
364 int readSymDB(const char* inDBName
, SymDB_Container
** outDB
)
366 ** Intialize the symbol DB.
367 ** Only call if the symbol DB should be initialized.
373 ** Initialize out arguments.
380 if(NULL
!= outDB
&& NULL
!= inDBName
)
384 symDB
= fopen(inDBName
, "r");
387 *outDB
= (SymDB_Container
*)calloc(1, sizeof(SymDB_Container
));
392 char* section
= NULL
;
395 unsigned lengthNum
= 0;
396 char* endLength
= NULL
;
399 ** Read the file line by line.
401 while(0 == retval
&& NULL
!= fgets(lineBuf
, sizeof(lineBuf
), symDB
))
406 ** Each line has four arguments. tab separated values (tsv).
413 symbol
= skipWhite(lineBuf
);
417 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
421 section
= strchr(symbol
, '\t');
425 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
431 length
= strchr(section
, '\t');
435 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
441 object
= strchr(length
, '\t');
445 ERROR_REPORT(retval
, inDBName
, "File does not appear to be a symbol DB.");
452 ** Convert the length into a number.
455 lengthNum
= strtoul(length
, &endLength
, 16);
456 if(0 == errno
&& endLength
!= length
)
458 SymDB_Symbol
* dbSymbol
= NULL
;
459 SymDB_Section
* dbSection
= NULL
;
460 SymDB_Size
* dbSize
= NULL
;
461 char* dbObject
= NULL
;
465 ** Are we looking at the same symbol as last line?
466 ** This assumes the symdb is pre sorted!!!
468 if(0 != (*outDB
)->mSymbolCount
)
470 unsigned index
= (*outDB
)->mSymbolCount
- 1;
472 if(0 == strcmp((*outDB
)->mSymbols
[index
].mName
, symbol
))
474 dbSymbol
= &(*outDB
)->mSymbols
[index
];
479 ** May need to create symbol.
484 ** Could be time to grow the symbol pool.
486 if((*outDB
)->mSymbolCount
>= (*outDB
)->mSymbolCapacity
)
488 moved
= realloc((*outDB
)->mSymbols
, sizeof(SymDB_Symbol
) * ((*outDB
)->mSymbolCapacity
+ SYMDB_SYMBOL_GROWBY
));
491 (*outDB
)->mSymbols
= (SymDB_Symbol
*)moved
;
492 memset(&(*outDB
)->mSymbols
[(*outDB
)->mSymbolCapacity
], 0, sizeof(SymDB_Symbol
) * SYMDB_SYMBOL_GROWBY
);
493 (*outDB
)->mSymbolCapacity
+= SYMDB_SYMBOL_GROWBY
;
498 ERROR_REPORT(retval
, inDBName
, "Unable to grow symbol DB symbol array.");
503 if((*outDB
)->mSymbolCount
< (*outDB
)->mSymbolCapacity
)
505 dbSymbol
= &(*outDB
)->mSymbols
[(*outDB
)->mSymbolCount
];
506 (*outDB
)->mSymbolCount
++;
508 dbSymbol
->mName
= strdup(symbol
);
509 if(NULL
== dbSymbol
->mName
)
512 ERROR_REPORT(retval
, symbol
, "Unable to duplicate string.");
519 ERROR_REPORT(retval
, symbol
, "Unable to grow symbol DB for symbol.");
525 ** Assume we have the symbol.
527 ** Is this the same section as the last section in the symbol?
528 ** This assumes the symdb was presorted!!!!
530 if(0 != dbSymbol
->mSectionCount
)
532 unsigned index
= dbSymbol
->mSectionCount
- 1;
534 if(0 == strcmp(dbSymbol
->mSections
[index
].mName
, section
))
536 dbSection
= &dbSymbol
->mSections
[index
];
541 ** May need to create the section.
543 if(NULL
== dbSection
)
545 moved
= realloc(dbSymbol
->mSections
, sizeof(SymDB_Section
) * (dbSymbol
->mSectionCount
+ 1));
548 dbSymbol
->mSections
= (SymDB_Section
*)moved
;
549 dbSection
= &dbSymbol
->mSections
[dbSymbol
->mSectionCount
];
550 dbSymbol
->mSectionCount
++;
552 memset(dbSection
, 0, sizeof(SymDB_Section
));
554 dbSection
->mName
= strdup(section
);
555 if(NULL
== dbSection
->mName
)
558 ERROR_REPORT(retval
, section
, "Unable to duplicate string.");
565 ERROR_REPORT(retval
, section
, "Unable to grow symbol sections for symbol DB.");
571 ** Assume we have the section.
573 ** Is this the same size as the last size?
574 ** This assumes the symdb was presorted!!!
576 if(0 != dbSection
->mSizeCount
)
578 unsigned index
= dbSection
->mSizeCount
- 1;
580 if(dbSection
->mSizes
[index
].mSize
== lengthNum
)
582 dbSize
= &dbSection
->mSizes
[index
];
587 ** May need to create the size in question.
591 moved
= realloc(dbSection
->mSizes
, sizeof(SymDB_Size
) * (dbSection
->mSizeCount
+ 1));
594 dbSection
->mSizes
= (SymDB_Size
*)moved
;
595 dbSize
= &dbSection
->mSizes
[dbSection
->mSizeCount
];
596 dbSection
->mSizeCount
++;
598 memset(dbSize
, 0, sizeof(SymDB_Size
));
600 dbSize
->mSize
= lengthNum
;
605 ERROR_REPORT(retval
, length
, "Unable to grow symbol section sizes for symbol DB.");
611 ** Assume we have the size.
613 ** We assume a one to one correllation between size and object.
614 ** Always try to add the new object name.
615 ** As the symdb is assumed to be sorted, the object names should also be in order.
617 moved
= realloc(dbSize
->mObjects
, sizeof(char*) * (dbSize
->mObjectCount
+ 1));
620 dbObject
= strdup(object
);
622 dbSize
->mObjects
= (char**)moved
;
623 dbSize
->mObjects
[dbSize
->mObjectCount
] = dbObject
;
624 dbSize
->mObjectCount
++;
629 ERROR_REPORT(retval
, object
, "Unable to duplicate string.");
636 ERROR_REPORT(retval
, object
, "Unable to grow symbol section size objects for symbol DB.");
643 ERROR_REPORT(retval
, length
, "Unable to convert symbol DB length into a number.");
648 if(0 == retval
&& 0 != ferror(symDB
))
651 ERROR_REPORT(retval
, inDBName
, "Unable to read file.");
657 ERROR_REPORT(retval
, inDBName
, "Unable to allocate symbol DB.");
666 ERROR_REPORT(retval
, inDBName
, "Unable to open symbol DB.");
672 ERROR_REPORT(retval
, "(NULL)", "Invalid arguments.");
679 void cleanSymDB(SymDB_Container
** inDB
)
684 if(NULL
!= inDB
&& NULL
!= *inDB
)
686 unsigned symLoop
= 0;
687 unsigned secLoop
= 0;
688 unsigned sizLoop
= 0;
689 unsigned objLoop
= 0;
691 for(symLoop
= 0; symLoop
< (*inDB
)->mSymbolCount
; symLoop
++)
693 for(secLoop
= 0; secLoop
< (*inDB
)->mSymbols
[symLoop
].mSectionCount
; secLoop
++)
695 for(sizLoop
= 0; sizLoop
< (*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizeCount
; sizLoop
++)
697 for(objLoop
= 0; objLoop
< (*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
[sizLoop
].mObjectCount
; objLoop
++)
699 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
[sizLoop
].mObjects
[objLoop
]);
701 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
[sizLoop
].mObjects
);
703 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mName
);
704 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
[secLoop
].mSizes
);
706 CLEANUP((*inDB
)->mSymbols
[symLoop
].mName
);
707 CLEANUP((*inDB
)->mSymbols
[symLoop
].mSections
);
709 CLEANUP((*inDB
)->mSymbols
);
715 int symDBLookup(const void* inKey
, const void* inItem
)
717 ** bsearch utility routine to find the symbol in the symdb.
721 const char* key
= (const char*)inKey
;
722 const SymDB_Symbol
* symbol
= (const SymDB_Symbol
*)inItem
;
724 retval
= strcmp(key
, symbol
->mName
);
730 int fillSymbolSizeFromDB(Options
* inOptions
, MSMap_Module
* inModule
, MSMap_Symbol
* inoutSymbol
, const char* inMangledName
)
732 ** If we have a symbol DB, attempt to determine the real size of the symbol
734 ** This helps us later in the game to avoid performing size guesses by
741 ** May need to initialize symdb.
743 if(NULL
== inOptions
->mSymDB
&& NULL
!= inOptions
->mSymDBName
)
745 retval
= readSymDB(inOptions
->mSymDBName
, &inOptions
->mSymDB
);
751 if(0 == retval
&& NULL
!= inOptions
->mSymDB
)
758 match
= bsearch(inMangledName
, inOptions
->mSymDB
->mSymbols
, inOptions
->mSymDB
->mSymbolCount
, sizeof(SymDB_Symbol
), symDBLookup
);
761 SymDB_Symbol
* symbol
= (SymDB_Symbol
*)match
;
762 unsigned symDBSize
= 0;
763 MSMap_Segment
* mapSection
= NULL
;
766 ** We found the symbol.
768 ** See if it has the section in question.
770 mapSection
= getSymbolSection(inModule
, inoutSymbol
);
771 if(NULL
!= mapSection
)
773 unsigned secLoop
= 0;
775 for(secLoop
= 0; secLoop
< symbol
->mSectionCount
; secLoop
++)
777 if(0 == strcmp(mapSection
->mSegment
, symbol
->mSections
[secLoop
].mName
))
779 SymDB_Section
* section
= &symbol
->mSections
[secLoop
];
782 ** We have a section match.
783 ** Should there be a single size for the symbol,
784 ** then we just default to that.
785 ** If more than one size, we have to do an
786 ** object match search.
787 ** Should there be no object match, we do nothign.
789 if(1 == section
->mSizeCount
)
791 symDBSize
= section
->mSizes
[0].mSize
;
795 char* mapObject
= NULL
;
798 ** Figure out the map object file name.
800 ** If it doesn't have a .obj in it, not worth continuing.
802 mapObject
= strrchr(inoutSymbol
->mObject
, ':');
803 if(NULL
== mapObject
)
805 mapObject
= inoutSymbol
->mObject
;
809 mapObject
++; /* colon */
812 if(NULL
!= strstr(mapObject
, ".obj"))
814 unsigned sizLoop
= 0;
815 unsigned objLoop
= 0;
816 SymDB_Size
* size
= NULL
;
818 for(sizLoop
= 0; sizLoop
< section
->mSizeCount
; sizLoop
++)
820 size
= §ion
->mSizes
[sizLoop
];
822 for(objLoop
= 0; objLoop
< size
->mObjectCount
; objLoop
++)
824 if(NULL
!= strstr(size
->mObjects
[objLoop
], mapObject
))
827 ** As we matched the object, in a particular section,
828 ** we'll go with this as the number.
830 symDBSize
= size
->mSize
;
836 ** If the object loop broke early, we break too.
838 if(objLoop
< size
->mObjectCount
)
854 inoutSymbol
->mSymDBSize
= symDBSize
;
862 char* symdup(const char* inSymbol
)
864 ** Attempts to demangle the symbol if appropriate.
865 ** Otherwise acts like strdup.
874 if(0 == strncmp("__imp_", inSymbol
, 6))
880 if('?' == inSymbol
[0])
882 char demangleBuf
[0x200];
883 DWORD demangleRes
= 0;
885 demangleRes
= UnDecorateSymbolName(inSymbol
, demangleBuf
, sizeof(demangleBuf
), UNDNAME_COMPLETE
);
888 if (strcmp(demangleBuf
, "`string'") == 0)
891 /* attempt manual demangling of string prefix.. */
893 /* first make sure we have enough space for the
894 updated string - the demangled string will
895 always be shorter than strlen(inSymbol) and the
896 prologue will always be longer than the
897 "string: " that we tack on the front of the string
899 char *curresult
= retval
= malloc(strlen(inSymbol
) + 11);
900 const char *curchar
= inSymbol
;
902 int state
= DEMANGLE_STATE_START
;
904 /* the hex state is for stuff like ?$EA which
905 really means hex value 0x40 */
907 char string_is_unicode
= 0;
909 /* sometimes we get a null-termination before the
910 final @ sign - in that case, remember that
911 we've seen the whole string */
912 int have_null_char
= 0;
914 /* stick our user-readable prefix on */
915 strcpy(curresult
, "string: \"");
920 // process current state
923 /* the Prologue states are divided up so
924 that someday we can try to decode
925 the random letters in between the '@'
926 signs. Also, some strings only have 2
927 prologue '@' signs, so we have to
928 figure out how to distinguish between
929 them at some point. */
930 case DEMANGLE_STATE_START
:
932 state
= DEMANGLE_STATE_PROLOGUE_1
;
933 /* ignore all other states */
936 case DEMANGLE_STATE_PROLOGUE_1
:
940 state
= DEMANGLE_STATE_HAVE_TYPE
;
944 state
= DEMANGLE_STATE_HAVE_TYPE
;
947 /* ignore all other characters */
951 case DEMANGLE_STATE_HAVE_TYPE
:
952 if (*curchar
>= '0' && *curchar
<= '9') {
953 state
= DEMANGLE_STATE_DEC_LENGTH
;
954 } else if (*curchar
>= 'A' && *curchar
<= 'Z') {
955 state
= DEMANGLE_STATE_HEX_LENGTH
;
957 case DEMANGLE_STATE_DEC_LENGTH
:
958 /* decimal lengths don't have the 2nd
962 state
= DEMANGLE_STATE_NORMAL
;
965 case DEMANGLE_STATE_HEX_LENGTH
:
966 /* hex lengths have a 2nd field
967 (though I have no idea what it is for)
970 state
= DEMANGLE_STATE_PROLOGUE_SECONDARY
;
973 case DEMANGLE_STATE_PROLOGUE_SECONDARY
:
975 state
= DEMANGLE_STATE_NORMAL
;
978 case DEMANGLE_STATE_NORMAL
:
981 state
= DEMANGLE_STATE_QDECODE
;
984 state
= DEMANGLE_STATE_STOP
;
987 *curresult
++ = DEMANGLE_SAFE_CHAR(*curchar
);
988 state
= DEMANGLE_STATE_NORMAL
;
994 case DEMANGLE_STATE_QDECODE
:
995 state
= DEMANGLE_STATE_NORMAL
;
997 /* there are certain shortcuts, like
1005 *curresult
++ = '\\';
1017 *curresult
++ = '\\';
1021 *curresult
++ = '\'';
1027 /* any other arbitrary ASCII value can
1028 be stored by prefixing it with ?$
1031 state
= DEMANGLE_STATE_DOLLAR_1
;
1035 case DEMANGLE_STATE_DOLLAR_1
:
1036 /* first digit of ?$ notation. All digits
1037 are hex, represented starting with the
1038 capital leter 'A' such that 'A' means 0x0,
1039 'B' means 0x1, 'K' means 0xA
1041 hex_state
= (*curchar
- 'A') * 0x10;
1042 state
= DEMANGLE_STATE_DOLLAR_2
;
1045 case DEMANGLE_STATE_DOLLAR_2
:
1046 /* same mechanism as above */
1047 hex_state
+= (*curchar
- 'A');
1049 *curresult
++ = DEMANGLE_SAFE_CHAR(hex_state
);
1056 state
= DEMANGLE_STATE_NORMAL
;
1059 case DEMANGLE_STATE_STOP
:
1066 /* add the appropriate termination depending
1067 if we completed the string or not */
1068 if (!have_null_char
)
1069 strcpy(curresult
, "...\"");
1071 strcpy(curresult
, "\"");
1073 retval
= strdup(demangleBuf
);
1079 ** fall back to normal.
1081 retval
= strdup(inSymbol
);
1084 else if('_' == inSymbol
[0])
1086 retval
= strdup(inSymbol
+ 1);
1090 retval
= strdup(inSymbol
);
1094 ** May need to rewrite the symbol if an import.
1096 if(NULL
!= retval
&& isImport
)
1098 const char importPrefix
[] = "__declspec(dllimport) ";
1099 char importBuf
[0x200];
1102 printRes
= _snprintf(importBuf
, sizeof(importBuf
), "%s%s", importPrefix
, retval
);
1108 retval
= strdup(importBuf
);
1112 #else /* F_DEMANGLE */
1113 retval
= strdup(inSymbol
);
1114 #endif /* F_DEMANGLE */
1120 int readmap(Options
* inOptions
, MSMap_Module
* inModule
)
1122 ** Read the input line by line, adding it to the module.
1126 char lineBuffer
[0x400];
1127 char* current
= NULL
;
1128 MSMap_ReadState fsm
;
1130 int forceContinue
= 0;
1132 memset(&fsm
, 0, sizeof(fsm
));
1135 ** Read the map file line by line.
1136 ** We keep a simple state machine to determine what we're looking at.
1138 while(0 == retval
&& NULL
!= fgets(lineBuffer
, sizeof(lineBuffer
), inOptions
->mInput
))
1143 ** Used to skip anticipated blank lines.
1149 current
= skipWhite(lineBuffer
);
1152 len
= strlen(current
);
1156 if(fsm
.mHasTimestamp
)
1158 if(fsm
.mHasPreferredLoadAddress
)
1160 if(fsm
.mHasSegmentData
)
1162 if(fsm
.mHasPublicSymbolData
)
1164 if(fsm
.mHasEntryPoint
)
1166 if(fsm
.mFoundStaticSymbols
)
1169 ** A blank line means we've reached the end of all static symbols.
1174 ** We're adding a new symbol.
1175 ** Make sure we have room for it.
1177 if(inModule
->mSymbolCapacity
== inModule
->mSymbolCount
)
1181 moved
= realloc(inModule
->mSymbols
, sizeof(MSMap_Symbol
) * (inModule
->mSymbolCapacity
+ MSMAP_SYMBOL_GROWBY
));
1184 inModule
->mSymbolCapacity
+= MSMAP_SYMBOL_GROWBY
;
1185 inModule
->mSymbols
= (MSMap_Symbol
*)moved
;
1190 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to grow symbols.");
1194 if(0 == retval
&& inModule
->mSymbolCapacity
> inModule
->mSymbolCount
)
1196 MSMap_Symbol
* theSymbol
= NULL
;
1199 char symbolBuf
[0x200];
1201 index
= inModule
->mSymbolCount
;
1202 inModule
->mSymbolCount
++;
1203 theSymbol
= (inModule
->mSymbols
+ index
);
1205 memset(theSymbol
, 0, sizeof(MSMap_Symbol
));
1206 theSymbol
->mScope
= STATIC
;
1208 scanRes
= sscanf(current
, "%x:%x %s %x", (unsigned*)&(theSymbol
->mPrefix
), (unsigned*)&(theSymbol
->mOffset
), symbolBuf
, (unsigned*)&(theSymbol
->mRVABase
));
1211 theSymbol
->mSymbol
= symdup(symbolBuf
);
1215 if(NULL
!= theSymbol
->mSymbol
)
1217 char *last
= lastWord(current
);
1219 theSymbol
->mObject
= strdup(last
);
1220 if(NULL
== theSymbol
->mObject
)
1223 ERROR_REPORT(retval
, last
, "Unable to copy object name.");
1229 ERROR_REPORT(retval
, symbolBuf
, "Unable to copy symbol name.");
1236 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to scan static symbols.");
1251 ** Static symbols are optional.
1252 ** If no static symbols we're done.
1253 ** Otherwise, set the flag such that it will work more.
1255 if(0 == strcmp(current
, "Static symbols"))
1257 fsm
.mFoundStaticSymbols
= __LINE__
;
1273 scanRes
= sscanf(current
, "entry point at %x:%x", (unsigned*)&(inModule
->mEntryPrefix
), (unsigned*)&(inModule
->mEntryOffset
));
1276 fsm
.mHasEntryPoint
= __LINE__
;
1282 ERROR_REPORT(retval
, current
, "Unable to obtain entry point.");
1289 ** Skip the N lines of public symbol data (column headers).
1291 if(2 <= fsm
.mHasPublicSymbolDataSkippedLines
)
1294 ** A blank line indicates end of public symbols.
1299 ** We're adding a new symbol.
1300 ** Make sure we have room for it.
1302 if(inModule
->mSymbolCapacity
== inModule
->mSymbolCount
)
1306 moved
= realloc(inModule
->mSymbols
, sizeof(MSMap_Symbol
) * (inModule
->mSymbolCapacity
+ MSMAP_SYMBOL_GROWBY
));
1309 inModule
->mSymbolCapacity
+= MSMAP_SYMBOL_GROWBY
;
1310 inModule
->mSymbols
= (MSMap_Symbol
*)moved
;
1315 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to grow symbols.");
1319 if(0 == retval
&& inModule
->mSymbolCapacity
> inModule
->mSymbolCount
)
1321 MSMap_Symbol
* theSymbol
= NULL
;
1324 char symbolBuf
[0x200];
1326 index
= inModule
->mSymbolCount
;
1327 inModule
->mSymbolCount
++;
1328 theSymbol
= (inModule
->mSymbols
+ index
);
1330 memset(theSymbol
, 0, sizeof(MSMap_Symbol
));
1331 theSymbol
->mScope
= PUBLIC
;
1333 scanRes
= sscanf(current
, "%x:%x %s %x", (unsigned*)&(theSymbol
->mPrefix
), (unsigned*)&(theSymbol
->mOffset
), symbolBuf
, (unsigned *)&(theSymbol
->mRVABase
));
1336 theSymbol
->mSymbol
= symdup(symbolBuf
);
1338 if(NULL
!= theSymbol
->mSymbol
)
1340 char *last
= lastWord(current
);
1342 theSymbol
->mObject
= strdup(last
);
1343 if(NULL
!= theSymbol
->mObject
)
1346 ** Finally, attempt to lookup the actual size of the symbol
1347 ** if there is a symbol DB available.
1349 retval
= fillSymbolSizeFromDB(inOptions
, inModule
, theSymbol
, symbolBuf
);
1354 ERROR_REPORT(retval
, last
, "Unable to copy object name.");
1360 ERROR_REPORT(retval
, symbolBuf
, "Unable to copy symbol name.");
1366 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to scan public symbols.");
1372 fsm
.mHasPublicSymbolData
= __LINE__
;
1377 fsm
.mHasPublicSymbolDataSkippedLines
++;
1384 ** Skip the first line of segment data (column headers).
1385 ** Mark that we've begun grabbing segement data.
1387 if(fsm
.mSegmentDataSkippedLine
)
1390 ** A blank line means end of the segment data.
1395 ** We're adding a new segment.
1396 ** Make sure we have room for it.
1398 if(inModule
->mSegmentCapacity
== inModule
->mSegmentCount
)
1402 moved
= realloc(inModule
->mSegments
, sizeof(MSMap_Segment
) * (inModule
->mSegmentCapacity
+ MSMAP_SEGMENT_GROWBY
));
1405 inModule
->mSegmentCapacity
+= MSMAP_SEGMENT_GROWBY
;
1406 inModule
->mSegments
= (MSMap_Segment
*)moved
;
1411 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to grow segments.");
1415 if(0 == retval
&& inModule
->mSegmentCapacity
> inModule
->mSegmentCount
)
1417 MSMap_Segment
* theSegment
= NULL
;
1419 char classBuf
[0x10];
1423 index
= inModule
->mSegmentCount
;
1424 inModule
->mSegmentCount
++;
1425 theSegment
= (inModule
->mSegments
+ index
);
1427 memset(theSegment
, 0, sizeof(MSMap_Segment
));
1429 scanRes
= sscanf(current
, "%x:%x %xH %s %s", (unsigned*)&(theSegment
->mPrefix
), (unsigned*)&(theSegment
->mOffset
), (unsigned*)&(theSegment
->mLength
), nameBuf
, classBuf
);
1432 if('.' == nameBuf
[0])
1434 theSegment
->mSegment
= strdup(&nameBuf
[1]);
1438 theSegment
->mSegment
= strdup(nameBuf
);
1441 if(NULL
!= theSegment
->mSegment
)
1443 if(0 == strcmp("DATA", classBuf
))
1445 theSegment
->mClass
= DATA
;
1447 else if(0 == strcmp("CODE", classBuf
))
1449 theSegment
->mClass
= CODE
;
1454 ERROR_REPORT(retval
, classBuf
, "Unrecognized segment class.");
1460 ERROR_REPORT(retval
, nameBuf
, "Unable to copy segment name.");
1466 ERROR_REPORT(retval
, inModule
->mModule
, "Unable to scan segments.");
1472 fsm
.mHasSegmentData
= __LINE__
;
1477 fsm
.mSegmentDataSkippedLine
= __LINE__
;
1486 ** The PLA has a particular format.
1488 scanRes
= sscanf(current
, "Preferred load address is %x", (unsigned*)&(inModule
->mPreferredLoadAddress
));
1491 fsm
.mHasPreferredLoadAddress
= __LINE__
;
1497 ERROR_REPORT(retval
, current
, "Unable to obtain preferred load address.");
1506 ** The timestamp has a particular format.
1508 scanRes
= sscanf(current
, "Timestamp is %x", (unsigned*)&(inModule
->mTimestamp
));
1511 fsm
.mHasTimestamp
= __LINE__
;
1517 ERROR_REPORT(retval
, current
, "Unable to obtain timestamp.");
1524 ** The module is on a line by itself.
1526 inModule
->mModule
= strdup(current
);
1527 if(NULL
!= inModule
->mModule
)
1529 fsm
.mHasModule
= __LINE__
;
1532 if(0 != inOptions
->mMatchModuleCount
)
1534 unsigned matchLoop
= 0;
1537 ** If this module name doesn't match, then bail.
1538 ** Compare in a case sensitive manner, exact match only.
1540 for(matchLoop
= 0; matchLoop
< inOptions
->mMatchModuleCount
; matchLoop
++)
1542 if(0 == strcmp(inModule
->mModule
, inOptions
->mMatchModules
[matchLoop
]))
1548 if(matchLoop
== inOptions
->mMatchModuleCount
)
1551 ** A match did not occur, bail out of read loop.
1552 ** No error, however.
1561 ERROR_REPORT(retval
, current
, "Unable to obtain module.");
1566 if(0 == retval
&& 0 != ferror(inOptions
->mInput
))
1569 ERROR_REPORT(retval
, inOptions
->mInputName
, "Unable to read file.");
1576 static int qsortRVABase(const void* in1
, const void* in2
)
1578 ** qsort callback to sort the symbols by their RVABase.
1581 MSMap_Symbol
* sym1
= (MSMap_Symbol
*)in1
;
1582 MSMap_Symbol
* sym2
= (MSMap_Symbol
*)in2
;
1585 if(sym1
->mRVABase
< sym2
->mRVABase
)
1589 else if(sym1
->mRVABase
> sym2
->mRVABase
)
1598 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
)
1600 ** Output a line of map information separated by tabs.
1601 ** Some items (const char*), if not present, will receive a default value.
1607 ** No need to output on no size.
1608 ** This can happen with zero sized segments,
1609 ** or an imported symbol which has multiple names (one will count).
1613 char objectBuf
[0x100];
1614 const char* symScope
= NULL
;
1615 const char* segClass
= NULL
;
1616 const char* undefined
= "UNDEF";
1619 ** Fill in unspecified values.
1621 if(NULL
== inObject
)
1623 sprintf(objectBuf
, "%s:%s:%s", undefined
, inModule
, inSegment
);
1624 inObject
= objectBuf
;
1626 if(NULL
== inSymbol
)
1628 inSymbol
= inObject
;
1632 ** Convert some enumerations to text.
1644 ERROR_REPORT(retval
, "", "Unable to determine class for output.");
1651 symScope
= "PUBLIC";
1654 symScope
= "STATIC";
1657 symScope
= undefined
;
1661 ERROR_REPORT(retval
, "", "Unable to determine scope for symbol.");
1669 printRes
= fprintf(inOptions
->mOutput
,
1670 "%.8X\t%s\t%s\t%s\t%s\t%s\t%s\n",
1683 ERROR_REPORT(retval
, inOptions
->mOutputName
, "Unable to output tsv data.");
1692 void cleanModule(MSMap_Module
* inModule
)
1696 for(loop
= 0; loop
< inModule
->mSymbolCount
; loop
++)
1698 CLEANUP(inModule
->mSymbols
[loop
].mObject
);
1699 CLEANUP(inModule
->mSymbols
[loop
].mSymbol
);
1701 CLEANUP(inModule
->mSymbols
);
1703 for(loop
= 0; loop
< inModule
->mSegmentCount
; loop
++)
1705 CLEANUP(inModule
->mSegments
[loop
].mSegment
);
1707 CLEANUP(inModule
->mSegments
);
1709 CLEANUP(inModule
->mModule
);
1711 memset(inModule
, 0, sizeof(MSMap_Module
));
1715 int map2tsv(Options
* inOptions
)
1718 ** Output tab separated value data.
1722 MSMap_Module module
;
1724 memset(&module
, 0, sizeof(module
));
1727 ** Read in the map file.
1729 retval
= readmap(inOptions
, &module
);
1732 unsigned symLoop
= 0;
1733 MSMap_Symbol
* symbol
= NULL
;
1734 unsigned secLoop
= 0;
1735 MSMap_Segment
* section
= NULL
;
1737 unsigned dbSize
= 0;
1738 unsigned offsetSize
= 0;
1739 unsigned endOffset
= 0;
1742 ** Quick sort the symbols via RVABase.
1744 qsort(module
.mSymbols
, module
.mSymbolCount
, sizeof(MSMap_Symbol
), qsortRVABase
);
1747 ** Go through all the symbols (in order by sort).
1748 ** Output their sizes.
1750 for(symLoop
= 0; 0 == retval
&& symLoop
< module
.mSymbolCount
; symLoop
++)
1752 symbol
= &module
.mSymbols
[symLoop
];
1753 section
= getSymbolSection(&module
, symbol
);
1758 ** Use the symbol DB size if available.
1760 dbSize
= symbol
->mSymDBSize
;
1763 ** Guess using offsets.
1764 ** Is there a next symbol available? If so, its start offset is the end of this symbol.
1765 ** Otherwise, our section offset + length is the end of this symbol.
1767 ** The trick is, the DB size can not go beyond the offset size, for sanity.
1771 ** Try next symbol, but only if in same section.
1772 ** If still not, use the end of the segment.
1773 ** This implies we were the last symbol in the segment.
1775 if((symLoop
+ 1) < module
.mSymbolCount
)
1777 MSMap_Symbol
* nextSymbol
= NULL
;
1778 MSMap_Segment
* nextSection
= NULL
;
1780 nextSymbol
= &module
.mSymbols
[symLoop
+ 1];
1781 nextSection
= getSymbolSection(&module
, nextSymbol
);
1783 if(section
== nextSection
)
1785 endOffset
= nextSymbol
->mOffset
;
1789 endOffset
= section
->mOffset
+ section
->mLength
;
1794 endOffset
= section
->mOffset
+ section
->mLength
;
1798 ** Can now guess at size.
1800 offsetSize
= endOffset
- symbol
->mOffset
;
1803 ** Now, determine which size to use.
1804 ** This is really a sanity check as well.
1809 if(dbSize
< offsetSize
)
1816 ** Output the symbol with the size.
1818 retval
= tsvout(inOptions
,
1829 ** Make sure we mark this amount of space as used in the section.
1831 section
->mUsed
+= size
;
1835 ** Go through the sections, and those whose length is longer than the
1836 ** amount of space used, output dummy filler values.
1838 for(secLoop
= 0; 0 == retval
&& secLoop
< module
.mSegmentCount
; secLoop
++)
1840 section
= &module
.mSegments
[secLoop
];
1842 if(section
&& section
->mUsed
< section
->mLength
)
1844 retval
= tsvout(inOptions
,
1845 section
->mLength
- section
->mUsed
,
1860 cleanModule(&module
);
1866 int initOptions(Options
* outOptions
, int inArgc
, char** inArgv
)
1868 ** returns int 0 if successful.
1875 const int switchCount
= sizeof(gSwitches
) / sizeof(gSwitches
[0]);
1876 Switch
* current
= NULL
;
1879 ** Set any defaults.
1881 memset(outOptions
, 0, sizeof(Options
));
1882 outOptions
->mProgramName
= inArgv
[0];
1883 outOptions
->mInput
= stdin
;
1884 outOptions
->mInputName
= strdup("stdin");
1885 outOptions
->mOutput
= stdout
;
1886 outOptions
->mOutputName
= strdup("stdout");
1888 if(NULL
== outOptions
->mOutputName
|| NULL
== outOptions
->mInputName
)
1891 ERROR_REPORT(retval
, "stdin/stdout", "Unable to strdup.");
1895 ** Go through and attempt to do the right thing.
1897 for(loop
= 1; loop
< inArgc
&& 0 == retval
; loop
++)
1902 for(switchLoop
= 0; switchLoop
< switchCount
&& 0 == retval
; switchLoop
++)
1904 if(0 == strcmp(gSwitches
[switchLoop
]->mLongName
, inArgv
[loop
]))
1908 else if(0 == strcmp(gSwitches
[switchLoop
]->mShortName
, inArgv
[loop
]))
1915 if(gSwitches
[switchLoop
]->mHasValue
)
1918 ** Attempt to absorb next option to fullfill value.
1920 if(loop
+ 1 < inArgc
)
1924 current
= gSwitches
[switchLoop
];
1925 current
->mValue
= inArgv
[loop
];
1930 current
= gSwitches
[switchLoop
];
1939 outOptions
->mHelp
= __LINE__
;
1941 ERROR_REPORT(retval
, inArgv
[loop
], "Unknown command line switch.");
1943 else if(NULL
== current
)
1945 outOptions
->mHelp
= __LINE__
;
1947 ERROR_REPORT(retval
, inArgv
[loop
], "Command line switch requires a value.");
1952 ** Do something based on address/swtich.
1954 if(current
== &gInputSwitch
)
1956 CLEANUP(outOptions
->mInputName
);
1957 if(NULL
!= outOptions
->mInput
&& stdin
!= outOptions
->mInput
)
1959 fclose(outOptions
->mInput
);
1960 outOptions
->mInput
= NULL
;
1963 outOptions
->mInput
= fopen(current
->mValue
, "r");
1964 if(NULL
== outOptions
->mInput
)
1967 ERROR_REPORT(retval
, current
->mValue
, "Unable to open input file.");
1971 outOptions
->mInputName
= strdup(current
->mValue
);
1972 if(NULL
== outOptions
->mInputName
)
1975 ERROR_REPORT(retval
, current
->mValue
, "Unable to strdup.");
1979 else if(current
== &gOutputSwitch
)
1981 CLEANUP(outOptions
->mOutputName
);
1982 if(NULL
!= outOptions
->mOutput
&& stdout
!= outOptions
->mOutput
)
1984 fclose(outOptions
->mOutput
);
1985 outOptions
->mOutput
= NULL
;
1988 outOptions
->mOutput
= fopen(current
->mValue
, "a");
1989 if(NULL
== outOptions
->mOutput
)
1992 ERROR_REPORT(retval
, current
->mValue
, "Unable to open output file.");
1996 outOptions
->mOutputName
= strdup(current
->mValue
);
1997 if(NULL
== outOptions
->mOutputName
)
2000 ERROR_REPORT(retval
, current
->mValue
, "Unable to strdup.");
2004 else if(current
== &gHelpSwitch
)
2006 outOptions
->mHelp
= __LINE__
;
2008 else if(current
== &gMatchModuleSwitch
)
2013 ** Add the value to the list of allowed module names.
2015 moved
= realloc(outOptions
->mMatchModules
, sizeof(char*) * (outOptions
->mMatchModuleCount
+ 1));
2018 outOptions
->mMatchModules
= (char**)moved
;
2019 outOptions
->mMatchModules
[outOptions
->mMatchModuleCount
] = strdup(current
->mValue
);
2020 if(NULL
!= outOptions
->mMatchModules
[outOptions
->mMatchModuleCount
])
2022 outOptions
->mMatchModuleCount
++;
2027 ERROR_REPORT(retval
, current
->mValue
, "Unable to duplicate string.");
2033 ERROR_REPORT(retval
, current
->mValue
, "Unable to allocate space for string.");
2036 else if(current
== &gSymDBSwitch
)
2038 CLEANUP(outOptions
->mSymDBName
);
2039 outOptions
->mSymDBName
= strdup(current
->mValue
);
2040 if(NULL
== outOptions
->mSymDBName
)
2043 ERROR_REPORT(retval
, current
->mValue
, "Unable to duplicate symbol db name.");
2046 else if(current
== &gBatchModeSwitch
)
2048 outOptions
->mBatchMode
= __LINE__
;
2053 ERROR_REPORT(retval
, current
->mLongName
, "No handler for command line switch.");
2062 void cleanOptions(Options
* inOptions
)
2064 ** Clean up any open handles, et. al.
2067 CLEANUP(inOptions
->mInputName
);
2068 if(NULL
!= inOptions
->mInput
&& stdin
!= inOptions
->mInput
)
2070 fclose(inOptions
->mInput
);
2072 CLEANUP(inOptions
->mOutputName
);
2073 if(NULL
!= inOptions
->mOutput
&& stdout
!= inOptions
->mOutput
)
2075 fclose(inOptions
->mOutput
);
2077 while(0 != inOptions
->mMatchModuleCount
)
2079 inOptions
->mMatchModuleCount
--;
2080 CLEANUP(inOptions
->mMatchModules
[inOptions
->mMatchModuleCount
]);
2082 CLEANUP(inOptions
->mMatchModules
);
2084 cleanSymDB(&inOptions
->mSymDB
);
2086 memset(inOptions
, 0, sizeof(Options
));
2090 void showHelp(Options
* inOptions
)
2092 ** Show some simple help text on usage.
2096 const int switchCount
= sizeof(gSwitches
) / sizeof(gSwitches
[0]);
2097 const char* valueText
= NULL
;
2099 printf("usage:\t%s [arguments]\n", inOptions
->mProgramName
);
2101 printf("arguments:\n");
2103 for(loop
= 0; loop
< switchCount
; loop
++)
2105 if(gSwitches
[loop
]->mHasValue
)
2107 valueText
= " <value>";
2114 printf("\t%s%s\n", gSwitches
[loop
]->mLongName
, valueText
);
2115 printf("\t %s%s", gSwitches
[loop
]->mShortName
, valueText
);
2116 printf(DESC_NEWLINE
"%s\n\n", gSwitches
[loop
]->mDescription
);
2119 printf("This tool normalizes MS linker .map files for use by other tools.\n");
2123 int batchMode(Options
* inOptions
)
2125 ** Batch mode means that the input file is actually a list of map files.
2126 ** We simply swap out our input file names while we do this.
2130 char lineBuf
[0x400];
2131 FILE* realInput
= NULL
;
2132 char* realInputName
= NULL
;
2133 FILE* mapFile
= NULL
;
2136 realInput
= inOptions
->mInput
;
2137 realInputName
= inOptions
->mInputName
;
2139 while(0 == retval
&& NULL
!= fgets(lineBuf
, sizeof(lineBuf
), realInput
))
2144 ** Skip/allow blank lines.
2146 if('\0' == lineBuf
[0])
2152 ** Override what we believe to be the input for this line.
2154 inOptions
->mInputName
= lineBuf
;
2155 inOptions
->mInput
= fopen(lineBuf
, "r");
2156 if(NULL
!= inOptions
->mInput
)
2163 mapRes
= map2tsv(inOptions
);
2166 ** We report the first error that we encounter, but we continue.
2167 ** This is batch mode after all.
2175 ** Close the input file.
2177 fclose(inOptions
->mInput
);
2182 ERROR_REPORT(retval
, lineBuf
, "Unable to open map file.");
2187 if(0 == retval
&& 0 != ferror(realInput
))
2190 ERROR_REPORT(retval
, realInputName
, "Unable to read file.");
2194 ** Restore what we've swapped.
2196 inOptions
->mInput
= realInput
;
2197 inOptions
->mInputName
= realInputName
;
2200 ** Report first map file error if there were no other operational
2212 int main(int inArgc
, char** inArgv
)
2217 retval
= initOptions(&options
, inArgc
, inArgv
);
2222 else if(0 == retval
)
2224 if(options
.mBatchMode
)
2226 retval
= batchMode(&options
);
2230 retval
= map2tsv(&options
);
2234 cleanOptions(&options
);