Browse Source

Implement timer* macros, and GNU's getopt_long

jD91mZM2 6 years ago
parent
commit
9d56ce42c6

+ 23 - 0
include/bits/sys/time.h

@@ -0,0 +1,23 @@
+#ifndef _BITS_SYS_TIME
+#define _BITS_SYS_TIME
+
+#define timeradd(x,y,res) (void) (\
+        (res)->tv_sec = (x)->tv_sec + (y)->tv_sec + ((x)->tv_usec + (y)->tv_usec / 1000000), \
+        (res)->tv_usec = ((x)->tv_usec + (y)->tv_usec) % 1000000 \
+    )
+#define timersub(x,y,res) (void) ( \
+        (res)->tv_sec = (x)->tv_sec - (y)->tv_sec, \
+        (res)->tv_usec = ((x)->tv_usec - (y)->tv_usec), \
+        ((res)->tv_usec < 0) && ((res)->tv_sec -= 1, (res)->tv_usec += 1000000) \
+    )
+#define timerclear(t) (void) ( \
+        (t)->tv_sec = 0, \
+        (t)->tv_usec = 0 \
+    )
+#define timerisset(t) (t)->tv_sec || (t)->tv_usec
+#define timercmp(x,y,op) (x)->tv_sec == (y)->tv_sec ? \
+    (x)->tv_usec op (y)->tv_usec \
+    : \
+    (x)->tv_sec op (y)->tv_sec
+
+#endif

+ 7 - 0
src/header/getopt/cbindgen.toml

@@ -0,0 +1,7 @@
+sys_includes = ["unistd.h"]
+include_guard = "_GETOPT_H"
+language = "C"
+style = "Tag"
+
+[enum]
+prefix_with_name = true

+ 201 - 0
src/header/getopt/mod.rs

