Implement timer* macros, and GNU's getopt_long
This commit is contained in:
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
sys_includes = ["unistd.h"]
|
||||
include_guard = "_GETOPT_H"
|
||||
language = "C"
|
||||
style = "Tag"
|
||||
|
||||
[enum]
|
||||
prefix_with_name = true
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,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
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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 +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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
Reference in New Issue
Block a user