1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! File System support (unfinished)

use core::mem::uninitialized;
use core::str::from_utf8_unchecked;

use card10_sys::*;

type Result<T> = core::result::Result<T, Error>;

#[derive(Copy, Clone, Debug)]
pub struct Error {
    pub errno: i32,
}

impl Error {
    fn check<F: FnOnce() -> i32>(f: F) -> Result<i32> {
        let res = f();
        if res >= 0 {
            Ok(res)
        } else {
            Err(res.into())
        }
    }
}

impl From<i32> for Error {
    fn from(errno: i32) -> Self {
        Error { errno }
    }
}

pub fn read_dir(path: &str) -> Result<ReadDir> {
    let pathbuf = crate::str_to_cstr(path);
    Error::check(|| unsafe { epic_file_opendir(pathbuf.as_ptr()) }).map(|fd| ReadDir { fd })
}

pub struct ReadDir {
    fd: i32,
}

impl<'a> Iterator for ReadDir {
    type Item = Result<DirStat>;
    fn next(&mut self) -> Option<Self::Item> {
        let mut entry: epic_stat = unsafe { uninitialized() };
        let res = unsafe { epic_file_readdir(self.fd, &mut entry as *mut epic_stat as *mut _) };
        if res < 0 {
            return Some(Err(res.into()));
        }
        match entry.type_ {
            epic_stat_type::None => None,
            _ => Some(Ok(DirStat { entry })),
        }
    }
}

#[allow(non_camel_case_types)]
#[repr(u8)]
#[derive(Copy, Clone)]
pub enum epic_stat_type {
    None = 0,
    File = 1,
    Dir = 2,
}

#[allow(non_camel_case_types)]
#[repr(packed)]
#[derive(Copy, Clone)]
pub struct epic_stat {
    /// not u32 as generated by bindgen
    pub type_: epic_stat_type,
    pub size: u32,
    pub name: [ctypes::c_char; 256usize],
    pub _reserved: [u8; 12usize],
}

pub struct DirStat {
    entry: epic_stat,
}

impl DirStat {
    pub fn is_file(&self) -> bool {
        match self.entry.type_ {
            epic_stat_type::File => true,
            _ => false,
        }
    }

    pub fn is_dir(&self) -> bool {
        match self.entry.type_ {
            epic_stat_type::Dir => true,
            _ => false,
        }
    }

    /// FIXME: corrupt
    pub fn size(&self) -> u32 {
        self.entry.size
    }

    pub fn name(&self) -> &str {
        let len = self.entry.name.iter().position(|b| *b == 0).unwrap_or(0);
        unsafe { from_utf8_unchecked(&self.entry.name[0..len]) }
    }
}

pub fn rename(from: &str, to: &str) -> Result<()> {
    let frombuf = crate::str_to_cstr(from);
    let tobuf = crate::str_to_cstr(to);
    Error::check(|| unsafe { epic_file_rename(frombuf.as_ptr(), tobuf.as_ptr()) }).map(|_| ())
}

pub struct File {
    pub fd: i32,
}

impl File {
    pub fn open(path: &str) -> Result<File> {
        let pathbuf = crate::str_to_cstr(path);
        let modebuf = b"r\0";
        Error::check(|| unsafe { epic_file_open(pathbuf.as_ptr(), modebuf.as_ptr()) })
            .map(|fd| File { fd })
    }

    pub fn read<'a>(&mut self, buf: &'a mut [u8]) -> Result<&'a [u8]> {
        let bytes =
            Error::check(|| unsafe { epic_file_read(self.fd, buf.as_ptr() as *mut _, buf.len()) })?
                as usize;
        Ok(&buf[0..bytes])
    }
}

impl Drop for File {
    fn drop(&mut self) {
        unsafe {
            epic_file_close(self.fd);
        }
    }
}