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 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 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 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 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 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 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 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