ioctl-id 0.2.0

Rust identifiers for `ioctl(2)` calls on Unix-like systems.
Documentation
#include <sys/ioctl.h>

typedef struct {
    char c;
} Size1;

typedef struct {
    char c[20];
} Size20;

typedef struct {
    char c[IOCPARM_MASK];
} SizeMax;

#define EQ_CHECK(name) \
    fprintf(fp, "    assert_eq!(%s, %u);\n", #name, name)

#define IOC_CHECK(dir, type, nr, size) \
    fprintf(fp, "    assert_eq!(ioc(%lu, %lu, %lu, %lu), 0x%lx);\n", \
            ((unsigned long) (dir)), \
            ((unsigned long) (type)), \
            ((unsigned long) (nr)), \
            ((unsigned long) (size)), \
            ((unsigned long) _IOC((dir), (type), (nr), (size))))

#define IO_CHECK(type, nr) \
    fprintf(fp, "    assert_eq!(io(%lu, %lu), 0x%lx);\n", \
            ((unsigned long) (type)), \
            ((unsigned long) (nr)), \
            ((unsigned long) _IO((type), (nr))))

#define IOFN_CHECK(rustfn, cfn, type, nr, passed_type) \
    fprintf(fp, "    assert_eq!(%s::<%s>(%lu, %lu), 0x%lx, \"Expected %s::<%s>(%lu, %lu) to equal 0x%lx\");\n", \
            #rustfn, \
            #passed_type, \
            ((unsigned long) (type)), \
            ((unsigned long) (nr)), \
            ((unsigned long) cfn((type), (nr), passed_type)), \
            #rustfn, \
            #passed_type, \
            ((unsigned long) (type)), \
            ((unsigned long) (nr)), \
            ((unsigned long) cfn((type), (nr), passed_type)))

#define IOFN_CHECKS(rustfn, cfn) \
    do { \
        IOFN_CHECK(ior, _IOR, 0, 0, Size1); \
        IOFN_CHECK(ior, _IOR, 1, 1, Size20); \
        IOFN_CHECK(ior, _IOR, 0, 0, SizeMax); \
        IOFN_CHECK(ior, _IOR, 0, 0xff, Size1); \
        IOFN_CHECK(ior, _IOR, 0, 0xff, SizeMax); \
        IOFN_CHECK(ior, _IOR, 0xff, 0, Size1); \
        IOFN_CHECK(ior, _IOR, 0xff, 0, SizeMax); \
        IOFN_CHECK(ior, _IOR, 0xff, 0xff, Size1); \
        IOFN_CHECK(ior, _IOR, 0xff, 0xff, SizeMax); \
    } while (0)

static int ioctl_ccompat_test(char const *arch) {
    char filename[256];
    FILE *fp;

    sprintf(filename, "src/ccompat_tests_macos.rs");
    fp = fopen(filename, "w");
    if (fp == NULL) {
        perror(filename);
        return 1;
    }

    fprintf(fp, "//! This is an automatically generated file; do not edit.\n");
    fprintf(fp, "//! Generated by gen_ccompat_tests.c on %s %s\n", __DATE__, __TIME__);
    fprintf(fp, "use super::*;\n");
    fprintf(fp, "\n");
    fprintf(fp, "#[repr(C)]\n");
    fprintf(fp, "struct Size1([u8; 1]);\n");
    fprintf(fp, "\n");
    fprintf(fp, "#[repr(C)]\n");
    fprintf(fp, "struct Size20([u8; 20]);\n");
    fprintf(fp, "\n");
    fprintf(fp, "#[repr(C)]\n");
    fprintf(fp, "struct SizeMax([u8; %u]);\n", IOCPARM_MASK);
    fprintf(fp, "\n");
    fprintf(fp, "#[test]\n");
    fprintf(fp, "fn test_ioctl_ccompat() {\n");
    fprintf(fp, "    assert_eq!(core::mem::size_of::<Size1>(), 1);\n");
    fprintf(fp, "    assert_eq!(core::mem::size_of::<Size20>(), 20);\n");
    fprintf(fp, "    assert_eq!(core::mem::size_of::<SizeMax>(), %u);\n", IOCPARM_MASK);
    fprintf(fp, "\n");
    EQ_CHECK(IOCPARM_MASK);
    EQ_CHECK(IOCPARM_MAX);
    EQ_CHECK(IOC_VOID);
    EQ_CHECK(IOC_OUT);
    EQ_CHECK(IOC_IN);

    IOC_CHECK(IOC_VOID, 0, 0, 0);
    IOC_CHECK(IOC_OUT, 0, 0, 0);
    IOC_CHECK(IOC_IN, 0, 0, 0);
    IOC_CHECK(IOC_IN|IOC_OUT, 0, 0, 0);
    IOC_CHECK(IOC_OUT, 1, 1, 20);
    IOC_CHECK(IOC_OUT, 0, 0, IOCPARM_MASK);
    IOC_CHECK(IOC_OUT, 0, 0xff, 0);
    IOC_CHECK(IOC_OUT, 0, 0xff, IOCPARM_MASK);
    IOC_CHECK(IOC_OUT, 0xff, 0, 0);
    IOC_CHECK(IOC_OUT, 0xff, 0, IOCPARM_MASK);
    IOC_CHECK(IOC_OUT, 0xff, 0xff, 0);
    IOC_CHECK(IOC_OUT, 0xff, 0xff, IOCPARM_MASK);

    IO_CHECK(0, 0);
    IO_CHECK(1, 1);
    IO_CHECK(0, 0xff);
    IO_CHECK(0xff, 0);
    IO_CHECK(0xff, 0xff);

    IOFN_CHECKS(ior, _IOR);
    IOFN_CHECKS(iow, _IOW);
    IOFN_CHECKS(iowr, _IOWR);

    fprintf(fp, "}\n");
    fclose(fp);
    return 0;
}