diff --git a/Cargo.toml b/Cargo.toml index d076b2bec265e22cbf97f81f6d8d76333e311383..98d3bba077c6c409b44e478e835280fb8d8cdb7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,4 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +libc = "0.2" diff --git a/src/lib.rs b/src/lib.rs index 31e1bb209f98ec5fc6b7cbea4c4766a555c87247..284f6bb5fdb2415cdb17f29c09c5e95b8dba21a1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,336 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); +use libc; +use libc::{__errno_location}; +use libc::{c_char, c_int, c_void, size_t, ssize_t}; + +use std::str; +use std::ffi::CStr; + +/// Get error message string +/// +/// The `strerror` function returns a string describing the error specified by +/// the error number `errnum`. +/// +/// Refer to [strerror(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/strerror.html) +/// for details. +/// +/// # Examples +/// +/// Print error description of of [EACCES][EACCES]: +/// ``` +/// use ti3_rust::strerror; +/// use libc::EACCES; +/// +/// println!("EACCES means: {}", strerror(EACCES)); +/// ``` +/// +/// [EACCES]: ../libc/constant.EACCES.html +/// +/// # See also +/// +/// [perror] +/// +pub fn strerror(errnum: c_int) -> &'static str { + let cs = unsafe { CStr::from_ptr(libc::strerror(errnum)) }; + match cs.to_str() { + Err(e) => panic!(e), + Ok(s) => s, } } + +/// Write error messages to standard error +/// +/// The `perror` function looks up the current global error number +/// [`errno`][get_errno] and maps it to a string description using +/// [`strerror][strerror], written to the standard error stream in the following +/// format: +/// +/// ```test +/// str: Error description +/// ``` +/// +/// Refer to [perror(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/perror.html) +/// for details. +/// +/// # Examples +/// +/// Refer to [open]. +/// +/// # See also +/// +/// [strerror] +/// +pub fn perror(s: &str) { + let errnum = get_errno(); + eprintln!("{}: {}", s, strerror(errnum)); +} + + +/// Get error return value +/// +/// The function `get_errno` gets the current global error number `errno`, used +/// by many functions to specify more detailed errors. +/// +/// Refer to [errno(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html) +/// for details. +/// +/// # Examples +/// +/// Refer to [perror] and [strerror]. +/// +/// # See also +/// +/// [perror], [strerror], [**Section 2.3, Error Numbers**](https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_03) +/// +pub fn get_errno() -> c_int { + return unsafe { *__errno_location() }; +} + +/// Read from a file +/// +/// The `read()` function tries to read the number of `nbyte` bytes from the +/// file specified by the open file descriptor `fildes`. It writes the result +/// into the buffer `buf` which must at least be of `nbyte` size. The number of +/// bytes actually read is returned. +/// +/// Note that even if `nbyte` bytes are available, the function *may* read +/// less bytes if the operating system so chooses, thus checking the return +/// value is indespensible. It guarantees a read of 1 byte however (as long as +/// `nbyte` is greater 0 and there's data to read). If no bytes are available +/// it returns 0. +/// +/// Refer to [read(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html) +/// for details. +/// +/// # Examples +/// +/// Read at most 1024 bytes from the standard input and end program if done. +/// ``` +/// use libc::{ssize_t, size_t, c_int, c_char}; +/// use ti3_rust::read; +/// +/// const BUFSIZ: size_t = 1024; +/// const STDIN: c_int = 0; // file descriptor to stdin +/// +/// let mut buffer: [c_char; BUFSIZ] = [0; BUFSIZ]; +/// +/// let read = match read(STDIN, &mut buffer, BUFSIZ) { +/// n if n < 0 => panic!("Error reading!"), +/// 0 => return, // End Of File reached +/// n => n as size_t, // cast ssize_t to size_t as we are definitely non-negative now +/// }; +/// ``` +/// +/// Read 1024 bytes in a loop from standard input, writing them to standard +/// output again. Note that [write] doesn't guarantee it wrote all output either. +/// Additionally we use [strerror] and [get_errno] for error handling. +/// ``` +/// use libc::{ssize_t, size_t, c_int, c_char}; +/// use ti3_rust::{read,write,strerror,get_errno}; +/// +/// const BUFSIZ: size_t = 1024; +/// const STDIN: c_int = 0; // file descriptor to stdin +/// const STDOUT: c_int = 1; // file descriptor to stdout +/// +/// loop { +/// let mut buffer: [c_char; BUFSIZ] = [0; BUFSIZ]; +/// +/// let read = match read(STDIN, &mut buffer, BUFSIZ) { +/// n if n < 0 => panic!("write: {}", strerror(get_errno())), +/// 0 => return, // End Of File reached +/// n => n as size_t, // cast ssize_t to size_t as we are definitely non-negative now +/// }; +/// +/// let mut remaining = read; +/// while remaining > 0 { +/// let written = match write(STDOUT, &buffer[BUFSIZ - remaining..], remaining) { +/// n if n < 0 => panic!("read: {}", strerror(get_errno())), +/// n => n as size_t, +/// }; +/// remaining -= written; +/// } +/// } +/// ``` +/// +/// # Return Value +/// +/// If reading the file was successful, the non-negative number of bytes read +/// shall be returned. This may be less than requested, either if the file +/// doesn't have enough bytes or the operating system chooses to do so. +/// +/// Otherwise -1 is returned and [`errno`][get_errno] is set to an error value +/// describing the error. +/// +/// # Errors +/// +/// The function may *at least* fail with the following error(s): +/// +/// * [EBADF][EBADF]: The `fildes` argument is not a valid file descriptor open +/// for reading. +/// +/// [EBADF]: ../libc/constant.EBADF.html +/// +/// # See also +/// +/// [open], [write] +/// +pub fn read(fildes: c_int, mut buf: &mut [c_char], nbyte: size_t) -> ssize_t { + let buf: *mut c_void = &mut buf as *mut _ as *mut c_void; + return unsafe { libc::read(fildes, buf, nbyte) }; +} + +/// Write to a file +/// +/// The `write()` function tries to write the number of `nbyte` bytes to the +/// file specified by the open file descriptor `fildes`. The data is read from +/// the buffer `buf` which must at least be of `nbyte` size. +/// +/// Note that even if space for `nbyte` bytes is available, the function *may* write +/// less bytes if the operating system so chooses, thus checking the return +/// value is indespensible. It guarantees a write of 1 byte however (as long as +/// `nbyte` is greater 0 and there's enough space). If all bytes are written +/// it returns `nbyte`. +/// +/// Refer to [write(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html) +/// for details. +/// +/// # Examples +/// +/// Refer to [read] +/// +/// # Return Value +/// +/// If writing the file was successful, the non-negative number of bytes written +/// shall be returned. This may be less than requested, if the file +/// the operating system chooses to do so. +/// +/// Otherwise -1 is returned and [`errno`][get_errno] is set to an error value +/// describing the error. +/// +/// # Errors +/// +/// The function may *at least* fail with the following error(s): +/// +/// * [EBADF][EBADF]: The `fildes` argument is not a valid file descriptor open +/// for reading. +/// * [ENOSPC][ENOSPC]: There was no free space remaining on the device containing the file. +/// +/// [EBADF]: ../libc/constant.EBADF.html +/// [ENOSPC]: ../libc/constant.ENOSPC.html +/// +/// # See also +/// +/// [open], [read] +/// +pub fn write(fildes: c_int, mut buf: &[c_char], nbyte: size_t) -> ssize_t { + let buf: *mut c_void = &mut buf as *mut _ as *mut c_void; + return unsafe { libc::write(fildes, buf, nbyte) }; +} + + + +/// Open file +/// +/// The `open()` function creates a connection between a file named by the +/// pathname `path` and a file descriptor which it creates and returns. The file +/// descriptor can then be used by other I/O functions to access that file. +/// +/// Refer to [open(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html) +/// for details +/// +/// # Examples +/// +/// Open a file by reading and panic otherwise. +/// ```should_panic +/// use libc::O_RDONLY; +/// use ti3_rust::open; +/// +/// let pathname = "/path/to/some/file"; +/// let fd = match open(pathname, O_RDONLY) { +/// -1 => panic!("{}: Couldn't open", pathname), +/// n => n, +/// }; +/// // ... +/// ``` +/// +/// Like above, but print more verbose error message using [perror] and Don't Panic! +/// Finally the file descriptor is deallocated using [close] +/// ``` +/// use libc::O_RDONLY; +/// use ti3_rust::{open,perror,close}; +/// +/// let pathname = "/path/to/some/file"; +/// let fd = open(pathname, O_RDONLY); +/// if fd == -1 { +/// perror(pathname); +/// return; +/// } +/// // ... +/// close(fd); +/// ``` +/// +/// # Return Value +/// +/// If opening the file succeeded, a non-negative integer will be returned. +/// Otherwise -1 is returned and [`errno`][get_errno] set accordingly. +/// +/// # Errors +/// +/// The value [`errno`][get_errno] can assume *at least* the following values: +/// +/// * [EACCES][EACCES]: Accessing either a component of the path prefix or the +/// file itself isn't allowed with the specified `oflags`. +/// +/// * [ENOENT][ENOENT]: Either the path specified by `path` does not name +/// an existing file while O_CREAT is set, or O_CREAT is set but a component +/// of the path prefix does not name an existing directory, or `path` is an +/// empty string. +/// +/// * [EINVAL][EINVAL]: The value of the `oflag` argument is not valid. +/// +/// [EACCES]: ../libc/constant.EACCES.html +/// [ENOENT]: ../libc/constant.ENOENT.html +/// [EINVAL]: ../libc/constant.EINVAL.html +/// +pub fn open(path: &str, oflag: c_int) -> c_int { + let path = path.as_bytes().as_ptr() as *const i8; + return unsafe { libc::open(path, oflag) }; +} + +//pub fn open_with_mode(path: &str, oflag: c_int, mode: mode_t) -> c_int { +// let path = path.as_bytes().as_ptr() as *const i8; +// return unsafe { libc::open(path, oflag, mode) }; +//} + + +/// Close a file descriptor +/// +/// The `close()` function shall deallocate the file descriptor `fildes`. +/// +/// Refer to [close(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html) +/// for details. +/// +/// # Examples +/// +/// Refer to [open]. +/// +/// # Return Value +/// +/// If the file descriptor could successfully be deallocated, 0 is returned. +/// Otherwise -1 is returned and [`errno`][get_errno] set accordingly. +/// +/// # Errors +/// +/// The value [`errno`][get_errno] can be set to *at least* the value(s): +/// +/// * [EBADF][EBADF]: The value `fildes` does not refer to a valid open file +/// descriptor. +/// +/// [EBADF]: ../libc/constant.EBADF.html +/// +/// # See also +/// +/// [open] +/// +pub fn close(fildes: c_int) -> c_int { + return unsafe { libc::close(fildes) }; +}