@@ -0,0 +1,201 @@
+//! getopt implementation for relibc
+
+use core::ptr;
+use header::{stdio, string};
+use header::unistd::{optarg, optind, opterr, optopt};
+use platform::types::*;
+
+static mut CURRENT_OPT: *mut c_char = ptr::null_mut();
+
+pub const no_argument:       c_int = 0;
+pub const required_argument: c_int = 1;
+pub const optional_argument: c_int = 2;
+
+#[repr(C)]
+pub struct option {
+    name: *const c_char,
+    has_arg: c_int,
+    flag: *mut c_int,
+    val: c_int
+}
+
+#[no_mangle]
+pub unsafe extern "C" fn getopt_long(
+    argc: c_int,
+    argv: *const *mut c_char,
+    optstring: *const c_char,
+    longopts: *const option,
+    longindex: *mut c_int
+) -> c_int {
+    // if optarg is not set, we still don't want the previous value leaking
+    optarg = ptr::null_mut();
+
+    if CURRENT_OPT.is_null() || *CURRENT_OPT == 0 {
+        if optind >= argc {
+            -1
+        } else {
+            let current_arg = *argv.offset(optind as isize);
+            if current_arg.is_null()
+                || *current_arg != b'-' as c_char
+                || *current_arg.offset(1) == 0
+            {
+                -1
+            } else if string::strcmp(current_arg, b"--\0".as_ptr() as _) == 0 {
+                optind += 1;
+                -1
+            } else {
+                // remove the '-'
+                let current_arg = current_arg.offset(1);
+
+                if *current_arg == b'-' as c_char && !longopts.is_null() {
+                    let current_arg = current_arg.offset(1);
+                    // is a long option
+                    for i in 0.. {
+                        let opt = &*longopts.offset(i);
+                        if opt.name.is_null() {
+                            break;
+                        }
+
+                        let mut end = 0;
+                        while { let c = *current_arg.offset(end); c != 0 && c != b'=' as c_char } {
+                            end += 1;
+                        }
+
+                        if string::strncmp(current_arg, opt.name, end as size_t) == 0 {
+                            optind += 1;
+                            *longindex = i as c_int;
+
+                            if opt.has_arg == optional_argument {
+                                if *current_arg.offset(end) == b'=' as c_char {
+                                    optarg = current_arg.offset(end + 1);
+                                }
+                            } else if opt.has_arg == required_argument {
+                                if *current_arg.offset(end) == b'=' as c_char {
+                                    optarg = current_arg.offset(end + 1);
+                                } else if optind < argc {
+                                    optarg = *argv.offset(optind as isize);
+                                    optind += 1;
+                                } else {
+                                    if *optstring == b':' as c_char {
+                                        return b':' as c_int;
+                                    } else {
+                                        stdio::fputs(*argv as _, &mut *stdio::stderr);
+                                        stdio::fputs(": option '--\0".as_ptr() as _, &mut *stdio::stderr);
+                                        stdio::fputs(current_arg, &mut *stdio::stderr);
+                                        stdio::fputs("' requires an argument\n".as_ptr() as _, &mut *stdio::stderr);
+                                        return b'?' as c_int;
+                                    }
+                                }
+                            }
+
+                            if opt.flag.is_null() {
+                                return opt.val;
+                            } else {
+                                *opt.flag = opt.val;
+                                return 0;
+                            }
+                        }
+                    }
+                }
+
+                parse_arg(argc, argv, current_arg, optstring)
+            }
+        }
+    } else {
+        parse_arg(argc, argv, CURRENT_OPT, optstring)
+    }
+}
+
+unsafe fn parse_arg(
+    argc: c_int,
+    argv: *const *mut c_char,
+    current_arg: *mut c_char,
+    optstring: *const c_char
+) -> c_int {
+    let update_current_opt = || {
+        CURRENT_OPT = current_arg.offset(1);
+        if *CURRENT_OPT == 0 {
+            optind += 1;
+        }
+    };
+
+    let print_error = |desc: &[u8]| {
+        // NOTE: we don't use fprintf to get around the usage of va_list
+        stdio::fputs(*argv as _, &mut *stdio::stderr);
+        stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
+        stdio::fputc(*current_arg as _, &mut *stdio::stderr);
+        stdio::fputc(b'\n' as _, &mut *stdio::stderr);
+    };
+
+    match find_option(*current_arg, optstring) {
+        Some(GetoptOption::Flag) => {
+            update_current_opt();
+
+            *current_arg as c_int
+        }
+        Some(GetoptOption::OptArg) => {
+            CURRENT_OPT = b"\0".as_ptr() as _;
+            if *current_arg.offset(1) == 0 {
+                optind += 2;
+                if optind > argc {
+                    CURRENT_OPT = ptr::null_mut();
+
+                    optopt = *current_arg as c_int;
+                    let errch = if *optstring == b':' as c_char {
+                        b':'
+                    } else {
+                        if opterr != 0 {
+                            print_error(b": option requries an argument -- \0");
+                        }
+
+                        b'?'
+                    };
+                    errch as c_int
+                } else {
+                    optarg = *argv.offset(optind as isize - 1);
+
+                    *current_arg as c_int
+                }
+            } else {
+                optarg = current_arg.offset(1);
+                optind += 1;
+
+                *current_arg as c_int
+            }
+        }
+        None => {
+            // couldn't find the given option in optstring
+            if opterr != 0 {
+                print_error(b": illegal option -- \0");
+            }
+
+            update_current_opt();
+
+            optopt = *current_arg as c_int;
+            b'?' as c_int
+        }
+    }
+}
+
+enum GetoptOption {
+    Flag,
+    OptArg,
+}
+
+unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option<GetoptOption> {
+    let mut i = 0;
+
+    while *optstring.offset(i) != 0 {
+        if *optstring.offset(i) == ch {
+            let result = if *optstring.offset(i + 1) == b':' as c_char {
+                GetoptOption::OptArg
+            } else {
+                GetoptOption::Flag
+            };
+            return Some(result);
+        }
+        i += 1;
+    }
+
+    None
+}

+ 1 - 0
src/header/mod.rs

@@ -7,6 +7,7 @@ pub mod fcntl;
 pub mod fenv;
 pub mod float;
 pub mod fnmatch;
+pub mod getopt;
 pub mod grp;
 pub mod inttypes;
 pub mod locale;

+ 1 - 0
src/header/sys_time/cbindgen.toml

@@ -1,6 +1,7 @@
 sys_includes = ["sys/types.h"]
 include_guard = "_SYS_TIME_H"
 language = "C"
+trailer = "#include <bits/sys/time.h>"
 
 # WORKAROUND:
 # Timeval is used by another header, and cbindgen doesn't prefix that with `struct` :|

+ 2 - 120
src/header/unistd/getopt.rs

@@ -2,7 +2,7 @@
 
 use core::ptr;
 
-use header::{stdio, string};
+use header::{getopt, stdio, string};
 use platform::types::*;
 
 #[allow(non_upper_case_globals)]
