nix 0.1.3

Rust friendly bindings to Posix APIs
use std::old_path::Path;
use libc::{c_int, mode_t};
use errno::{SysResult, SysError};
use sys::stat::Mode;
use utils::ToCStr;

pub use self::consts::*;
pub use self::ffi::flock;

pub type Fd = c_int;

#[allow(dead_code)]
mod ffi {
    pub use libc::{open, fcntl};
    pub use self::os::*;

    #[cfg(target_os = "linux")]
    mod os {
        use libc::{c_int, c_short, off_t, pid_t};

        #[repr(C)]
        #[derive(Copy)]
        pub struct flock {
            pub l_type: c_short,
            pub l_whence: c_short,
            pub l_start: off_t,
            pub l_len: off_t,
            pub l_pid: pid_t,

            // not actually here, but brings in line with freebsd
            pub l_sysid: c_int,
        }

        pub const F_DUPFD:         c_int = 0;
        pub const F_DUPFD_CLOEXEC: c_int = 1030;
        pub const F_GETFD:         c_int = 1;
        pub const F_SETFD:         c_int = 2;
        pub const F_GETFL:         c_int = 3;
        pub const F_SETFL:         c_int = 4;
        pub const F_SETLK:         c_int = 6;
        pub const F_SETLKW:        c_int = 7;
        pub const F_GETLK:         c_int = 5;
    }

    #[cfg(any(target_os = "macos", target_os = "ios"))]
    mod os {
        use libc::{c_int, c_short, off_t, pid_t};

        #[repr(C)]
        #[derive(Copy)]
        pub struct flock {
            pub l_start: off_t,
            pub l_len: off_t,
            pub l_pid: pid_t,
            pub l_type: c_short,
            pub l_whence: c_short,

            // not actually here, but brings in line with freebsd
            pub l_sysid: c_int,
        }

        pub const F_DUPFD:         c_int = 0;
        pub const F_DUPFD_CLOEXEC: c_int = 67;
        pub const F_GETFD:         c_int = 1;
        pub const F_SETFD:         c_int = 2;
        pub const F_GETFL:         c_int = 3;
        pub const F_SETFL:         c_int = 4;
        pub const F_SETLK:         c_int = 8;
        pub const F_SETLKW:        c_int = 9;
        pub const F_GETLK:         c_int = 7;
    }
}

pub fn open(path: &Path, oflag: OFlag, mode: Mode) -> SysResult<Fd> {
    let fd = unsafe { ffi::open(path.to_c_str().as_ptr(), oflag.bits(), mode.bits() as mode_t) };

    if fd < 0 {
        return Err(SysError::last());
    }

    Ok(fd)
}

pub enum FcntlArg<'a> {
    F_DUPFD(Fd),
    F_DUPFD_CLOEXEC(Fd),
    F_GETFD,
    F_SETFD(FdFlag), // FD_FLAGS
    F_GETFL,
    F_SETFL(OFlag), // O_NONBLOCK
    F_SETLK(&'a flock),
    F_SETLKW(&'a flock),
    F_GETLK(&'a mut flock),
    #[cfg(target_os = "linux")]
    F_OFD_SETLK(&'a flock),
    #[cfg(target_os = "linux")]
    F_OFD_SETLKW(&'a flock),
    #[cfg(target_os = "linux")]
    F_OFD_GETLK(&'a mut flock)

    // TODO: Rest of flags
}

// TODO: Figure out how to handle value fcntl returns
pub fn fcntl(fd: Fd, arg: FcntlArg) -> SysResult<()> {
    use self::FcntlArg::*;

    let res = unsafe {
        match arg {
            F_SETFD(flag) => ffi::fcntl(fd, ffi::F_SETFD, flag.bits()),
            F_SETFL(flag) => ffi::fcntl(fd, ffi::F_SETFL, flag.bits()),
            _ => unimplemented!()
        }
    };

    if res < 0 {
        return Err(SysError::last());
    }

    Ok(())
}

#[cfg(target_os = "linux")]
mod consts {
    use libc::c_int;

    bitflags!(
        flags OFlag: c_int {
            const O_ACCMODE   = 0o00000003,
            const O_RDONLY    = 0o00000000,
            const O_WRONLY    = 0o00000001,
            const O_RDWR      = 0o00000002,
            const O_CREAT     = 0o00000100,
            const O_EXCL      = 0o00000200,
            const O_NOCTTY    = 0o00000400,
            const O_TRUNC     = 0o00001000,
            const O_APPEND    = 0o00002000,
            const O_NONBLOCK  = 0o00004000,
            const O_DSYNC     = 0o00010000,
            const O_DIRECT    = 0o00040000,
            const O_LARGEFILE = 0o00100000,
            const O_DIRECTORY = 0o00200000,
            const O_NOFOLLOW  = 0o00400000,
            const O_NOATIME   = 0o01000000,
            const O_CLOEXEC   = 0o02000000,
            const O_SYNC      = 0o04000000,
            const O_PATH      = 0o10000000,
            const O_TMPFILE   = 0o20000000,
            const O_NDELAY    = O_NONBLOCK.bits
        }
    );

    bitflags!(
        flags FdFlag: c_int {
            const FD_CLOEXEC = 1
        }
    );
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
mod consts {
    use libc::c_int;

    bitflags!(
        flags OFlag: c_int {
            const O_ACCMODE   = 0x0000003,
            const O_RDONLY    = 0x0000000,
            const O_WRONLY    = 0x0000001,
            const O_RDWR      = 0x0000002,
            const O_CREAT     = 0x0000200,
            const O_EXCL      = 0x0000800,
            const O_NOCTTY    = 0x0020000,
            const O_TRUNC     = 0x0000400,
            const O_APPEND    = 0x0000008,
            const O_NONBLOCK  = 0x0000004,
            const O_DSYNC     = 0x0400000,
            const O_DIRECTORY = 0x0100000,
            const O_NOFOLLOW  = 0x0000100,
            const O_CLOEXEC   = 0x1000000,
            const O_SYNC      = 0x0000080,
            const O_NDELAY    = O_NONBLOCK.bits,
            const O_FSYNC     = O_SYNC.bits
        }
    );

    bitflags!(
        flags FdFlag: c_int {
            const FD_CLOEXEC = 1
        }
    );
}