Skip to content

Commit 91b8666

Browse files
fix(argp): honor exit and version globals (bd-1mjtdi)
1 parent 716cdec commit 91b8666

2 files changed

Lines changed: 90 additions & 6 deletions

File tree

crates/frankenlibc-abi/src/unistd_abi.rs

Lines changed: 68 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22485,6 +22485,7 @@ const ARGP_HELP_POST_DOC: c_uint = 0x20;
2248522485
const ARGP_HELP_BUG_ADDR: c_uint = 0x40;
2248622486
const ARGP_HELP_EXIT_ERR: c_uint = 0x100;
2248722487
const ARGP_HELP_EXIT_OK: c_uint = 0x200;
22488+
const ARGP_NO_EXIT: c_uint = 0x20;
2248822489
const ARGP_TEXT_SCAN_LIMIT: usize = 16 * 1024;
2248922490
const ARGP_HELP_STATE_NON_RENDERING_FLAGS: c_uint =
2249022491
ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR | ARGP_HELP_EXIT_OK;
@@ -22646,18 +22647,56 @@ unsafe fn argp_write_diagnostic(state: *mut c_void, message: &[u8], errnum: c_in
2264622647
unsafe { argp_write_bytes(stream, &line) }
2264722648
}
2264822649

22650+
#[inline]
22651+
unsafe fn argp_exit_unless_suppressed(state: &ArgpStateHeader, status: c_int) {
22652+
if state.flags & ARGP_NO_EXIT == 0 {
22653+
unsafe { crate::stdlib_abi::exit(status) };
22654+
}
22655+
}
22656+
22657+
unsafe fn argp_write_version(stream: *mut libc::FILE, state: *mut c_void) -> bool {
22658+
let hook = unsafe { crate::glibc_internal_abi::argp_program_version_hook };
22659+
if !hook.is_null() {
22660+
type ArgpVersionHook = unsafe extern "C" fn(*mut libc::FILE, *mut c_void);
22661+
// SAFETY: `argp_program_version_hook` is the public GNU argp callback
22662+
// slot. A non-null value is required by that ABI to be a function with
22663+
// this exact signature.
22664+
let hook = unsafe { core::mem::transmute::<*mut c_void, ArgpVersionHook>(hook) };
22665+
unsafe { hook(stream, state) };
22666+
return true;
22667+
}
22668+
22669+
let version = unsafe { crate::glibc_internal_abi::argp_program_version };
22670+
let Some(version) = (unsafe { argp_read_text(version) }) else {
22671+
return false;
22672+
};
22673+
unsafe { argp_write_text_line(stream, &version) }
22674+
}
22675+
22676+
unsafe fn argp_parse_version_requested(argc: c_int, argv: *mut *mut c_char) -> bool {
22677+
for index in 1..argc as usize {
22678+
let arg = unsafe { *argv.add(index) };
22679+
if let Some(arg) = unsafe { argp_read_text(arg.cast_const()) }
22680+
&& arg == b"--version"
22681+
{
22682+
return true;
22683+
}
22684+
}
22685+
false
22686+
}
22687+
2264922688
/// `argp_parse` — parse arguments using argp framework.
2265022689
///
2265122690
/// Native phase-1 support handles the common zeroed `struct argp` case as a
22652-
/// successful no-op parse, matching glibc's behavior for empty parsers.
22691+
/// successful no-op parse and its GNU `--version` built-in.
2265322692
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
2265422693
pub unsafe extern "C" fn argp_parse(
2265522694
argp: *const c_void,
2265622695
argc: c_int,
2265722696
argv: *mut *mut c_char,
22658-
_flags: libc::c_uint,
22697+
flags: libc::c_uint,
2265922698
arg_index: *mut c_int,
22660-
_input: *mut c_void,
22699+
input: *mut c_void,
2266122700
) -> c_int {
2266222701
if argp.is_null() || argc < 0 || (argc > 0 && argv.is_null()) {
2266322702
unsafe { set_abi_errno(libc::EINVAL) };
@@ -22666,6 +22705,22 @@ pub unsafe extern "C" fn argp_parse(
2266622705

2266722706
let header = unsafe { &*(argp as *const ArgpHeader) };
2266822707
if header.is_empty() {
22708+
let version = unsafe { crate::glibc_internal_abi::argp_program_version };
22709+
let version_hook = unsafe { crate::glibc_internal_abi::argp_program_version_hook };
22710+
let version_available = !version.is_null() || !version_hook.is_null();
22711+
if version_available && unsafe { argp_parse_version_requested(argc, argv) } {
22712+
let stream = unsafe { crate::stdio_abi::stdout }.cast::<libc::FILE>();
22713+
if !unsafe { argp_write_version(stream, input) } {
22714+
unsafe { set_abi_errno(libc::EIO) };
22715+
}
22716+
if !arg_index.is_null() {
22717+
unsafe { *arg_index = argc };
22718+
}
22719+
if flags & ARGP_NO_EXIT == 0 {
22720+
unsafe { crate::stdlib_abi::exit(0) };
22721+
}
22722+
return 0;
22723+
}
2266922724
if !arg_index.is_null() {
2267022725
unsafe { *arg_index = argc.min(1) };
2267122726
}
@@ -22715,7 +22770,7 @@ pub unsafe extern "C" fn argp_usage(state: *mut c_void) {
2271522770
unsafe { argp_state_help(state, core::ptr::null_mut(), ARGP_HELP_STD_USAGE_PHASE1) };
2271622771
}
2271722772

22718-
/// `argp_error` — report a bounded formatted parsing diagnostic to state error stream.
22773+
/// `argp_error` — report a bounded formatted parsing diagnostic and exit unless suppressed.
2271922774
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
2272022775
pub unsafe extern "C" fn argp_error(state: *mut c_void, fmt: *const c_char, mut args: ...) {
2272122776
if unsafe { argp_diagnostic_stream(state) }.is_null() {
@@ -22734,13 +22789,16 @@ pub unsafe extern "C" fn argp_error(state: *mut c_void, fmt: *const c_char, mut
2273422789
if !unsafe { argp_write_diagnostic(state, &rendered, 0) } {
2273522790
unsafe { set_abi_errno(libc::EIO) };
2273622791
}
22792+
let state = unsafe { &*(state as *const ArgpStateHeader) };
22793+
let status = unsafe { crate::glibc_internal_abi::argp_err_exit_status };
22794+
unsafe { argp_exit_unless_suppressed(state, status) };
2273722795
}
2273822796

22739-
/// `argp_failure` — report a bounded formatted parsing failure diagnostic.
22797+
/// `argp_failure` — report a bounded formatted parsing failure diagnostic and exit when requested.
2274022798
#[cfg_attr(not(debug_assertions), unsafe(no_mangle))]
2274122799
pub unsafe extern "C" fn argp_failure(
2274222800
state: *mut c_void,
22743-
_status: c_int,
22801+
status: c_int,
2274422802
errnum: c_int,
2274522803
fmt: *const c_char,
2274622804
mut args: ...
@@ -22761,6 +22819,10 @@ pub unsafe extern "C" fn argp_failure(
2276122819
if !unsafe { argp_write_diagnostic(state, &rendered, errnum) } {
2276222820
unsafe { set_abi_errno(libc::EIO) };
2276322821
}
22822+
if status != 0 {
22823+
let state = unsafe { &*(state as *const ArgpStateHeader) };
22824+
unsafe { argp_exit_unless_suppressed(state, status) };
22825+
}
2276422826
}
2276522827

2276622828
/// `argp_state_help` — print bounded phase-1 help from state.

crates/frankenlibc-abi/tests/unistd_abi_test.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3335,6 +3335,28 @@ fn abi_argp_failure_exits_with_status_in_child() {
33353335
);
33363336
}
33373337

3338+
#[test]
3339+
fn abi_argp_failure_honors_no_exit_flag_in_child() {
3340+
let name = CString::new("failure-demo").unwrap();
3341+
let fmt = CString::new("failed %d").unwrap();
3342+
3343+
let output = capture_argp_child_exit(127, |stream| unsafe {
3344+
let mut state = fixture_argp_state(std::ptr::null(), name.as_ptr().cast_mut());
3345+
state.flags = ARGP_NO_EXIT;
3346+
state.err_stream = stream;
3347+
frankenlibc_abi::unistd_abi::argp_failure(
3348+
(&mut state as *mut FixtureArgpState).cast(),
3349+
74,
3350+
libc::EINVAL,
3351+
fmt.as_ptr(),
3352+
9 as c_int,
3353+
);
3354+
})
3355+
.unwrap();
3356+
3357+
assert_eq!(output, "failure-demo: failed 9: Invalid argument\n");
3358+
}
3359+
33383360
#[test]
33393361
fn abi_argp_error_and_failure_null_state_preserve_fixture_noop_contract() {
33403362
let fmt = CString::new("ignored %d").unwrap();

0 commit comments

Comments
 (0)