@@ -21,129 +21,11 @@ pub static mut opterr: c_int = 1;
 #[no_mangle]
 pub static mut optopt: c_int = -1;
 
-static mut CURRENT_OPT: *mut c_char = ptr::null_mut();
-
 #[no_mangle]
 pub unsafe extern "C" fn getopt(
     argc: c_int,
     argv: *const *mut c_char,
     optstring: *const c_char,
 ) -> c_int {
-    if CURRENT_OPT.is_null() || *CURRENT_OPT == 0 {
-        if optind >= argc {
-            -1
-        } else {
-            let current_arg = *argv.offset(optind as isize);
-            if current_arg.is_null()
-                || *current_arg != b'-' as c_char
-                || string::strcmp(current_arg, b"-\0".as_ptr() as _) == 0
-            {
-                -1
-            } else if string::strcmp(current_arg, b"--\0".as_ptr() as _) == 0 {
-                optind += 1;
-                -1
-            } else {
-                // remove the '-'
-                let current_arg = current_arg.offset(1);
-
-                parse_arg(argc, argv, current_arg, optstring)
-            }
-        }
-    } else {
-        parse_arg(argc, argv, CURRENT_OPT, optstring)
-    }
-}
-
-unsafe fn parse_arg(
-    argc: c_int,
-    argv: *const *mut c_char,
-    current_arg: *mut c_char,
-    optstring: *const c_char,
-) -> c_int {
-    let update_current_opt = || {
-        CURRENT_OPT = current_arg.offset(1);
-        if *CURRENT_OPT == 0 {
-            optind += 1;
-        }
-    };
-
-    let print_error = |desc: &[u8]| {
-        // NOTE: we don't use fprintf to get around the usage of va_list
-        stdio::fputs(*argv as _, &mut *stdio::stderr);
-        stdio::fputs(desc.as_ptr() as _, &mut *stdio::stderr);
-        stdio::fputc(*current_arg as _, &mut *stdio::stderr);
-        stdio::fputc(b'\n' as _, &mut *stdio::stderr);
-    };
-
-    match find_option(*current_arg, optstring) {
-        Some(GetoptOption::Flag) => {
-            update_current_opt();
-
-            *current_arg as c_int
-        }
-        Some(GetoptOption::OptArg) => {
-            CURRENT_OPT = b"\0".as_ptr() as _;
-            if *current_arg.offset(1) == 0 {
-                optind += 2;
-                if optind > argc {
-                    CURRENT_OPT = ptr::null_mut();
-
-                    optopt = *current_arg as c_int;
-                    let errch = if *optstring == b':' as c_char {
-                        b':'
-                    } else {
-                        if opterr != 0 {
-                            print_error(b": option requries an argument -- \0");
-                        }
-
-                        b'?'
-                    };
-                    errch as c_int
-                } else {
-                    optarg = *argv.offset(optind as isize - 1);
-
-                    *current_arg as c_int
-                }
-            } else {
-                optarg = current_arg.offset(1);
-                optind += 1;
-
-                *current_arg as c_int
-            }
-        }
-        None => {
-            // couldn't find the given option in optstring
-            if opterr != 0 {
-                print_error(b": illegal option -- \0");
-            }
-
-            update_current_opt();
-
-            optopt = *current_arg as _;
-            b'?' as c_int
-        }
-    }
-}
-
-enum GetoptOption {
-    Flag,
-    OptArg,
-}
-
-unsafe fn find_option(ch: c_char, optstring: *const c_char) -> Option<GetoptOption> {
-    let mut i = 0;
-
-    while *optstring.offset(i) != 0 {
-        if *optstring.offset(i) == ch {
-            let result = if *optstring.offset(i + 1) == b':' as c_char {
-                GetoptOption::OptArg
-            } else {
-                GetoptOption::Flag
-            };
-            return Some(result);
-        }
-        i += 1;
-    }
-
-    None
+    getopt::getopt_long(argc, argv, optstring, ptr::null(), ptr::null_mut())
 }

+ 2 - 0
tests/Makefile

@@ -53,6 +53,7 @@ EXPECT_BINS=\
 	time/asctime \
 	time/gmtime \
 	time/localtime \
+	time/macros \
 	time/mktime \
 	time/strftime \
 	time/time \
@@ -64,6 +65,7 @@ EXPECT_BINS=\
 	unistd/fsync \
 	unistd/ftruncate \
 	unistd/getopt \
