Home | History | Annotate | Download | only in codesighs
      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