#!/usr/bin/perl -w

# What it does:
#
# 1. Substitutes 0 for (nil). There are a few cases where (nil) is printed.
#   a) A function call returning a null value, e.g.,
#      int* p = null_returning_function();
#   b) In a memory variable name as in _dsn_mem_(nil).
#   c) In the condition part of an if statement generated due to unsupported
#      operators in SMT (e.g., bit shifting).
# 2. Adds declarations for _dsm_mem variables.
# 3. Adds a missing closing bracket in the case of a crash.
# 4. Initializes optind to 1 if the said extern variable presents.

use strict;
use warnings;

my ($infilename, @bad) = @ARGV;
die "Too many arguments " if @bad;
die "missing filename " unless $infilename;

my %mem_vars = ();
my %mem_vars_init = ();

# Identify memory variables (i.e., _dsn_mem vars) with correct types.
# Assumes that type information follows immediately in a comment
# in the form of, e.g,. '/*|int |*/' or '*|int |val: 9|*/'.
# Oct 30, 2014: type information is no longer used, as the concrete pass
# converts every type to signed long long.
open INFILE, "<", $infilename or die $!;
while (<INFILE>) {
    while (m!_dsn_mem_(0x[0-9a-fA-F]+|\(nil\))/\*\|([^|]*)(\|val: ([^|]*)|)\|\*/( = |)!g) {
        my $type = $2;
        my $addr = $1 eq "(nil)" ? "0x0" : $1;
        my $val = (defined $4 and $4 eq "(nil)") ? "0x0" : $4;
        my $is_asgn = $5 eq " = ";

        # Check if the memory location being accessed is initialized.
        if (!$is_asgn and !exists($mem_vars{$addr})) {
            # Uninitialized; record the current value to add initialization
            # in the final output.
            die "$0: value cannot be extracted." if !defined $val;
            $mem_vars_init{$addr} = $val;
        }
        $mem_vars{$addr} = 1;
    }
}

open INFILE, "<", $infilename or die $!;
my $last_seen = "";
while (<INFILE>) {
    s/_dsn_mem_\(nil\)/_dsn_mem_0x0/g;
    s/ = \(nil\);/ = 0;/g;
    s/ == \(nil\)/ == 0/g;
    s!// Assigned: \(nil\)!// Assigned: 0!;
    s!\|val: \(nil\)\|\*/!\|val: 0\|\*/!g;

    s/extern long long optind.*/long long optind = 1;/;
    # optarg is usually initialized to 0 (though undocumented).
    s/extern long long optarg.*/long long optarg = 0;/;
    
    print ($last_seen = $_);
    if (/^int main\(int argc/) {
        for my $addr (keys %mem_vars) {
            my $init = exists($mem_vars_init{$addr}) ?
                          " = $mem_vars_init{$addr}" : "";
            print "  long long _dsn_mem_$addr$init;\n";
        }
    }
}

chomp $last_seen;
if ($last_seen ne "} // main") {
    print "\n  // Looks like the concretized trace ran into a crash.\n";
    #print "  // Adding an assert and a closing bracket for main.\n";
    #print "  assert(0);\n";
    print "} // main";
}
