#!/usr/bin/env perl
#
# Copyright (c) 2010-2014 Cisco Systems, Inc.  All rights reserved.
# Copyright (c) 2015      Intel, Inc. All rights reserved.
# $COPYRIGHT$

use strict;
use Getopt::Long;

# globals
my $myfile;
my $mylib;
my $myprefix;
my $mysuffix;
my $mycapprefix;

# Set to true if the script should merely check for symbols in
# the library that are not in the provided output file - useful
# for determining if something has changed prior to doing an update
my $CHECK_ONLY = 0;
# Set to true to suppress most informational messages. Only missing
# symbols will be printed.
my $QUIET = 0;
# Set to true if we just want to see the help message
my $HELP = 0;
# Set to true if we want to reverse the hiding direction
my $REVERSE = 0;


GetOptions(
    "help" => \$HELP,
    "quiet" => \$QUIET,
    "check-only" => \$CHECK_ONLY,
    "prefix=s" => \$myprefix,
    "suffix=s" => \$mysuffix,
    "lib=s" => \$mylib,
    "file=s" => \$myfile,
    "reverse" => \$REVERSE,
) or die "unable to parse options, stopped";

if ($HELP) {
    print <<EOT;
$0 [options]

--help | -h          This help message
--quiet | -q         Only output critical messages to stdout
--check-only         Output the symbols we would have prefixed, but don't do anything
--prefix=NAME        Add NAME to the front of all found symbols
--suffix=NAME        Add NAME to the end of all found symbols
--lib=NAME           Library containing symbols that are to be "hidden"
--file=NAME          Output file for results, or existing file to be updated
--reverse            Reverse the direction of hiding (i.e., #define prefix_foo to be foo)
EOT
    exit(0);
}

#-------------------------------------------------------------------------------
# predeclare sub for print-like syntax
sub quiet_print {
    unless ($QUIET) {
        print @_;
    }
}

#-------------------------------------------------------------------------------

$mycapprefix = uc $myprefix;

# get the symbol output for this lib
my $output = qx(nm $mylib);

# cycle across each line and tokenize it
my @symbols;
my $len = 0;
foreach my $line (split /[\r\n]+/, $output) {
    quiet_print "$line\n";
    my @values = split(' ',$line);
    my $val = shift(@values);
    # if the first character isn't a number, then
    # we can ignore the line
    my $str = substr($val,0,1);
    if ("0" ne $str) {
        quiet_print "skipping\n";
        next;
    }
    # this is a line of interest - see if the
    # next token indicates a public symbol by
    # being a 'T' or a 'B'
    $val = shift(@values);
    if ("T" eq $val || "B" eq $val || "D" eq $val) {
        $val = shift(@values);
        # if this symbol contains a '.', then we
        # need to ignore it
        if (index($val, ".") != -1) {
            quiet_print "skipping $val\n";
            next;
        }
        quiet_print "GOT: " . $val . "\n";
        push @symbols, $val;
        if ($len < length($val)) {
            $len = length($val);
        }
    }
}

$len = $len + 5;
if ($myfile ne "") {
    open FILE, ">$myfile" || die "file could not be opened";
}
sub checkCase {
    if ($_[0] =~ /^[[:upper:]]/) {
        return 1;
    }
    else {
        return 0;
    }
}

foreach my $sym (@symbols) {
    my $out;
    if ($REVERSE) {
        # if the first char is a cap, then use the cap prefix
        if (checkCase($sym)) {
            $out = "#define " . $mycapprefix . $sym . $mysuffix;
        } else {
            $out = "#define " . $myprefix . $sym . $mysuffix;
        }
    } else {
        $out = "#define " . $sym;
    }
    my $diff = $len - length($sym);
    for (my $i=0; $i < $diff; $i++) {
       $out = $out . " ";
    }
    if ($REVERSE) {
        $out = $out . $sym . "\n";
    } else {
        # if the first char is a cap, then use the cap prefix
        if (checkCase($sym)) {
            $out = $out . $mycapprefix . $sym . $mysuffix . "\n";
        } else {
            $out = $out . $myprefix . $sym . $mysuffix . "\n";
        }
    }
    if ($myfile ne "") {
        print FILE $out;
    } else {
        print $out;
    }
}
if ($myfile ne "") {
    close FILE;
}