Add fmtmsg

This commit is contained in:
Mustafa öz
2026-05-01 05:00:11 +00:00
committed by Mathew John Roberts
parent c1912066a1
commit 03d56f4887
7 changed files with 222 additions and 2 deletions
+9
View File
@@ -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
+167
View File
@@ -0,0 +1,167 @@
//! `fmtmsg.h` implementation.
//!
//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fmtmsg.h.html>.
//! Ported from Musl.
//!
//! See <https://github.com/kraj/musl/blob/kraj/master/src/misc/fmtmsg.c>
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 <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fmtmsg.h.html>
#[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
}
+1 -1
View File
@@ -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;
+2 -1
View File
@@ -208,7 +208,8 @@ EXPECT_NAMES=\
wctype/towlower \
wctype/towupper \
mknod \
mknodat
mknodat \
fmtmsg
# Binaries that may generate varied output
VARIED_NAMES=\
+4
View File
@@ -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
View File
+39
View File
@@ -0,0 +1,39 @@
#include <fmtmsg.h>
#include <unistd.h>
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;
}