+	unistd/getopt_long \
 	unistd/isatty \
 	unistd/pipe \
 	unistd/rmdir \

+ 0 - 0
tests/expected/time/macros.stderr


+ 0 - 0
tests/expected/time/macros.stdout


+ 0 - 0
tests/expected/unistd/getopt_long.stderr


+ 20 - 0
tests/expected/unistd/getopt_long.stdout

@@ -0,0 +1,20 @@
+--- Running: test --test0 -a
+getopt_long returned 1, argument test0=NULL
+Option -a with value NULL
+--- Running: test --test1 -a
+getopt_long returned 0, set flag to 2, argument test1=NULL
+Option -a with value NULL
+--- Running: test --test2 -a
+getopt_long returned 3, argument test2=NULL
+Option -a with value NULL
+--- Running: test --test2=arg -a
+getopt_long returned 3, argument test2=arg
+Option -a with value NULL
+--- Running: test --test3 -a
+getopt_long returned 4, argument test3=-a
+--- Running: test --test3=arg -a
+getopt_long returned 4, argument test3=arg
+Option -a with value NULL
+--- Running: test --test3 arg -a
+getopt_long returned 4, argument test3=arg
+Option -a with value NULL

+ 26 - 0
tests/time/macros.c

@@ -0,0 +1,26 @@
+#include <assert.h>
+#include <sys/time.h>
+
+int main() {
+    struct timeval x = { .tv_sec = 0, .tv_usec = 15 };
+    struct timeval y = { .tv_sec = 0, .tv_usec = 0 };
+    struct timeval one = { .tv_sec = 0, .tv_usec = 1 };
+    struct timeval max_usec = { .tv_sec = 0, .tv_usec = 999999 };
+
+    assert(!timerisset(&y));
+    assert(timerisset(&x));
+    timerclear(&x);
+    assert(!timerisset(&x));
+
+    assert(timercmp(&x, &y, ==));
+    timeradd(&y, &one, &y);
+    assert(!timercmp(&x, &y, ==));
+    assert(timercmp(&x, &y, <));
+
+    timeradd(&y, &max_usec, &y);
+    assert(y.tv_sec == 1);
+    assert(y.tv_usec == 0);
+    timersub(&y, &one, &y);
+    assert(y.tv_sec == 0);
+    assert(y.tv_usec == 999999);
+}

+ 63 - 0
tests/unistd/getopt_long.c

@@ -0,0 +1,63 @@
+#include <getopt.h>
+#include <stdio.h>
+
+#define RUN(...) { \
+        optind = 1; \
+        optarg = NULL; \
+        opterr = 1; \
+        optopt = -1; \
+        char *args_arr[] = { __VA_ARGS__ }; \
+        runner(sizeof(args_arr) / sizeof(char*), args_arr); \
+    }
+
+void runner(int argc, char *argv[]) {
+    printf("--- Running:");
+    for (int i = 0; i < argc; i += 1) {
+        printf(" %s", argv[i]);
+    }
+    puts("");
+
+    static int flag = 0;
+
+    static struct option long_options[] = {
+        {"test0", no_argument, NULL,  1},
+        {"test1", no_argument, &flag, 2},
+        {"test2", optional_argument, NULL, 3},
+        {"test3", required_argument, NULL, 4},
+        {NULL, 0, NULL, 5},
+    };
+
+    int option_index = 0;
+    char c;
+    while((c = getopt_long(argc, argv, ":a", long_options, &option_index)) != -1) {
+        switch(c) {
+            case 'a':
+                printf("Option -a with value %s\n", optarg);
+                break;
+            case ':':
+                printf("unrecognized argument: -%c\n", optopt);
+                break;
+            case '?':
+                printf("error: -%c\n", optopt);
+                break;
+            default:
+                printf("getopt_long returned %d, ", c);
+                if (flag) {
+                    printf("set flag to %d, ", flag);
+                    flag = 0;
+                }
+                printf("argument %s=%s\n", long_options[option_index].name, optarg);
+                break;
+        }
+    }
+}
+
+int main(int argc, const char *argv[]) {
+    RUN("test", "--test0", "-a");
+    RUN("test", "--test1", "-a");
+    RUN("test", "--test2", "-a");
+    RUN("test", "--test2=arg", "-a");
+    RUN("test", "--test3", "-a");
+    RUN("test", "--test3=arg", "-a");
+    RUN("test", "--test3", "arg", "-a");
+}