import sys
import os
import operator

if len(sys.argv) != 2:
    sys.stderr.write("Wrong number of argmuments. Wanted one, the name of the\
            arch to use, got %s" % sys.argv)

arch = sys.argv[1]

f = open(os.path.join(arch, 'ioctl_list')) # found by find_ioctls.py
m = open(os.path.join(arch, 'manually_found')) # found by the Mach V Eyeball
i = open(os.path.join(arch, 'ignore_list')) # removed from the output of find_ioctls.py

print("// Initially generated by process_ioctls.py")

consts = {
    "mips": { "NONE": 1, "READ": 2, "WRITE": 4, "SIZEBITS": 13, "DIRBITS": 3},
    "powerpc": { "NONE": 1, "READ": 2, "WRITE": 4, "SIZEBITS": 13, "DIRBITS": 3},
    "x86": { "NONE": 0, "READ": 2, "WRITE": 1, "SIZEBITS": 14, "DIRBITS": 2},
    "x86_64": { "NONE": 0, "READ": 2, "WRITE": 1, "SIZEBITS": 14, "DIRBITS": 2},
    "arm": { "NONE": 0, "READ": 2, "WRITE": 1, "SIZEBITS": 14, "DIRBITS": 2},
    "aarch64": { "NONE": 0, "READ": 2, "WRITE": 1, "SIZEBITS": 14, "DIRBITS": 2},
}

ioc_consts = {
    "SIOCPROTOPRIVATE": 0x89E0,
    "SIOCDEVPRIVATE": 0x89F0,
    "PCIIOC_BASE": (ord('P') << 24 | ord('C') << 16 | ord('I') << 8),
    "FIONREAD": 0x541B,
    "CZIOC": ord('M') << 8,
    "TIOCOUTQ": 0x5411,
    "TIOCM_CAR": 0x040,
    "TIOCM_RNG": 0x080,
}

NONE = consts[arch]["NONE"]
READ = consts[arch]["READ"]
WRITE = consts[arch]["WRITE"]
SIZEBITS = consts[arch]["SIZEBITS"]
DIRBITS = consts[arch]["DIRBITS"]
NRBITS = 8
TYPEBITS = 8
NRSHIFT = 0
TYPESHIFT = NRSHIFT + NRBITS
SIZESHIFT = TYPESHIFT + SIZEBITS
DIRSHIFT = SIZESHIFT + DIRBITS
NRMASK = (1 << NRBITS) - 1
TYPEMASK = (1 << TYPEBITS) - 1
SIZEMASK = (1 << SIZEBITS) - 1
DIRMASK = (1 << DIRBITS) - 1

def decode(val):
    return (((val >> DIRSHIFT) & DIRMASK), ((val >> TYPESHIFT) & TYPEMASK),
            ((val >> NRSHIFT) & NRMASK), ((val >> SIZESHIFT) & SIZEMASK))

ioctls = [] # ones we want to actually process

for ioctl in f:
    ioctls.append(eval(ioctl))
for ioctl in m:
    ioctls.append(eval(ioctl))

mp = { "_IO": "none", "_IOR": "read", "_IOW": "write", "_IOWR": "readwrite",
        "DRM_IO": "none", "DRM_IOR": "read", "DRM_IOW": "write", "DRM_IOWR": "readwrite"}

tys = {
        "__uint8_t": "u8",
        "__uint16": "u16",
        "__uint32_t": "u32",
        "__uint64_t": "u64",
        "__int8_t": "i8",
        "__int16": "i16",
        "__int32_t": "i32",
        "__int64_t": "i64",
        "uint8_t": "u8",
        "uint16": "u16",
        "uint32_t": "u32",
        "uint64_t": "u64",
        "int8_t": "i8",
        "int16": "i16",
        "int32_t": "i32",
        "int64_t": "i64",
        "__u8": "u8",
        "__u16": "u16",
        "__u32": "u32",
        "__u64": "u64",
        "__s8": "i8",
        "__s16": "i16",
        "__s32": "i32",
        "__s64": "i64",
        "int": "::libc::c_int",
        "long": "::libc::c_long",
        "char": "::libc::c_char",
        "size_t": "::libc::size_t",
}

utys = {
        "int": "::libc::c_uint",
        "long": "::libc::c_ulong",
        "char": "::libc::c_uchar",
}

known_structs = ["input_id", "ff_effect", "ff_trigger", "ff_replay"]

manually_bound = ["EVIOCGNAME", "EVIOCGPHYS", "EVIOCGUNIQ", "EVIOCGPROP",
"EVIOCGMTSLOTS", "EVIOCGKEY", "EVIOCGLED", "EVIOCGSND", "EVIOCGSW",
"EVIOCGBIT", "EVIOCGABS", "EVIOCSABS", "EVIOCGRAB", "EVIOCREVOKE",
"EVIOCSCLOCKID"]

bad_recovery = {}

