#!/usr/bin/python

""" 
Generates common/src/SyscallInformation.C and dynutil/h/dyn_syscalls.h 
"""

import os
import sys
import string

def generate_dyn_syscalls(allSyscallNames):
    # Open file for writing
    outFile = open("dyn_syscalls.h", "w")

    # Write out "header" information
    outFile.write("/* This file was autogenerated from syscalls/generateSyscallInformation.py */" + "\n")
    outFile.write("#if !defined(DYN_SYSCALLS_H_)\n")
    outFile.write("#define DYN_SYSCALLS_H_ \n")
    outFile.write("\n")
    outFile.write("namespace Dyninst {\n")
    outFile.write("namespace Syscall {\n")
    outFile.write("enum {\n")

    # Write out our system calls!
    for syscall in allSyscallNames:
        if (syscall.find("//") == -1):
            outFile.write("\tdyn_" + syscall + ", \n")
        else:
            outFile.write("\t" + syscall + "\n")

    # Write out "footer" information
    outFile.write("};\n")
    outFile.write("}}\n")
    outFile.write("#endif\n")

    # Close file
    outFile.close()

def generate_SyscallInformation(allSyscallNames, allSyscalls):
    # Open file for writing
    outFile = open("SyscallInformation.C", "w")

    # Write out "header" information
    outFile.write("/* This file was autogenerated from syscalls/generateSyscallInformation.py */" + "\n")
    outFile.write("#include \"dyn_regs.h\"\n")
    outFile.write("#include \"dyn_syscalls.h\"\n")
    outFile.write("SyscallInformation::SyscallInformation() {\n")

    curOS = allSyscalls[0][0]
    curArch = allSyscalls[0][1]
    curPlatName = curOS + "_" + curArch 

    outFile.write("\n/* " + curOS + " " + curArch + " Numbers */\n")
    outFile.write("Platform " + curPlatName + "(" + curArch + ", " + curOS + ");\n")

    for syscall in allSyscalls:
        newPlat = False
        if (curOS != syscall[0]):
            curOS = syscall[0]
            newPlat = True
        if (curArch != syscall[1]):
            curArch = syscall[1]
            newPlat = True

        if (newPlat):
            curPlatName = curOS + "_" + curArch
            outFile.write("\n/* " + curOS + " " + curArch + " Numbers */\n")
            outFile.write("Platform " + curPlatName + "_plat(" + curArch + ", " + curOS + ");\n")
        
        curName = syscall[2]
        curPlatformNumber = syscall[3]

        # Write out the two mapping insertions
        outFile.write(curPlatName + "_syscallNumbers.insert(make_pair<MachSyscall::SyscallIDIndependent, MachSyscall::SyscallIDPlatform>(Syscall::dyn_" + curName + ", " + str(curPlatformNumber) + "));\n")
        outFile.write(curPlatName + "_syscallNames.insert(make_pair(" + curPlatformNumber + ", \"" + curName + "\"));\n")

    # Write out "footer" information
    outFile.write("}\n")

    # Close file
    outFile.close()

allSyscallNames = []
allSyscalls = []

topDir = "unistd-by-platform"
for curOS in os.listdir(topDir):
    print curOS
    for curArch in os.listdir(topDir + "/" + curOS):
        print "\t" + curArch
        for curFile in os.listdir(topDir + "/" + curOS + "/" + curArch):
            print "\t\t" + curFile

            # Cheat and add some additional information to the dyn_syscalls.h enum
            allSyscallNames.append("// " + curOS + "/" + curArch + "/" + curFile)

            # Process the file!
            curFileHandle = open(topDir + "/" + curOS + "/" + curArch + "/" + curFile, "r")

            # Keep a simple name-number mapping to deal with subsequent syscalls defined with addition
            sysNumbers = dict()

            for line in curFileHandle:
                line = line.rstrip()

                if (line.find("#define") == 0) and (line.find("NR") != -1):
                    print line

                    # Extract out system call name and number
                    define, NR, info = line.partition("NR_")
                    infoList = info.split()
                    syscallName = infoList[0]
                    syscallNumber = infoList[1]

                    # Build a platform-specific descriptor for the current syscall
                    curSyscall = []
                    curSyscall.append(curOS)
                    curSyscall.append(curArch)
                    curSyscall.append(syscallName)
                    
                    # Deal with non-numeric values (e.g., those that do math)
                    if (syscallNumber.find("NR") != -1):
                        # Deal with addition shenanigans in unistd.h
                        left, NR, add = syscallNumber.partition("NR_")
                        syscallNumber = add.rstrip(")")
                        baseSyscall, add, val = syscallNumber.partition("+")
                        baseSyscallNumber = sysNumbers[baseSyscall]
                        realSyscallNumber = int(float(baseSyscallNumber)) + int(float(val))
                        syscallNumber = realSyscallNumber
                       
                    # Finally, add correct syscall numbers to our map
                    curSyscall.append(str(syscallNumber))
                    allSyscalls.append(curSyscall)

                    # Add to our dictionary for later
                    sysNumbers[syscallName] = syscallNumber

                    # If necessary, add current syscall name to master list
                    if (allSyscallNames.count(syscallName) == 0):
                        allSyscallNames.append(syscallName)

# Generate the two files
generate_dyn_syscalls(allSyscallNames)
generate_SyscallInformation(allSyscallNames, allSyscalls)

