/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is nm2tsv.c code, released * Oct 10, 2002. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 2002 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Garrett Arch Blythe, 10-October-2002 * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <ctype.h> #define ERROR_REPORT(num, val, msg) fprintf(stderr, "error(%d):\t\"%s\"\t%s\n", (num), (val), (msg)); #define CLEANUP(ptr) do { if(NULL != ptr) { free(ptr); ptr = NULL; } } while(0) typedef struct __struct_Options /* ** Options to control how we perform. ** ** mProgramName Used in help text. ** mInput File to read for input. ** Default is stdin. ** mInputName Name of the file. ** mOutput Output file, append. ** Default is stdout. ** mOutputName Name of the file. ** mHelp Whether or not help should be shown. */ { const char* mProgramName; FILE* mInput; char* mInputName; FILE* mOutput; char* mOutputName; int mHelp; } Options; typedef struct __struct_Switch /* ** Command line options. */ { const char* mLongName; const char* mShortName; int mHasValue; const char* mValue; const char* mDescription; } Switch; #define DESC_NEWLINE "\n\t\t" static Switch gInputSwitch = {"--input", "-i", 1, NULL, "Specify input file." DESC_NEWLINE "stdin is default."}; static Switch gOutputSwitch = {"--output", "-o", 1, NULL, "Specify output file." DESC_NEWLINE "Appends if file exists." DESC_NEWLINE "stdout is default."}; static Switch gHelpSwitch = {"--help", "-h", 0, NULL, "Information on usage."}; static Switch* gSwitches[] = { &gInputSwitch, &gOutputSwitch, &gHelpSwitch }; char* scanWhite(char* inScan) /* ** Scan for whitespace. */ { char* retval = inScan; while('\0' != *retval && 0 == isspace(*retval)) { retval++; } return retval; } void trimWhite(char* inString) /* ** Remove any whitespace from the end of the string. */ { int len = strlen(inString); while(len) { len--; if(isspace(*(inString + len))) { *(inString + len) = '\0'; } else { break; } } } int nm2tsv(Options* inOptions) /* ** Read all input. ** Output tab separated value data. ** ** We expect our data to be in a particular format. ** nm --format=bsd --size-sort --print-file-name --demangle */ { int retval = 0; char lineBuffer[4096]; /* yes, the are some very large symbols */ char* module = NULL; char* size = NULL; char* type = NULL; char* symbol = NULL; /* ** Read in the nm file. */ while(0 == retval && NULL != fgets(lineBuffer, sizeof(lineBuffer), inOptions->mInput)) { trimWhite(lineBuffer); /* ** Find the various pieces of information we'll be looking for. */ size = strchr(lineBuffer, ':'); if(NULL != size) { *size = '\0'; size++; module = strrchr(lineBuffer, '/'); if(NULL == module) { module = lineBuffer; } else { *module = '\0'; module++; } type = scanWhite(size); *type = '\0'; type++; symbol = type + 1; *symbol = '\0'; symbol++; /* ** Skip certain types. */ switch(*type) { case '-': continue; break; default: break; } /* ** Simply output the data with a little more interpretation. ** First is size. */ fprintf(inOptions->mOutput, "%s\t", size); /* ** Type, CODE or DATA */ switch(toupper(*type)) { case 'T': /* text (code) */ case 'W': /* weak symbol ??? */ fprintf(inOptions->mOutput, "CODE\t"); break; default: fprintf(inOptions->mOutput, "DATA\t"); break; } /* ** Scope, PUBLIC, STATIC, or UNDEF */ if(islower(*type)) { fprintf(inOptions->mOutput, "STATIC\t"); } else { switch(*type) { case '?': fprintf(inOptions->mOutput, "UNDEF\t"); break; default: fprintf(inOptions->mOutput, "PUBLIC\t"); break; } } /* ** Module name, segment. */ fprintf(inOptions->mOutput, "%s\t", module); fprintf(inOptions->mOutput, "%c\t", toupper(*type)); /* ** Origin */ fprintf(inOptions->mOutput, "UNDEF:%s:%c\t", module, toupper(*type)); /* ** Symbol is last. */ fprintf(inOptions->mOutput, "%s\n", symbol); } else { retval = __LINE__; ERROR_REPORT(retval, lineBuffer, "Malformed input line."); } } if(0 == retval && 0 != ferror(inOptions->mInput)) { retval = __LINE__; ERROR_REPORT(retval, inOptions->mInputName, "Unable to read file."); } return retval; } int initOptions(Options* outOptions, int inArgc, char** inArgv) /* ** returns int 0 if successful. */ { int retval = 0; int loop = 0; int switchLoop = 0; int match = 0; const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); Switch* current = NULL; /* ** Set any defaults. */ memset(outOptions, 0, sizeof(Options)); outOptions->mProgramName = inArgv[0]; outOptions->mInput = stdin; outOptions->mInputName = strdup("stdin"); outOptions->mOutput = stdout; outOptions->mOutputName = strdup("stdout"); if(NULL == outOptions->mOutputName || NULL == outOptions->mInputName) { retval = __LINE__; ERROR_REPORT(retval, "stdin/stdout", "Unable to strdup."); } /* ** Go through and attempt to do the right thing. */ for(loop = 1; loop < inArgc && 0 == retval; loop++) { match = 0; current = NULL; for(switchLoop = 0; switchLoop < switchCount && 0 == retval; switchLoop++) { if(0 == strcmp(gSwitches[switchLoop]->mLongName, inArgv[loop])) { match = __LINE__; } else if(0 == strcmp(gSwitches[switchLoop]->mShortName, inArgv[loop])) { match = __LINE__; } if(match) { if(gSwitches[switchLoop]->mHasValue) { /* ** Attempt to absorb next option to fullfill value. */ if(loop + 1 < inArgc) { loop++; current = gSwitches[switchLoop]; current->mValue = inArgv[loop]; } } else { current = gSwitches[switchLoop]; } break; } } if(0 == match) { outOptions->mHelp = __LINE__; retval = __LINE__; ERROR_REPORT(retval, inArgv[loop], "Unknown command line switch."); } else if(NULL == current) { outOptions->mHelp = __LINE__; retval = __LINE__; ERROR_REPORT(retval, inArgv[loop], "Command line switch requires a value."); } else { /* ** Do something based on address/swtich. */ if(current == &gInputSwitch) { CLEANUP(outOptions->mInputName); if(NULL != outOptions->mInput && stdin != outOptions->mInput) { fclose(outOptions->mInput); outOptions->mInput = NULL; } outOptions->mInput = fopen(current->mValue, "r"); if(NULL == outOptions->mInput) { retval = __LINE__; ERROR_REPORT(retval, current->mValue, "Unable to open input file."); } else { outOptions->mInputName = strdup(current->mValue); if(NULL == outOptions->mInputName) { retval = __LINE__; ERROR_REPORT(retval, current->mValue, "Unable to strdup."); } } } else if(current == &gOutputSwitch) { CLEANUP(outOptions->mOutputName); if(NULL != outOptions->mOutput && stdout != outOptions->mOutput) { fclose(outOptions->mOutput); outOptions->mOutput = NULL; } outOptions->mOutput = fopen(current->mValue, "a"); if(NULL == outOptions->mOutput) { retval = __LINE__; ERROR_REPORT(retval, current->mValue, "Unable to open output file."); } else { outOptions->mOutputName = strdup(current->mValue); if(NULL == outOptions->mOutputName) { retval = __LINE__; ERROR_REPORT(retval, current->mValue, "Unable to strdup."); } } } else if(current == &gHelpSwitch) { outOptions->mHelp = __LINE__; } else { retval = __LINE__; ERROR_REPORT(retval, current->mLongName, "No handler for command line switch."); } } } return retval; } void cleanOptions(Options* inOptions) /* ** Clean up any open handles. */ { CLEANUP(inOptions->mInputName); if(NULL != inOptions->mInput && stdin != inOptions->mInput) { fclose(inOptions->mInput); } CLEANUP(inOptions->mOutputName); if(NULL != inOptions->mOutput && stdout != inOptions->mOutput) { fclose(inOptions->mOutput); } memset(inOptions, 0, sizeof(Options)); } void showHelp(Options* inOptions) /* ** Show some simple help text on usage. */ { int loop = 0; const int switchCount = sizeof(gSwitches) / sizeof(gSwitches[0]); const char* valueText = NULL; printf("usage:\t%s [arguments]\n", inOptions->mProgramName); printf("\n"); printf("arguments:\n"); for(loop = 0; loop < switchCount; loop++) { if(gSwitches[loop]->mHasValue) { valueText = " <value>"; } else { valueText = ""; } printf("\t%s%s\n", gSwitches[loop]->mLongName, valueText); printf("\t %s%s", gSwitches[loop]->mShortName, valueText); printf(DESC_NEWLINE "%s\n\n", gSwitches[loop]->mDescription); } printf("This tool normalizes nm output for use by other tools.\n"); printf("GNU nm is assumed for symbol type determination.\n"); printf("i.e. Use this tool to parse the output of:\n"); printf("\t/usr/bin/nm --format=bsd --size-sort --print-file-name --demangle <exefile>\n"); } int main(int inArgc, char** inArgv) { int retval = 0; Options options; retval = initOptions(&options, inArgc, inArgv); if(options.mHelp) { showHelp(&options); } else if(0 == retval) { retval = nm2tsv(&options); } cleanOptions(&options); return retval; }