def translate(ty):
    if len(ty) == 1:
        return tys.get(ty[0], "FIXME1<%s>" % ty)
    elif ty[-1] == '*':
        return "*mut " + translate(ty[:-1])
    elif len(ty) == 2:
        if ty[0] == "struct":
            return "/*struct*/ %s" % ty[1]
        elif ty[0] == "unsigned":
            return utys[ty[1]]
        else:
            return "FIXME2<%s>" % ty
    elif ty[-1] == ']':
        count = ty[-2]
        return "[%s; %s]" % (translate(ty[:-3]), count)
    else:
        return "FIXME3<%s>" % ty

def translate_type_code(ty):
    if ty[0] == "'":
        return "b" + ty
    else:
        return ty

def bad(name, val):
    if name in bad_recovery:
        process(bad_recovery[name])
    else:
        pval = None
        try:
            pval = int(val, 0)
        except:
            pass
        if pval is None:
            print("ioctl!(bad %s with %s);" % (name.lower(), val))
        else:
            (dr, ty, nr, sz) = decode(pval)
            if dr == NONE:
                print("ioctl!(none %s with %s, %s);" % (name.lower(), ty, nr))
            elif dr == READ:
                print("ioctl!(read %s with %s, %s; [u8; %s]);" %
                        (name.lower(), ty, nr, sz));
            elif dr == WRITE:
                print("ioctl!(write %s with %s, %s; [u8; %s]);" %
                        (name.lower(), ty, nr, sz));
            elif dr == READ|WRITE:
                print("ioctl!(readwrite %s with %s, %s; [u8; %s]);" %
                        (name.lower(), ty, nr, sz));
            else:
                raise "This really shouldn't happen"

def bad2(name, val1, val2, op):
    if val1 in ioc_consts:
        val1 = ioc_consts[val1]
    else:
        val1 = int(val1, 0)

    if val2 in ioc_consts:
        val2 = ioc_consts[val2]
    else:
        val2 = int(val2, 0)

    bad(name, str(op(val1, val2)))

def process(ioctl):
    name = ioctl[0]
    rhs = ioctl[1:-1] # remove '#' or trailing comment
    cmd = rhs[0]
    body = rhs[2:-1]

    if name in manually_bound:
        return
    elif cmd == "_IO":
        print("ioctl!(none %s with %s, %s);" % (name.lower(), translate_type_code(body[0]),
            body[2]))
    elif cmd == '_IOR' or cmd == '_IOW' or cmd == '_IOWR':
        if body[3] == ',':
            # this looks like _IOR(X, B, type...)
            first = body[0]
            second = body[2]
            ty = body[4:]
            ty = translate(ty)
            if "FIXME" in ty or "/*struct*/" in ty and not ty[11:] in known_structs:
                print("// ioctl!(%s %s with %s, %s; %s);" % (mp[cmd], name.lower(),
                    translate_type_code(first), second, ty))
            else:
                print("ioctl!(%s %s with %s, %s; %s);" % (mp[cmd], name.lower(),
                    translate_type_code(first), second, ty))
        elif body[3] == '+':
            first = body[0]
            second = " ".join(body[2:5])
            ty = body[6:]
            ty = translate(ty)
            if "FIXME" in ty or "/*struct*/" in ty and not ty[11:] in known_structs:
                print("// ioctl!(%s %s with %s, %s; %s);" % (mp[cmd], name.lower(),
                    translate_type_code(first), second, ty))
            else:
                print("ioctl!(%s %s with %s, %s; %s);" % (mp[cmd], name.lower(),
                    translate_type_code(first), second, ty))
            # We probably have _IOR(X, B + C, type...)
        else:
            raise "This really shouldn't happen"
    elif cmd == "DRM_IO" or cmd == "DRM_IOR" or cmd == "DRM_IOW" or cmd == "DRM_IOWR":
        # rewrite into "canonical" version.
        process([name, cmd[3:], "(", "DRM_IOCTL_BASE", ","] + rhs[2:] + ["#"])
    elif len(rhs) == 1: # single constant :(
        bad(name.lower(), ioc_consts.get(rhs[0], rhs[0]))
    elif len(rhs) == 3 and rhs[0] == "(": # single constant in parens
        bad(name.lower(), ioc_consts.get(rhs[1], rhs[1]))
    elif rhs[2] == "+": # we have the sum of two constants
        try:
            bad2(name.lower(), rhs[1], rhs[3], operator.add)
        except:
            bad(name.lower(), " ".join(rhs[1:-1]))
    elif rhs[2] == "|": # we have an or of two constants (eugh)
        try:
            bad2(name.lower(), rhs[1], rhs[3], operator.or_)
        except:
            bad(name.lower(), " ".join(rhs[1:-1]))
    else:
        print("// TODO #define %s %s" % (name, " ".join(rhs)))

for ioctl in ioctls:
    process(ioctl)
