• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  *
3  * ***** BEGIN LICENSE BLOCK *****
4  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5  *
6  * The contents of this file are subject to the Mozilla Public License Version
7  * 1.1 (the "License"); you may not use this file except in compliance with
8  * the License. You may obtain a copy of the License at
9  * http://www.mozilla.org/MPL/
10  *
11  * Software distributed under the License is distributed on an "AS IS" basis,
12  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13  * for the specific language governing rights and limitations under the
14  * License.
15  *
16  * The Original Code is nm2tsv.c code, released
17  * Oct 10, 2002.
18  *
19  * The Initial Developer of the Original Code is
20  * Netscape Communications Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 2002
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *   Garrett Arch Blythe, 10-October-2002
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <time.h>
45 #include <ctype.h>
46 
47 
48 #define ERROR_REPORT(num, val, msg)   fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg));
49 #define CLEANUP(ptr)    do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0)
50 
51 
52 typedef struct __struct_Options
53 /*
54 **  Options to control how we perform.
55 **
56 **  mProgramName    Used in help text.
57 **  mInput          File to read for input.
58 **                  Default is stdin.
59 **  mInputName      Name of the file.
60 **  mOutput         Output file, append.
61 **                  Default is stdout.
62 **  mOutputName     Name of the file.
63 **  mHelp           Whether or not help should be shown.
64 */
65 {
66     const char* mProgramName;
67     FILE* mInput;
68     char* mInputName;
69     FILE* mOutput;
70     char* mOutputName;
71     int mHelp;
72 }
73 Options;
74 
75 
76 typedef struct __struct_Switch
77 /*
78 **  Command line options.
79 */
80 {
81     const char* mLongName;
82     const char* mShortName;
83     int mHasValue;
84     const char* mValue;
85     const char* mDescription;
86 }
87 Switch;
88 
89 #define DESC_NEWLINE "\n\t\t"
90 
91 static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."};
92 static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."};
93 static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."};
94 
95 static Switch* gSwitches[] = {
96         &gInputSwitch,
97         &gOutputSwitch,
98         &gHelpSwitch
99 };
100 
101 
scanWhite(char * inScan)102 char* scanWhite(char* inScan)
103 /*
104 **  Scan for whitespace.
105 */
106 {
107     char* retval = inScan;
108 
109     while('\0' != *retval && 0 == isspace(*retval))
110     {
111         retval++;
112     }
113 
114     return retval;
115 }
116 
117 
trimWhite(char * inString)118 void trimWhite(char* inString)
119 /*
120 **  Remove any whitespace from the end of the string.
121 */
122 {
123     int len = strlen(inString);
124 
125     while(len)
126     {
127         len--;
128 
129         if(isspace(*(inString + len)))
130         {
131             *(inString + len) = '\0';
132         }
133         else
134         {
135             break;
136         }
137     }
138 }
139 
140 
nm2tsv(Options * inOptions)141 int nm2tsv(Options* inOptions)
142 /*
143 **  Read all input.
144 **  Output tab separated value data.
145 **
146 **  We expect our data to be in a particular format.
147 **  nm --format=bsd --size-sort --print-file-name --demangle
148 */
149 {
150     int retval = 0;
151     char lineBuffer[4096];  /* yes, the are some very large symbols */
152     char* module = NULL;
153     char* size = NULL;
154     char* type = NULL;
155     char* symbol = NULL;
156 
157     /*
158     **  Read in the nm file.
159     */
160     while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput))
161     {
162         trimWhite(lineBuffer);
163 
164         /*
165         ** Find the various pieces of information we'll be looking for.
166         */
167         size = strchr(lineBuffer, ':');
168         if(NULL != size)
169         {
170             *size = '\0';
171             size++;
172 
173             module = strrchr(lineBuffer, '/');
174             if(NULL == module)
175             {
176                 module = lineBuffer;
177             }
178             else
179             {
180                 *module = '\0';
181                 module++;
182             }
183 
184             type = scanWhite(size);
185             *type = '\0';
186             type++;
187 
188             symbol = type + 1;
189             *symbol = '\0';
190             symbol++;
191 
192             /*
193             **  Skip certain types.
194             */
195             switch(*type)
196             {
197                 case '-':
198                     continue;
199                     break;
200                 default:
201                     break;
202             }
203 
204             /*
205             **  Simply output the data with a little more interpretation.
206             **  First is size.
207             */
208             fprintf(inOptions->mOutput, "%s\t", size);
209 
210             /*
211             **  Type, CODE or DATA
212             */
213             switch(toupper(*type))
214             {
215                 case 'T': /* text (code) */
216                 case 'W': /* weak symbol ??? */
217                     fprintf(inOptions->mOutput, "CODE\t");
218                     break;
219                 default:
220                     fprintf(inOptions->mOutput, "DATA\t");
221                     break;
222             }
223 
224             /*
225             **  Scope, PUBLIC, STATIC, or UNDEF
226             */
227             if(islower(*type))
228             {
229                 fprintf(inOptions->mOutput, "STATIC\t");
230             }
231             else
232             {
233                 switch(*type)
234                 {
235                     case '?':
236                         fprintf(inOptions->mOutput, "UNDEF\t");
237                         break;
238                     default:
239                         fprintf(inOptions->mOutput, "PUBLIC\t");
240                         break;
241                 }
242             }
243 
244             /*
245             **  Module name, segment.
246             */
247             fprintf(inOptions->mOutput, "%s\t", module);
248             fprintf(inOptions->mOutput, "%c\t", toupper(*type));
249 
250             /*
251             **  Origin
252             */
253             fprintf(inOptions->mOutput, "UNDEF:%s:%c\t", module, toupper(*type));
254 
255             /*
256             **  Symbol is last.
257             */
258             fprintf(inOptions->mOutput, "%s\n", symbol);
259         }
260         else
261         {
262             retval = __LINE__;
263             ERROR_REPORT(retval, lineBuffer, "Malformed input line.");
264         }
265     }
266 
267     if(0 == retval && 0 != ferror(inOptions->mInput))
268     {
269         retval = __LINE__;
270         ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file.");
271     }
272 
273     return retval;
274 }
275 
276 
initOptions(Options * outOptions,int inArgc,char ** inArgv)277 int initOptions(Options* outOptions, int inArgc, char** inArgv)
278 /*
279 **  returns int     0 if successful.
280 */
281 {
282     int retval = 0;
283     int loop = 0;
284     int switchLoop = 0;
285     int match = 0;
286     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
287     Switch* current = NULL;
288 
289     /*
290     **  Set any defaults.
291     */
292     memset(outOptions, 0, sizeof(Options));
293     outOptions->mProgramName = inArgv[0];
294     outOptions->mInput = stdin;
295     outOptions->mInputName = strdup("stdin");
296     outOptions->mOutput = stdout;
297     outOptions->mOutputName = strdup("stdout");
298 
299     if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName)
300     {
301         retval = __LINE__;
302         ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup.");
303     }
304 
305     /*
306     **  Go through and attempt to do the right thing.
307     */
308     for(loop = 1; loop < inArgc && 0 == retval; loop++)
309     {
310         match = 0;
311         current = NULL;
312 
313         for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++)
314         {
315             if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop]))
316             {
317                 match = __LINE__;
318             }
319             else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop]))
320             {
321                 match = __LINE__;
322             }
323 
324             if(match)
325             {
326                 if(gSwitches[switchLoop]->mHasValue)
327                 {
328                     /*
329                     **  Attempt to absorb next option to fullfill value.
330                     */
331                     if(loop + 1 < inArgc)
332                     {
333                         loop++;
334 
335                         current = gSwitches[switchLoop];
336                         current->mValue = inArgv[loop];
337                     }
338                 }
339                 else
340                 {
341                     current = gSwitches[switchLoop];
342                 }
343 
344                 break;
345             }
346         }
347 
348         if(0 == match)
349         {
350             outOptions->mHelp = __LINE__;
351             retval = __LINE__;
352             ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch.");
353         }
354         else if(NULL == current)
355         {
356             outOptions->mHelp = __LINE__;
357             retval = __LINE__;
358             ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value.");
359         }
360         else
361         {
362             /*
363             ** Do something based on address/swtich.
364             */
365             if(current == &gInputSwitch)
366             {
367                 CLEANUP(outOptions->mInputName);
368                 if(NULL != outOptions->mInput && stdin != outOptions->mInput)
369                 {
370                     fclose(outOptions->mInput);
371                     outOptions->mInput = NULL;
372                 }
373 
374                 outOptions->mInput = fopen(current->mValue, "r");
375                 if(NULL == outOptions->mInput)
376                 {
377                     retval = __LINE__;
378                     ERROR_REPORT(retval, current->mValue, "Unable to open input file.");
379                 }
380                 else
381                 {
382                     outOptions->mInputName = strdup(current->mValue);
383                     if(NULL == outOptions->mInputName)
384                     {
385                         retval = __LINE__;
386                         ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
387                     }
388                 }
389             }
390             else if(current == &gOutputSwitch)
391             {
392                 CLEANUP(outOptions->mOutputName);
393                 if(NULL != outOptions->mOutput && stdout != outOptions->mOutput)
394                 {
395                     fclose(outOptions->mOutput);
396                     outOptions->mOutput = NULL;
397                 }
398 
399                 outOptions->mOutput = fopen(current->mValue, "a");
400                 if(NULL == outOptions->mOutput)
401                 {
402                     retval = __LINE__;
403                     ERROR_REPORT(retval, current->mValue, "Unable to open output file.");
404                 }
405                 else
406                 {
407                     outOptions->mOutputName = strdup(current->mValue);
408                     if(NULL == outOptions->mOutputName)
409                     {
410                         retval = __LINE__;
411                         ERROR_REPORT(retval, current->mValue, "Unable to strdup.");
412                     }
413                 }
414             }
415             else if(current == &gHelpSwitch)
416             {
417                 outOptions->mHelp = __LINE__;
418             }
419             else
420             {
421                 retval = __LINE__;
422                 ERROR_REPORT(retval, current->mLongName, "No handler for command line switch.");
423             }
424         }
425     }
426 
427     return retval;
428 }
429 
430 
cleanOptions(Options * inOptions)431 void cleanOptions(Options* inOptions)
432 /*
433 **  Clean up any open handles.
434 */
435 {
436     CLEANUP(inOptions->mInputName);
437     if(NULL != inOptions->mInput && stdin != inOptions->mInput)
438     {
439         fclose(inOptions->mInput);
440     }
441     CLEANUP(inOptions->mOutputName);
442     if(NULL != inOptions->mOutput && stdout != inOptions->mOutput)
443     {
444         fclose(inOptions->mOutput);
445     }
446 
447     memset(inOptions, 0, sizeof(Options));
448 }
449 
450 
showHelp(Options * inOptions)451 void showHelp(Options* inOptions)
452 /*
453 **  Show some simple help text on usage.
454 */
455 {
456     int loop = 0;
457     const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]);
458     const char* valueText = NULL;
459 
460     printf("usage:\t%s [arguments]\n", inOptions->mProgramName);
461     printf("\n");
462     printf("arguments:\n");
463 
464     for(loop = 0; loop < switchCount; loop++)
465     {
466         if(gSwitches[loop]->mHasValue)
467         {
468             valueText = " <value>";
469         }
470         else
471         {
472             valueText = "";
473         }
474 
475         printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText);
476         printf("\t %s%s", gSwitches[loop]->mShortName, valueText);
477         printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription);
478     }
479 
480     printf("This tool normalizes nm output for use by other tools.\n");
481     printf("GNU nm is assumed for symbol type determination.\n");
482     printf("i.e. Use this tool to parse the output of:\n");
483     printf("\t/usr/bin/nm --format=bsd --size-sort --print-file-name --demangle <exefile>\n");
484 }
485 
486 
main(int inArgc,char ** inArgv)487 int main(int inArgc, char** inArgv)
488 {
489     int retval = 0;
490     Options options;
491 
492     retval = initOptions(&options, inArgc, inArgv);
493     if(options.mHelp)
494     {
495         showHelp(&options);
496     }
497     else if(0 == retval)
498     {
499         retval = nm2tsv(&options);
500     }
501 
502     cleanOptions(&options);
503     return retval;
504 }
505 
506