Skip to main content

relibc/header/fmtmsg/
mod.rs

1//! `fmtmsg.h` implementation.
2//!
3//! See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fmtmsg.h.html>.
4//! Ported from Musl.
5//!
6//! See <https://github.com/kraj/musl/blob/kraj/master/src/misc/fmtmsg.c>
7
8use crate::{
9    byte_literal::ByteLiteral,
10    header::{
11        fcntl::{O_WRONLY, open},
12        pthread::{PTHREAD_CANCEL_DISABLE, pthread_setcancelstate},
13        stdio::dprintf,
14        stdlib::getenv,
15        string::strchr,
16        unistd::close,
17    },
18    platform::types::{c_char, c_int, c_long},
19};
20
21// constant values below taken from musl
22
23/// Source of the condition is hardware.
24pub const MM_HARD: c_int = 1;
25/// Source of the condition is software.
26pub const MM_SOFT: c_int = 2;
27/// Source of the condition is firmware.
28pub const MM_FIRM: c_int = 4;
29
30/// Condition detected by application.
31pub const MM_APPL: c_int = 8;
32/// Condition detected by utility.
33pub const MM_UTIL: c_int = 16;
34/// Condition detected by operating system.
35pub const MM_OPSYS: c_int = 32;
36
37/// Recoverable error.
38pub const MM_RECOVER: c_int = 64;
39/// Non-recoverable error.
40pub const MM_NRECOV: c_int = 128;
41
42/// Display message on standard error.
43pub const MM_PRINT: c_long = 256;
44/// Display message on system console.
45pub const MM_CONSOLE: c_long = 512;
46
47/// Class argument.
48pub const MM_NULLMC: c_long = 0;
49
50/// Error causing application to halt.
51pub const MM_HALT: c_int = 1;
52/// Application has encountered a non-fatal fault.
53pub const MM_ERROR: c_int = 2;
54/// Application has detected unusual non-error condition.
55pub const MM_WARNING: c_int = 3;
56/// Informative message.
57pub const MM_INFO: c_int = 4;
58/// No severity level provided for the message.
59pub const MM_NOSEV: c_int = 0;
60
61/// The function succeeded.
62pub const MM_OK: c_int = 0;
63/// The function failed completely.
64pub const MM_NOTOK: c_int = -1;
65/// The function was unable to generate a message on standard error, but otherwise succeeded.
66pub const MM_NOMSG: c_int = 1;
67/// The function was unable to generate a console message, but otherwise succeeded.
68pub const MM_NOCON: c_int = 4;
69
70/// Severity argument.
71pub const MM_NULLSEV: c_int = 0;
72
73/// If lstr is the first part of bstr, check that the next char in bstr is either \0 or ':'
74unsafe fn strcolcmp(mut lstr: *const c_char, mut bstr: *const c_char) -> c_int {
75    unsafe {
76        // We already know lstr and bstr are non-null
77        while *lstr != 0 && *bstr != 0 && (*lstr == *bstr) {
78            lstr = lstr.add(1);
79            bstr = bstr.add(1);
80        }
81        if *lstr != 0 || (*bstr != 0 && *bstr != ByteLiteral::cast_cchar(b':')) {
82            1
83        } else {
84            0
85        }
86    }
87}
88
89/// See <https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/fmtmsg.h.html>
90#[unsafe(no_mangle)]
91pub unsafe extern "C" fn fmtmsg(
92    classification: c_long,
93    label: *const c_char,
94    severity: c_int,
95    text: *const c_char,
96    action: *const c_char,
97    tag: *const c_char,
98) -> c_int {
99    let mut ret: c_int = 0;
100    let mut verb: c_int = 0;
101    let mut i: usize;
102    let consolefd: c_int;
103    let mut cs: c_int = 0;
104    unsafe {
105        pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &raw mut cs);
106    }
107    let mut cmsg = unsafe { getenv(c"MSGVERB".as_ptr()) };
108    let errstring: *const c_char = match severity {
109        MM_HALT => c"HALT: ".as_ptr(),
110        MM_ERROR => c"ERROR: ".as_ptr(),
111        MM_WARNING => c"WARNING: ".as_ptr(),
112        MM_INFO => c"INFO: ".as_ptr(),
113        _ => MM_NULLSEV as _, // Default
114    };
115    let msgs: [*const c_char; 6] = [
116        c"label".as_ptr(),
117        c"severity".as_ptr(),
118        c"text".as_ptr(),
119        c"action".as_ptr(),
120        c"tag".as_ptr(),
121        core::ptr::null(),
122    ];
123    if (classification & MM_CONSOLE) != 0 {
124        //TODO: rusty
125        unsafe {
126            consolefd = open(c"/dev/console".as_ptr(), O_WRONLY);
127            if consolefd < 0 {
128                ret = MM_NOCON;
129            } else {
130                #[rustfmt::skip]
131                let status = dprintf(
132                    consolefd,
133                    c"%s%s%s%s%s%s%s%s\n".as_ptr(),
134                    if !label.is_null() {label } else { c"".as_ptr()},
135                    if !label.is_null() {c": ".as_ptr()} else {  c"".as_ptr() }, 
136                    if severity != 0 { errstring } else { c"".as_ptr()},
137                    if !text.is_null() { text } else { c"".as_ptr() },
138                    if !action.is_null() {c"\nTO FIX: ".as_ptr()} else { c"".as_ptr()},
139                    if !action.is_null() {action} else {c"".as_ptr()},
140                    if !action.is_null() {c" ".as_ptr()} else { c"".as_ptr()},
141                    if !tag.is_null() { tag } else { c"".as_ptr() },
142                );
143                if status < 1 {
144                    ret = MM_NOCON;
145                }
146                close(consolefd);
147            }
148        }
149    }
150    if (classification & MM_PRINT) != 0 {
151        unsafe {
152            while !cmsg.is_null() && *cmsg != 0 {
153                i = 0;
154                loop {
155                    if msgs[i].is_null() {
156                        break;
157                    }
158                    if strcolcmp(msgs[i], cmsg) == 0 {
159                        break;
160                    }
161                    i += 1;
162                }
163                if msgs[i].is_null() {
164                    //ignore MSGVERB-unrecognized component
165                    verb = 0xFF;
166                    break;
167                } else {
168                    verb |= 1 << i;
169                    cmsg = strchr(cmsg, b':'.into());
170                    if !cmsg.is_null() {
171                        cmsg = cmsg.add(1);
172                    }
173                }
174            }
175            if verb == 0 {
176                verb = 0xFF;
177            }
178            #[rustfmt::skip]
179            let status = dprintf(2,c"%s%s%s%s%s%s%s%s\n".as_ptr(),
180                if (verb & 1 != 0) && !label.is_null() { label } else { c"".as_ptr() },
181                    if (verb & 1 != 0) && !label.is_null() { c": ".as_ptr() } else { c"".as_ptr() },
182                    if (verb & 2 != 0) && severity != 0 { errstring } else { c"".as_ptr() },
183                    if (verb & 4 != 0) && !text.is_null() { text } else { c"".as_ptr() },
184                    if (verb & 8 != 0) && !action.is_null() { c"\nTO FIX: ".as_ptr() } else { c"".as_ptr() },
185                    if (verb & 8 != 0) && !action.is_null() { action } else { c"".as_ptr() },
186                    if (verb & 8 != 0) && !action.is_null() { c" ".as_ptr() } else { c"".as_ptr() },
187                    if (verb & 16 != 0) && !tag.is_null() { tag } else { c"".as_ptr() },
188            );
189            if status < 1 {
190                ret |= MM_NOMSG;
191            }
192        }
193    }
194    if ret & (MM_NOCON | MM_NOMSG) == (MM_NOCON | MM_NOMSG) {
195        ret = MM_NOTOK;
196    }
197    unsafe {
198        pthread_setcancelstate(cs, 0 as _);
199    }
200    ret
201}