diff --git a/src/header/fmtmsg/cbindgen.toml b/src/header/fmtmsg/cbindgen.toml new file mode 100644 index 0000000000..981a1cc266 --- /dev/null +++ b/src/header/fmtmsg/cbindgen.toml @@ -0,0 +1,9 @@ +sys_includes = [] +include_guard = "_RELIBC_FMTMSG_H" +language = "C" +style = "Tag" +no_includes = true +cpp_compat = true + +[enum] +prefix_with_name = true \ No newline at end of file diff --git a/src/header/fmtmsg/mod.rs b/src/header/fmtmsg/mod.rs new file mode 100644 index 0000000000..d1282458c5 --- /dev/null +++ b/src/header/fmtmsg/mod.rs @@ -0,0 +1,167 @@ +//! `fmtmsg.h` implementation. +//! +//! See . +//! Ported from Musl. +//! +//! See + +use crate::{ + header::{ + fcntl::open, + pthread::{PTHREAD_CANCEL_DISABLE, pthread_setcancelstate}, + stdio::dprintf, + stdlib::getenv, + string::strchr, + unistd::close, + }, + platform::types::{c_char, c_int, c_long}, +}; +pub const MM_NULLSEV: c_int = 0; +pub const MM_HALT: c_int = 1; +pub const MM_ERROR: c_int = 2; +pub const MM_WARNING: c_int = 3; +pub const MM_INFO: c_int = 4; +pub const MM_NOMSG: c_int = 1; +pub const MM_NOTOK: c_int = -1; +// c_long because classification is a c_long +pub const MM_PRINT: c_long = 256; +// c_long because classification is a c_long +pub const MM_CONSOLE: c_long = 512; +pub const MM_NOCON: c_int = 4; +#[cfg(target_os = "linux")] +pub const O_WRONLY: c_int = 0x0001; +#[cfg(target_os = "redox")] +//Take from `src/header/fcntl/redox.rs` +pub const O_WRONLY: c_int = 0x0002_0000; + +/* + * If lstr is the first part of bstr, check that the next char in bstr + * is either \0 or ':' + */ +unsafe fn strcolcmp(mut lstr: *const c_char, mut bstr: *const c_char) -> c_int { + unsafe { + // We already know lstr and bstr are non-null + while *lstr != 0 && *bstr != 0 && (*lstr == *bstr) { + lstr = lstr.add(1); + bstr = bstr.add(1); + } + if *lstr != 0 || (*bstr != 0 && *bstr != b':' as c_char) { + 1 + } else { + 0 + } + } +} +/// See +#[unsafe(no_mangle)] +pub unsafe extern "C" fn fmtmsg( + classification: c_long, + label: *const c_char, + severity: c_int, + text: *const c_char, + action: *const c_char, + tag: *const c_char, +) -> c_int { + let mut ret: c_int = 0; + let mut verb: c_int = 0; + let mut i: usize; + let consolefd: c_int; + let mut cs: c_int = 0; + unsafe { + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &mut cs as *mut c_int); + } + let mut cmsg = unsafe { getenv(c"MSGVERB".as_ptr()) }; + let errstring: *const c_char = match severity { + MM_HALT => c"HALT: ".as_ptr(), + MM_ERROR => c"ERROR: ".as_ptr(), + MM_WARNING => c"WARNING: ".as_ptr(), + MM_INFO => c"INFO: ".as_ptr(), + _ => MM_NULLSEV as _, // Default + }; + let msgs: [*const c_char; 6] = [ + c"label".as_ptr(), + c"severity".as_ptr(), + c"text".as_ptr(), + c"action".as_ptr(), + c"tag".as_ptr(), + core::ptr::null(), + ]; + if (classification & MM_CONSOLE) != 0 { + //TODO: rusty + unsafe { + consolefd = open(c"/dev/console".as_ptr(), O_WRONLY); + if consolefd < 0 { + ret = MM_NOCON; + } else { + #[rustfmt::skip] + let status = dprintf( + consolefd, + c"%s%s%s%s%s%s%s%s\n".as_ptr(), + if !label.is_null() {label } else { c"".as_ptr()}, + if !label.is_null() {c": ".as_ptr()} else { c"".as_ptr() }, + if severity != 0 { errstring } else { c"".as_ptr()}, + if !text.is_null() { text } else { c"".as_ptr() }, + if !action.is_null() {c"\nTO FIX: ".as_ptr()} else { c"".as_ptr()}, + if !action.is_null() {action} else {c"".as_ptr()}, + if !action.is_null() {c" ".as_ptr()} else { c"".as_ptr()}, + if !tag.is_null() { tag } else { c"".as_ptr() }, + ); + if status < 1 { + ret = MM_NOCON; + } + close(consolefd); + } + } + } + if (classification & MM_PRINT) != 0 { + unsafe { + while !cmsg.is_null() && *cmsg != 0 { + i = 0; + loop { + if msgs[i].is_null() { + break; + } + if strcolcmp(msgs[i], cmsg) == 0 { + break; + } + i += 1; + } + if msgs[i].is_null() { + //ignore MSGVERB-unrecognized component + verb = 0xFF; + break; + } else { + verb |= 1 << i; + cmsg = strchr(cmsg, b':' as _); + if !cmsg.is_null() { + cmsg = cmsg.add(1); + } + } + } + if verb == 0 { + verb = 0xFF; + } + #[rustfmt::skip] + let status = dprintf(2,c"%s%s%s%s%s%s%s%s\n".as_ptr(), + if (verb & 1 != 0) && !label.is_null() { label } else { c"".as_ptr() }, + if (verb & 1 != 0) && !label.is_null() { c": ".as_ptr() } else { c"".as_ptr() }, + if (verb & 2 != 0) && severity != 0 { errstring } else { c"".as_ptr() }, + if (verb & 4 != 0) && !text.is_null() { text } else { c"".as_ptr() }, + if (verb & 8 != 0) && !action.is_null() { c"\nTO FIX: ".as_ptr() } else { c"".as_ptr() }, + if (verb & 8 != 0) && !action.is_null() { action } else { c"".as_ptr() }, + if (verb & 8 != 0) && !action.is_null() { c" ".as_ptr() } else { c"".as_ptr() }, + if (verb & 16 != 0) && !tag.is_null() { tag } else { c"".as_ptr() }, + ); + if status < 1 { + ret |= MM_NOMSG; + } + } + } + if ret & (MM_NOCON | MM_NOMSG) == (MM_NOCON | MM_NOMSG) { + ret = MM_NOTOK; + } + unsafe { + pthread_setcancelstate(cs, 0 as _); + } + ret +} diff --git a/src/header/mod.rs b/src/header/mod.rs index 4bdb6b19e8..1f0e6f03b6 100644 --- a/src/header/mod.rs +++ b/src/header/mod.rs @@ -32,7 +32,7 @@ pub mod err; pub mod errno; pub mod fcntl; pub mod float; -// TODO: fmtmsg.h +pub mod fmtmsg; pub mod fnmatch; // TODO: ftw.h pub mod getopt; diff --git a/tests/Makefile.tests.mk b/tests/Makefile.tests.mk index 239e259207..7c205f07a7 100644 --- a/tests/Makefile.tests.mk +++ b/tests/Makefile.tests.mk @@ -208,7 +208,8 @@ EXPECT_NAMES=\ wctype/towlower \ wctype/towupper \ mknod \ - mknodat + mknodat \ + fmtmsg # Binaries that may generate varied output VARIED_NAMES=\ diff --git a/tests/expected/fmtmsg.stderr b/tests/expected/fmtmsg.stderr new file mode 100644 index 0000000000..59a82a835a --- /dev/null +++ b/tests/expected/fmtmsg.stderr @@ -0,0 +1,4 @@ +XSI:cat: ERROR: illegal option +TO FIX: refer to cat in user's reference manual XSI:cat:001 +XSI:cat: ERROR: illegal option +TO FIX: refer to cat in user's reference manual XSI:cat:001 diff --git a/tests/expected/fmtmsg.stdout b/tests/expected/fmtmsg.stdout new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/fmtmsg.c b/tests/fmtmsg.c new file mode 100644 index 0000000000..7b6d730ef3 --- /dev/null +++ b/tests/fmtmsg.c @@ -0,0 +1,39 @@ +#include +#include +int main() { + /* Returned Value + * MM_OK: 0 - The function succeeded. + * MM_NOCON: 4 - The function was unable to generate a console message, but otherwise succeeded. + * MM_NOMSG: 1 - The function was unable to generate a message on standard error, but otherwise succeeded. + * MM_NOTOK: -1 - The function failed completely. + * + * Take from: https://www.ibm.com/docs/en/zos/3.1.0?topic=functions-fmtmsg-display-message-in-specified-format + */ + int mm_ok = fmtmsg(MM_PRINT, "XSI:cat", MM_ERROR, "illegal option", + "refer to cat in user's reference manual", "XSI:cat:001"); + if (mm_ok != 0) { + return -1; + } + // Thats return 0 in glibc but return 4 in musl + // So we expect 4 here + int mm_nocon = fmtmsg(MM_PRINT | MM_CONSOLE, "XSI:cat", MM_ERROR, "illegal option", + "refer to cat in user's reference manual", "XSI:cat:001"); + if (mm_nocon != MM_NOCON) { + return -1; + } + //We close stderr to test. + close(STDERR_FILENO); + int mm_nomsg = fmtmsg(MM_PRINT , "XSI:cat", MM_ERROR, "illegal option", + "refer to cat in user's reference manual", "XSI:cat:001"); + if (mm_nomsg != MM_NOMSG) { + return -1; + } + // Thats return 1 in glibc but return -1 in musl + // Soo we expect -1 here + int mm_notok = fmtmsg(MM_PRINT | MM_CONSOLE, "XSI:cat", MM_ERROR, "illegal option", + "refer to cat in user's reference manual", "XSI:cat:001"); + if (mm_notok != MM_NOTOK) { + return -1; + } + return 0; +} \ No newline at end of file