use CLibcType::*;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CLibcType {
Void,
Char,
SChar,
UChar,
Short,
UShort,
Int,
UInt,
Long,
ULong,
LongLong,
ULongLong,
Float,
Double,
LongDouble,
CharPtr,
ConstCharPtr,
VoidPtr,
ConstVoidPtr,
IntPtr,
SizeT,
WCharT,
FilePtr,
FposT,
ClockT,
TimeT,
DivT,
LDivT,
LLDivT,
TmPtr,
VaList,
JmpBuf,
SigAtomicT,
LconvPtr,
WIntT,
MbstateT,
IMaxDivT,
FnPtr {
ret: Box<CLibcType>,
params: Vec<CLibcType>,
},
Array {
elem: Box<CLibcType>,
},
}
impl CLibcType {
pub fn as_str(&self) -> &'static str {
match self {
CLibcType::Void => "void",
CLibcType::Char => "char",
CLibcType::SChar => "signed char",
CLibcType::UChar => "unsigned char",
CLibcType::Short => "short",
CLibcType::UShort => "unsigned short",
CLibcType::Int => "int",
CLibcType::UInt => "unsigned int",
CLibcType::Long => "long",
CLibcType::ULong => "unsigned long",
CLibcType::LongLong => "long long",
CLibcType::ULongLong => "unsigned long long",
CLibcType::Float => "float",
CLibcType::Double => "double",
CLibcType::LongDouble => "long double",
CLibcType::CharPtr => "char *",
CLibcType::ConstCharPtr => "const char *",
CLibcType::VoidPtr => "void *",
CLibcType::ConstVoidPtr => "const void *",
CLibcType::IntPtr => "int *",
CLibcType::SizeT => "size_t",
CLibcType::WCharT => "wchar_t",
CLibcType::FilePtr => "FILE *",
CLibcType::FposT => "fpos_t",
CLibcType::ClockT => "clock_t",
CLibcType::TimeT => "time_t",
CLibcType::DivT => "div_t",
CLibcType::LDivT => "ldiv_t",
CLibcType::LLDivT => "lldiv_t",
CLibcType::TmPtr => "struct tm *",
CLibcType::VaList => "va_list",
CLibcType::JmpBuf => "jmp_buf",
CLibcType::SigAtomicT => "sig_atomic_t",
CLibcType::LconvPtr => "struct lconv *",
CLibcType::WIntT => "wint_t",
CLibcType::MbstateT => "mbstate_t",
CLibcType::IMaxDivT => "imaxdiv_t",
CLibcType::FnPtr { .. } => "function pointer",
CLibcType::Array { .. } => "array",
}
}
pub fn is_pointer(&self) -> bool {
matches!(
self,
CLibcType::CharPtr
| CLibcType::ConstCharPtr
| CLibcType::VoidPtr
| CLibcType::ConstVoidPtr
| CLibcType::IntPtr
| CLibcType::FilePtr
| CLibcType::TmPtr
| CLibcType::LconvPtr
| CLibcType::FnPtr { .. }
)
}
pub fn is_integer(&self) -> bool {
matches!(
self,
CLibcType::Char
| CLibcType::SChar
| CLibcType::UChar
| CLibcType::Short
| CLibcType::UShort
| CLibcType::Int
| CLibcType::UInt
| CLibcType::Long
| CLibcType::ULong
| CLibcType::LongLong
| CLibcType::ULongLong
| CLibcType::SizeT
| CLibcType::ClockT
| CLibcType::TimeT
| CLibcType::SigAtomicT
| CLibcType::WIntT
)
}
}
impl std::fmt::Display for CLibcType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LibcHeader {
Stdio,
Stdlib,
String,
Ctype,
Math,
Errno,
Assert,
Time,
Setjmp,
Signal,
Locale,
Wchar,
Inttypes,
Stdatomic,
Complex,
}
impl LibcHeader {
pub fn filename(&self) -> &'static str {
match self {
LibcHeader::Stdio => "<stdio.h>",
LibcHeader::Stdlib => "<stdlib.h>",
LibcHeader::String => "<string.h>",
LibcHeader::Ctype => "<ctype.h>",
LibcHeader::Math => "<math.h>",
LibcHeader::Errno => "<errno.h>",
LibcHeader::Assert => "<assert.h>",
LibcHeader::Time => "<time.h>",
LibcHeader::Setjmp => "<setjmp.h>",
LibcHeader::Signal => "<signal.h>",
LibcHeader::Locale => "<locale.h>",
LibcHeader::Wchar => "<wchar.h>",
LibcHeader::Inttypes => "<inttypes.h>",
LibcHeader::Stdatomic => "<stdatomic.h>",
LibcHeader::Complex => "<complex.h>",
}
}
pub fn name(&self) -> &'static str {
match self {
LibcHeader::Stdio => "stdio.h",
LibcHeader::Stdlib => "stdlib.h",
LibcHeader::String => "string.h",
LibcHeader::Ctype => "ctype.h",
LibcHeader::Math => "math.h",
LibcHeader::Errno => "errno.h",
LibcHeader::Assert => "assert.h",
LibcHeader::Time => "time.h",
LibcHeader::Setjmp => "setjmp.h",
LibcHeader::Signal => "signal.h",
LibcHeader::Locale => "locale.h",
LibcHeader::Wchar => "wchar.h",
LibcHeader::Inttypes => "inttypes.h",
LibcHeader::Stdatomic => "stdatomic.h",
LibcHeader::Complex => "complex.h",
}
}
pub fn short_name(&self) -> &'static str {
match self {
LibcHeader::Stdio => "stdio",
LibcHeader::Stdlib => "stdlib",
LibcHeader::String => "string",
LibcHeader::Ctype => "ctype",
LibcHeader::Math => "math",
LibcHeader::Errno => "errno",
LibcHeader::Assert => "assert",
LibcHeader::Time => "time",
LibcHeader::Setjmp => "setjmp",
LibcHeader::Signal => "signal",
LibcHeader::Locale => "locale",
LibcHeader::Wchar => "wchar",
LibcHeader::Inttypes => "inttypes",
LibcHeader::Stdatomic => "stdatomic",
LibcHeader::Complex => "complex",
}
}
pub fn all() -> &'static [LibcHeader] {
&[
LibcHeader::Stdio,
LibcHeader::Stdlib,
LibcHeader::String,
LibcHeader::Ctype,
LibcHeader::Math,
LibcHeader::Errno,
LibcHeader::Assert,
LibcHeader::Time,
LibcHeader::Setjmp,
LibcHeader::Signal,
LibcHeader::Locale,
LibcHeader::Wchar,
LibcHeader::Inttypes,
LibcHeader::Stdatomic,
LibcHeader::Complex,
]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum LibcBuiltinCategory {
Io,
Memory,
String,
Ctype,
Math,
Conversion,
Process,
SearchSort,
Time,
Error,
Diagnostics,
Other,
}
impl LibcBuiltinCategory {
pub fn is_pure(&self) -> bool {
matches!(
self,
LibcBuiltinCategory::Math
| LibcBuiltinCategory::Ctype
| LibcBuiltinCategory::Conversion
)
}
pub fn may_throw(&self) -> bool {
matches!(
self,
LibcBuiltinCategory::Io
| LibcBuiltinCategory::Memory
| LibcBuiltinCategory::Math
| LibcBuiltinCategory::Process
)
}
}
#[derive(Debug, Clone)]
pub struct LibcFunctionDecl {
pub name: &'static str,
pub header: LibcHeader,
pub ret_ty: CLibcType,
pub params: Vec<CLibcType>,
pub is_vararg: bool,
pub category: LibcBuiltinCategory,
pub is_pure: bool,
pub is_noreturn: bool,
pub returns_twice: bool,
pub may_throw: bool,
pub introduced: &'static str,
pub doc: &'static str,
}
impl LibcFunctionDecl {
pub fn new(
name: &'static str,
header: LibcHeader,
ret_ty: CLibcType,
params: Vec<CLibcType>,
is_vararg: bool,
category: LibcBuiltinCategory,
doc: &'static str,
) -> Self {
let is_pure = category.is_pure();
let may_throw = category.may_throw();
LibcFunctionDecl {
name,
header,
ret_ty,
params,
is_vararg,
category,
is_pure,
is_noreturn: false,
returns_twice: false,
may_throw,
introduced: "C89",
doc,
}
}
pub fn with_noreturn(mut self) -> Self {
self.is_noreturn = true;
self
}
pub fn with_returns_twice(mut self) -> Self {
self.returns_twice = true;
self
}
pub fn with_introduced(mut self, standard: &'static str) -> Self {
self.introduced = standard;
self
}
pub fn to_prototype(&self) -> String {
let mut s = String::new();
s.push_str(self.ret_ty.as_str());
s.push(' ');
s.push_str(self.name);
s.push('(');
for (i, param) in self.params.iter().enumerate() {
if i > 0 {
s.push_str(", ");
}
s.push_str(param.as_str());
}
if self.is_vararg {
if !self.params.is_empty() {
s.push_str(", ");
}
s.push_str("...");
}
if self.params.is_empty() && !self.is_vararg {
s.push_str("void");
}
s.push(')');
s
}
pub fn fixed_param_count(&self) -> usize {
self.params.len()
}
pub fn is_zero_arity(&self) -> bool {
self.params.is_empty() && !self.is_vararg
}
}
#[derive(Debug, Clone)]
pub struct LibcMacroDef {
pub name: &'static str,
pub header: LibcHeader,
pub replacement: Option<String>,
pub is_function_like: bool,
pub doc: &'static str,
}
impl LibcMacroDef {
pub fn simple(
name: &'static str,
header: LibcHeader,
replacement: &str,
doc: &'static str,
) -> Self {
LibcMacroDef {
name,
header,
replacement: Some(replacement.to_string()),
is_function_like: false,
doc,
}
}
pub fn with_introduced(self, _standard: &'static str) -> Self {
self
}
pub fn function_like(name: &'static str, header: LibcHeader, doc: &'static str) -> Self {
LibcMacroDef {
name,
header,
replacement: None,
is_function_like: true,
doc,
}
}
}
#[derive(Debug, Clone)]
pub struct LibcTypeDef {
pub name: &'static str,
pub header: LibcHeader,
pub underlying: CLibcType,
pub doc: &'static str,
}
impl LibcTypeDef {
pub fn new(
name: &'static str,
header: LibcHeader,
underlying: CLibcType,
doc: &'static str,
) -> Self {
LibcTypeDef {
name,
header,
underlying,
doc,
}
}
}
#[derive(Debug, Clone)]
pub struct LibcGlobalDecl {
pub name: &'static str,
pub header: LibcHeader,
pub ty: CLibcType,
pub doc: &'static str,
}
impl LibcGlobalDecl {
pub fn new(name: &'static str, header: LibcHeader, ty: CLibcType, doc: &'static str) -> Self {
LibcGlobalDecl {
name,
header,
ty,
doc,
}
}
}
fn build_stdio_types() -> Vec<LibcTypeDef> {
vec![
LibcTypeDef::new(
"FILE",
LibcHeader::Stdio,
CLibcType::FilePtr,
"Opaque type representing a stream for input or output.",
),
LibcTypeDef::new(
"fpos_t",
LibcHeader::Stdio,
CLibcType::FposT,
"Type capable of uniquely specifying every position within a file.",
),
LibcTypeDef::new(
"size_t",
LibcHeader::Stdio,
CLibcType::SizeT,
"Unsigned integral type for object sizes (also in <stddef.h>).",
),
]
}
fn build_stdio_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"EOF",
LibcHeader::Stdio,
"(-1)",
"Negative integer constant indicating end-of-file or error.",
),
LibcMacroDef::simple(
"_IOFBF",
LibcHeader::Stdio,
"0",
"Fully-buffered mode for setvbuf.",
),
LibcMacroDef::simple(
"_IOLBF",
LibcHeader::Stdio,
"1",
"Line-buffered mode for setvbuf.",
),
LibcMacroDef::simple(
"_IONBF",
LibcHeader::Stdio,
"2",
"Unbuffered mode for setvbuf.",
),
LibcMacroDef::simple(
"BUFSIZ",
LibcHeader::Stdio,
"8192",
"Default buffer size for setbuf.",
),
LibcMacroDef::simple(
"FILENAME_MAX",
LibcHeader::Stdio,
"4096",
"Maximum length of a file name.",
),
LibcMacroDef::simple(
"FOPEN_MAX",
LibcHeader::Stdio,
"16",
"Minimum number of files that can be open simultaneously.",
),
LibcMacroDef::simple(
"TMP_MAX",
LibcHeader::Stdio,
"238328",
"Maximum number of unique temporary file names.",
),
LibcMacroDef::simple(
"L_tmpnam",
LibcHeader::Stdio,
"20",
"Minimum buffer size needed for tmpnam.",
),
LibcMacroDef::simple(
"SEEK_SET",
LibcHeader::Stdio,
"0",
"Seek from beginning of file.",
),
LibcMacroDef::simple(
"SEEK_CUR",
LibcHeader::Stdio,
"1",
"Seek from current position.",
),
LibcMacroDef::simple("SEEK_END", LibcHeader::Stdio, "2", "Seek from end of file."),
]
}
fn build_stdio_globals() -> Vec<LibcGlobalDecl> {
vec![
LibcGlobalDecl::new(
"stdin",
LibcHeader::Stdio,
CLibcType::FilePtr,
"Standard input stream (normally connected to the keyboard).",
),
LibcGlobalDecl::new(
"stdout",
LibcHeader::Stdio,
CLibcType::FilePtr,
"Standard output stream (normally connected to the terminal).",
),
LibcGlobalDecl::new(
"stderr",
LibcHeader::Stdio,
CLibcType::FilePtr,
"Standard error stream (normally connected to the terminal, unbuffered).",
),
]
}
fn build_stdio_functions() -> Vec<LibcFunctionDecl> {
use CLibcType::*;
vec![
LibcFunctionDecl::new(
"printf", LibcHeader::Stdio, Int,
vec![ConstCharPtr], true,
LibcBuiltinCategory::Io,
"Prints formatted output to stdout. Supports %d, %s, %c, %x, %p, %f, %u, %ld, %lld, %%.",
),
LibcFunctionDecl::new(
"fprintf", LibcHeader::Stdio, Int,
vec![FilePtr, ConstCharPtr], true,
LibcBuiltinCategory::Io,
"Prints formatted output to a FILE stream.",
),
LibcFunctionDecl::new(
"sprintf", LibcHeader::Stdio, Int,
vec![CharPtr, ConstCharPtr], true,
LibcBuiltinCategory::Io,
"Prints formatted output to a character string (no bounds checking).",
).with_introduced("C89"),
LibcFunctionDecl::new(
"snprintf", LibcHeader::Stdio, Int,
vec![CharPtr, SizeT, ConstCharPtr], true,
LibcBuiltinCategory::Io,
"Prints formatted output to a string with bounded size.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"vprintf", LibcHeader::Stdio, Int,
vec![ConstCharPtr, VaList], false,
LibcBuiltinCategory::Io,
"Prints formatted output to stdout using a va_list.",
),
LibcFunctionDecl::new(
"vfprintf", LibcHeader::Stdio, Int,
vec![FilePtr, ConstCharPtr, VaList], false,
LibcBuiltinCategory::Io,
"Prints formatted output to a FILE stream using a va_list.",
),
LibcFunctionDecl::new(
"vsprintf", LibcHeader::Stdio, Int,
vec![CharPtr, ConstCharPtr, VaList], false,
LibcBuiltinCategory::Io,
"Prints formatted output to a string using a va_list (no bounds checking).",
),
LibcFunctionDecl::new(
"vsnprintf", LibcHeader::Stdio, Int,
vec![CharPtr, SizeT, ConstCharPtr, VaList], false,
LibcBuiltinCategory::Io,
"Prints formatted output to a string with bounded size using a va_list.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"scanf", LibcHeader::Stdio, Int,
vec![ConstCharPtr], true,
LibcBuiltinCategory::Io,
"Reads formatted input from stdin.",
),
LibcFunctionDecl::new(
"fscanf", LibcHeader::Stdio, Int,
vec![FilePtr, ConstCharPtr], true,
LibcBuiltinCategory::Io,
"Reads formatted input from a FILE stream.",
),
LibcFunctionDecl::new(
"sscanf", LibcHeader::Stdio, Int,
vec![ConstCharPtr, ConstCharPtr], true,
LibcBuiltinCategory::Io,
"Reads formatted input from a string.",
),
LibcFunctionDecl::new(
"vscanf", LibcHeader::Stdio, Int,
vec![ConstCharPtr, VaList], false,
LibcBuiltinCategory::Io,
"Reads formatted input from stdin using a va_list.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"vfscanf", LibcHeader::Stdio, Int,
vec![FilePtr, ConstCharPtr, VaList], false,
LibcBuiltinCategory::Io,
"Reads formatted input from a FILE stream using a va_list.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"vsscanf", LibcHeader::Stdio, Int,
vec![ConstCharPtr, ConstCharPtr, VaList], false,
LibcBuiltinCategory::Io,
"Reads formatted input from a string using a va_list.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"fopen", LibcHeader::Stdio, FilePtr,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::Io,
"Opens a file and associates a stream with it. Modes: r, w, a, r+, w+, a+ (optionally b for binary).",
),
LibcFunctionDecl::new(
"freopen", LibcHeader::Stdio, FilePtr,
vec![ConstCharPtr, ConstCharPtr, FilePtr], false,
LibcBuiltinCategory::Io,
"Reopens an existing stream with a different file or mode.",
),
LibcFunctionDecl::new(
"fclose", LibcHeader::Stdio, Int,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Closes a file stream and disassociates it from the file.",
),
LibcFunctionDecl::new(
"fflush", LibcHeader::Stdio, Int,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Flushes a stream's output buffer. If NULL, flushes all output streams.",
),
LibcFunctionDecl::new(
"fread", LibcHeader::Stdio, SizeT,
vec![VoidPtr, SizeT, SizeT, FilePtr], false,
LibcBuiltinCategory::Io,
"Reads up to count elements of size bytes from a stream.",
),
LibcFunctionDecl::new(
"fwrite", LibcHeader::Stdio, SizeT,
vec![ConstVoidPtr, SizeT, SizeT, FilePtr], false,
LibcBuiltinCategory::Io,
"Writes up to count elements of size bytes to a stream.",
),
LibcFunctionDecl::new(
"fseek", LibcHeader::Stdio, Int,
vec![FilePtr, Long, Int], false,
LibcBuiltinCategory::Io,
"Sets the file position indicator for a stream.",
),
LibcFunctionDecl::new(
"ftell", LibcHeader::Stdio, Long,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Returns the current file position indicator for a stream.",
),
LibcFunctionDecl::new(
"rewind", LibcHeader::Stdio, Void,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Sets the file position indicator to the beginning of the file and clears errors.",
),
LibcFunctionDecl::new(
"fgetpos", LibcHeader::Stdio, Int,
vec![FilePtr, FposT], false,
LibcBuiltinCategory::Io,
"Gets the current file position indicator and stores it in a fpos_t.",
),
LibcFunctionDecl::new(
"fsetpos", LibcHeader::Stdio, Int,
vec![FilePtr, FposT], false,
LibcBuiltinCategory::Io,
"Sets the file position indicator from a previously stored fpos_t value.",
),
LibcFunctionDecl::new(
"fgetc", LibcHeader::Stdio, Int,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Reads a single character from a stream and advances the position.",
),
LibcFunctionDecl::new(
"fputc", LibcHeader::Stdio, Int,
vec![Int, FilePtr], false,
LibcBuiltinCategory::Io,
"Writes a single character to a stream.",
),
LibcFunctionDecl::new(
"getc", LibcHeader::Stdio, Int,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Reads a single character from a stream (may be implemented as a macro).",
),
LibcFunctionDecl::new(
"putc", LibcHeader::Stdio, Int,
vec![Int, FilePtr], false,
LibcBuiltinCategory::Io,
"Writes a single character to a stream (may be implemented as a macro).",
),
LibcFunctionDecl::new(
"getchar", LibcHeader::Stdio, Int,
vec![], false,
LibcBuiltinCategory::Io,
"Reads a single character from stdin. Equivalent to getc(stdin).",
),
LibcFunctionDecl::new(
"putchar", LibcHeader::Stdio, Int,
vec![Int], false,
LibcBuiltinCategory::Io,
"Writes a single character to stdout. Equivalent to putc(c, stdout).",
),
LibcFunctionDecl::new(
"ungetc", LibcHeader::Stdio, Int,
vec![Int, FilePtr], false,
LibcBuiltinCategory::Io,
"Pushes a character back onto a stream so it will be read next.",
),
LibcFunctionDecl::new(
"fgets", LibcHeader::Stdio, CharPtr,
vec![CharPtr, Int, FilePtr], false,
LibcBuiltinCategory::Io,
"Reads at most n-1 characters from a stream into a string, stopping at newline.",
),
LibcFunctionDecl::new(
"fputs", LibcHeader::Stdio, Int,
vec![ConstCharPtr, FilePtr], false,
LibcBuiltinCategory::Io,
"Writes a string to a stream (excluding the null terminator).",
),
LibcFunctionDecl::new(
"gets", LibcHeader::Stdio, CharPtr,
vec![CharPtr], false,
LibcBuiltinCategory::Io,
"Reads a line from stdin into a buffer (DEPRECATED: unsafe, removed in C11).",
).with_introduced("C89"),
LibcFunctionDecl::new(
"puts", LibcHeader::Stdio, Int,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Io,
"Writes a string to stdout followed by a newline.",
),
LibcFunctionDecl::new(
"perror", LibcHeader::Stdio, Void,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Error,
"Prints an error message to stderr based on the current errno value.",
),
LibcFunctionDecl::new(
"remove", LibcHeader::Stdio, Int,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Io,
"Deletes the file specified by filename.",
),
LibcFunctionDecl::new(
"rename", LibcHeader::Stdio, Int,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::Io,
"Renames or moves a file.",
),
LibcFunctionDecl::new(
"tmpfile", LibcHeader::Stdio, FilePtr,
vec![], false,
LibcBuiltinCategory::Io,
"Creates a temporary binary file that is automatically deleted when closed.",
),
LibcFunctionDecl::new(
"tmpnam", LibcHeader::Stdio, CharPtr,
vec![CharPtr], false,
LibcBuiltinCategory::Io,
"Generates a unique temporary file name (DEPRECATED: use tmpfile or mkstemp).",
),
LibcFunctionDecl::new(
"setbuf", LibcHeader::Stdio, Void,
vec![FilePtr, CharPtr], false,
LibcBuiltinCategory::Io,
"Sets the buffer for a stream. If buf is NULL, turns off buffering.",
),
LibcFunctionDecl::new(
"setvbuf", LibcHeader::Stdio, Int,
vec![FilePtr, CharPtr, Int, SizeT], false,
LibcBuiltinCategory::Io,
"Sets the buffer and buffering mode (_IOFBF, _IOLBF, _IONBF) for a stream.",
),
LibcFunctionDecl::new(
"feof", LibcHeader::Stdio, Int,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Tests the end-of-file indicator for a stream.",
),
LibcFunctionDecl::new(
"ferror", LibcHeader::Stdio, Int,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Tests the error indicator for a stream.",
),
LibcFunctionDecl::new(
"clearerr", LibcHeader::Stdio, Void,
vec![FilePtr], false,
LibcBuiltinCategory::Io,
"Clears the end-of-file and error indicators for a stream.",
),
]
}
fn build_stdlib_types() -> Vec<LibcTypeDef> {
vec![
LibcTypeDef::new(
"size_t",
LibcHeader::Stdlib,
CLibcType::SizeT,
"Unsigned integral type for object sizes.",
),
LibcTypeDef::new(
"wchar_t",
LibcHeader::Stdlib,
CLibcType::WCharT,
"Wide character type.",
),
LibcTypeDef::new(
"div_t",
LibcHeader::Stdlib,
CLibcType::DivT,
"Structure returned by div(), containing quot and rem (both int).",
),
LibcTypeDef::new(
"ldiv_t",
LibcHeader::Stdlib,
CLibcType::LDivT,
"Structure returned by ldiv(), containing quot and rem (both long).",
),
LibcTypeDef::new(
"lldiv_t",
LibcHeader::Stdlib,
CLibcType::LLDivT,
"Structure returned by lldiv(), containing quot and rem (both long long).",
),
]
}
fn build_stdlib_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"EXIT_SUCCESS",
LibcHeader::Stdlib,
"0",
"Successful program termination status.",
),
LibcMacroDef::simple(
"EXIT_FAILURE",
LibcHeader::Stdlib,
"1",
"Unsuccessful program termination status.",
),
LibcMacroDef::simple(
"RAND_MAX",
LibcHeader::Stdlib,
"2147483647",
"Maximum value returned by rand() (at least 32767).",
),
LibcMacroDef::simple(
"MB_CUR_MAX",
LibcHeader::Stdlib,
"6",
"Maximum number of bytes in a multibyte character for the current locale.",
),
]
}
fn build_stdlib_functions() -> Vec<LibcFunctionDecl> {
use CLibcType::*;
vec![
LibcFunctionDecl::new(
"malloc", LibcHeader::Stdlib, VoidPtr,
vec![SizeT], false,
LibcBuiltinCategory::Memory,
"Allocates size bytes of uninitialized memory. Returns NULL on failure.",
),
LibcFunctionDecl::new(
"calloc", LibcHeader::Stdlib, VoidPtr,
vec![SizeT, SizeT], false,
LibcBuiltinCategory::Memory,
"Allocates memory for an array of count elements of size bytes, initialized to zero.",
),
LibcFunctionDecl::new(
"realloc", LibcHeader::Stdlib, VoidPtr,
vec![VoidPtr, SizeT], false,
LibcBuiltinCategory::Memory,
"Reallocates a memory block to a new size, preserving contents up to min(old, new).",
),
LibcFunctionDecl::new(
"free", LibcHeader::Stdlib, Void,
vec![VoidPtr], false,
LibcBuiltinCategory::Memory,
"Deallocates a memory block previously allocated by malloc/calloc/realloc.",
),
LibcFunctionDecl::new(
"aligned_alloc", LibcHeader::Stdlib, VoidPtr,
vec![SizeT, SizeT], false,
LibcBuiltinCategory::Memory,
"Allocates memory with a specified alignment.",
).with_introduced("C11"),
LibcFunctionDecl::new(
"atof", LibcHeader::Stdlib, Double,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Conversion,
"Converts a string to double (no error detection).",
),
LibcFunctionDecl::new(
"atoi", LibcHeader::Stdlib, Int,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Conversion,
"Converts a string to int (no error detection).",
),
LibcFunctionDecl::new(
"atol", LibcHeader::Stdlib, Long,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Conversion,
"Converts a string to long (no error detection).",
),
LibcFunctionDecl::new(
"atoll", LibcHeader::Stdlib, LongLong,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Conversion,
"Converts a string to long long (no error detection).",
).with_introduced("C99"),
LibcFunctionDecl::new(
"strtol", LibcHeader::Stdlib, Long,
vec![ConstCharPtr, CharPtr, Int], false,
LibcBuiltinCategory::Conversion,
"Converts a string to long with error detection and base (2-36, or 0 for auto-detect).",
),
LibcFunctionDecl::new(
"strtoul", LibcHeader::Stdlib, ULong,
vec![ConstCharPtr, CharPtr, Int], false,
LibcBuiltinCategory::Conversion,
"Converts a string to unsigned long with error detection.",
),
LibcFunctionDecl::new(
"strtoll", LibcHeader::Stdlib, LongLong,
vec![ConstCharPtr, CharPtr, Int], false,
LibcBuiltinCategory::Conversion,
"Converts a string to long long with error detection.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"strtoull", LibcHeader::Stdlib, ULongLong,
vec![ConstCharPtr, CharPtr, Int], false,
LibcBuiltinCategory::Conversion,
"Converts a string to unsigned long long with error detection.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"strtof", LibcHeader::Stdlib, Float,
vec![ConstCharPtr, CharPtr], false,
LibcBuiltinCategory::Conversion,
"Converts a string to float.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"strtod", LibcHeader::Stdlib, Double,
vec![ConstCharPtr, CharPtr], false,
LibcBuiltinCategory::Conversion,
"Converts a string to double with error detection.",
),
LibcFunctionDecl::new(
"strtold", LibcHeader::Stdlib, LongDouble,
vec![ConstCharPtr, CharPtr], false,
LibcBuiltinCategory::Conversion,
"Converts a string to long double.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"rand", LibcHeader::Stdlib, Int,
vec![], false,
LibcBuiltinCategory::Math,
"Returns a pseudo-random integer in the range [0, RAND_MAX].",
),
LibcFunctionDecl::new(
"srand", LibcHeader::Stdlib, Void,
vec![UInt], false,
LibcBuiltinCategory::Math,
"Seeds the pseudo-random number generator used by rand().",
),
LibcFunctionDecl::new(
"abort", LibcHeader::Stdlib, Void,
vec![], false,
LibcBuiltinCategory::Process,
"Causes abnormal program termination (raises SIGABRT).",
).with_noreturn(),
LibcFunctionDecl::new(
"exit", LibcHeader::Stdlib, Void,
vec![Int], false,
LibcBuiltinCategory::Process,
"Causes normal program termination with cleanup (atexit handlers, flushed streams).",
).with_noreturn(),
LibcFunctionDecl::new(
"atexit", LibcHeader::Stdlib, Int,
vec![CLibcType::FnPtr {
ret: Box::new(Void),
params: vec![],
}],
false,
LibcBuiltinCategory::Process,
"Registers a function to be called on normal program termination.",
),
LibcFunctionDecl::new(
"_Exit", LibcHeader::Stdlib, Void,
vec![Int], false,
LibcBuiltinCategory::Process,
"Causes normal program termination without cleanup (no atexit handlers).",
).with_noreturn().with_introduced("C99"),
LibcFunctionDecl::new(
"quick_exit", LibcHeader::Stdlib, Void,
vec![Int], false,
LibcBuiltinCategory::Process,
"Causes normal program termination without full cleanup (calls at_quick_exit handlers).",
).with_noreturn().with_introduced("C11"),
LibcFunctionDecl::new(
"at_quick_exit", LibcHeader::Stdlib, Int,
vec![CLibcType::FnPtr {
ret: Box::new(Void),
params: vec![],
}],
false,
LibcBuiltinCategory::Process,
"Registers a function to be called on quick_exit.",
).with_introduced("C11"),
LibcFunctionDecl::new(
"getenv", LibcHeader::Stdlib, CharPtr,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Other,
"Searches the environment list for a string matching name.",
),
LibcFunctionDecl::new(
"system", LibcHeader::Stdlib, Int,
vec![ConstCharPtr], false,
LibcBuiltinCategory::Process,
"Passes a command to the host environment's command processor.",
),
LibcFunctionDecl::new(
"bsearch", LibcHeader::Stdlib, VoidPtr,
vec![ConstVoidPtr, ConstVoidPtr, SizeT, SizeT,
CLibcType::FnPtr {
ret: Box::new(Int),
params: vec![ConstVoidPtr, ConstVoidPtr],
}],
false,
LibcBuiltinCategory::SearchSort,
"Binary search of a sorted array. Returns pointer to found element or NULL.",
),
LibcFunctionDecl::new(
"qsort", LibcHeader::Stdlib, Void,
vec![VoidPtr, SizeT, SizeT,
CLibcType::FnPtr {
ret: Box::new(Int),
params: vec![ConstVoidPtr, ConstVoidPtr],
}],
false,
LibcBuiltinCategory::SearchSort,
"Sorts an array using the quicksort algorithm with a user-provided comparison function.",
),
LibcFunctionDecl::new(
"abs", LibcHeader::Stdlib, Int,
vec![Int], false,
LibcBuiltinCategory::Math,
"Returns the absolute value of an int.",
),
LibcFunctionDecl::new(
"labs", LibcHeader::Stdlib, Long,
vec![Long], false,
LibcBuiltinCategory::Math,
"Returns the absolute value of a long.",
),
LibcFunctionDecl::new(
"llabs", LibcHeader::Stdlib, LongLong,
vec![LongLong], false,
LibcBuiltinCategory::Math,
"Returns the absolute value of a long long.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"div", LibcHeader::Stdlib, DivT,
vec![Int, Int], false,
LibcBuiltinCategory::Math,
"Computes both quotient and remainder of int division.",
),
LibcFunctionDecl::new(
"ldiv", LibcHeader::Stdlib, LDivT,
vec![Long, Long], false,
LibcBuiltinCategory::Math,
"Computes both quotient and remainder of long division.",
),
LibcFunctionDecl::new(
"lldiv", LibcHeader::Stdlib, LLDivT,
vec![LongLong, LongLong], false,
LibcBuiltinCategory::Math,
"Computes both quotient and remainder of long long division.",
).with_introduced("C99"),
LibcFunctionDecl::new(
"mblen", LibcHeader::Stdlib, Int,
vec![ConstCharPtr, SizeT], false,
LibcBuiltinCategory::Conversion,
"Returns the number of bytes in the multibyte character pointed to by s.",
),
LibcFunctionDecl::new(
"mbtowc", LibcHeader::Stdlib, Int,
vec![CLibcType::Array { elem: Box::new(WCharT) }, ConstCharPtr, SizeT],
false,
LibcBuiltinCategory::Conversion,
"Converts a multibyte character to a wide character.",
),
LibcFunctionDecl::new(
"wctomb", LibcHeader::Stdlib, Int,
vec![CharPtr, WCharT], false,
LibcBuiltinCategory::Conversion,
"Converts a wide character to a multibyte character.",
),
LibcFunctionDecl::new(
"mbstowcs", LibcHeader::Stdlib, SizeT,
vec![CLibcType::Array { elem: Box::new(WCharT) }, ConstCharPtr, SizeT],
false,
LibcBuiltinCategory::Conversion,
"Converts a multibyte string to a wide-character string.",
),
LibcFunctionDecl::new(
"wcstombs", LibcHeader::Stdlib, SizeT,
vec![CharPtr, CLibcType::Array { elem: Box::new(ConstCharPtr) }, SizeT],
false,
LibcBuiltinCategory::Conversion,
"Converts a wide-character string to a multibyte string.",
),
]
}
fn build_string_types() -> Vec<LibcTypeDef> {
vec![LibcTypeDef::new(
"size_t",
LibcHeader::String,
CLibcType::SizeT,
"Unsigned integral type for object sizes.",
)]
}
fn build_string_functions() -> Vec<LibcFunctionDecl> {
use CLibcType::*;
vec![
LibcFunctionDecl::new(
"memcpy", LibcHeader::String, VoidPtr,
vec![VoidPtr, ConstVoidPtr, SizeT], false,
LibcBuiltinCategory::String,
"Copies n bytes from src to dest (undefined if regions overlap).",
),
LibcFunctionDecl::new(
"memmove", LibcHeader::String, VoidPtr,
vec![VoidPtr, ConstVoidPtr, SizeT], false,
LibcBuiltinCategory::String,
"Copies n bytes from src to dest (safe for overlapping regions).",
),
LibcFunctionDecl::new(
"memcmp", LibcHeader::String, Int,
vec![ConstVoidPtr, ConstVoidPtr, SizeT], false,
LibcBuiltinCategory::String,
"Compares the first n bytes of two memory regions.",
),
LibcFunctionDecl::new(
"memchr", LibcHeader::String, VoidPtr,
vec![ConstVoidPtr, Int, SizeT], false,
LibcBuiltinCategory::String,
"Finds the first occurrence of a byte value in a memory region.",
),
LibcFunctionDecl::new(
"memset", LibcHeader::String, VoidPtr,
vec![VoidPtr, Int, SizeT], false,
LibcBuiltinCategory::String,
"Sets n bytes of memory to a specified value.",
),
LibcFunctionDecl::new(
"strcpy", LibcHeader::String, CharPtr,
vec![CharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Copies src string to dest (including null terminator, no bounds checking).",
),
LibcFunctionDecl::new(
"strncpy", LibcHeader::String, CharPtr,
vec![CharPtr, ConstCharPtr, SizeT], false,
LibcBuiltinCategory::String,
"Copies at most n characters from src to dest. Pads with nulls if src is shorter.",
),
LibcFunctionDecl::new(
"strcat", LibcHeader::String, CharPtr,
vec![CharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Appends src string to dest (no bounds checking).",
),
LibcFunctionDecl::new(
"strncat", LibcHeader::String, CharPtr,
vec![CharPtr, ConstCharPtr, SizeT], false,
LibcBuiltinCategory::String,
"Appends at most n characters from src to dest, then adds null terminator.",
),
LibcFunctionDecl::new(
"strcmp", LibcHeader::String, Int,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Compares two strings lexicographically.",
),
LibcFunctionDecl::new(
"strncmp", LibcHeader::String, Int,
vec![ConstCharPtr, ConstCharPtr, SizeT], false,
LibcBuiltinCategory::String,
"Compares at most n characters of two strings.",
),
LibcFunctionDecl::new(
"strcoll", LibcHeader::String, Int,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Compares two strings using the current locale's collation order.",
),
LibcFunctionDecl::new(
"strchr", LibcHeader::String, CharPtr,
vec![ConstCharPtr, Int], false,
LibcBuiltinCategory::String,
"Finds the first occurrence of a character in a string.",
),
LibcFunctionDecl::new(
"strrchr", LibcHeader::String, CharPtr,
vec![ConstCharPtr, Int], false,
LibcBuiltinCategory::String,
"Finds the last occurrence of a character in a string.",
),
LibcFunctionDecl::new(
"strspn", LibcHeader::String, SizeT,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Returns the length of the initial segment of s1 consisting entirely of characters in s2.",
),
LibcFunctionDecl::new(
"strcspn", LibcHeader::String, SizeT,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Returns the length of the initial segment of s1 not containing any characters from s2.",
),
LibcFunctionDecl::new(
"strpbrk", LibcHeader::String, CharPtr,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Finds the first occurrence of any character from s2 in s1.",
),
LibcFunctionDecl::new(
"strstr", LibcHeader::String, CharPtr,
vec![ConstCharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Finds the first occurrence of substring s2 in s1.",
),
LibcFunctionDecl::new(
"strtok", LibcHeader::String, CharPtr,
vec![CharPtr, ConstCharPtr], false,
LibcBuiltinCategory::String,
"Tokenizes a string using delimiter characters (stateful).",
),
LibcFunctionDecl::new(
"strlen", LibcHeader::String, SizeT,
vec![ConstCharPtr], false,
LibcBuiltinCategory::String,
"Returns the length of a string (excluding the null terminator).",
),
LibcFunctionDecl::new(
"strerror", LibcHeader::String, CharPtr,
vec![Int], false,
LibcBuiltinCategory::Error,
"Returns a pointer to the textual description of an error code.",
),
LibcFunctionDecl::new(
"strxfrm", LibcHeader::String, SizeT,
vec![CharPtr, ConstCharPtr, SizeT], false,
LibcBuiltinCategory::String,
"Transforms a string for locale-dependent comparison. Returns needed buffer size.",
),
LibcFunctionDecl::new(
"memset_s", LibcHeader::String, Int,
vec![VoidPtr, SizeT, Int, SizeT], false,
LibcBuiltinCategory::String,
"Sets n bytes of memory to a value (bounds-checked).",
).with_introduced("C11"),
]
}
fn build_ctype_functions() -> Vec<LibcFunctionDecl> {
use CLibcType::*;
let ctype_io = |name: &'static str, doc: &'static str| -> LibcFunctionDecl {
LibcFunctionDecl::new(
name,
LibcHeader::Ctype,
Int,
vec![Int],
false,
LibcBuiltinCategory::Ctype,
doc,
)
};
vec![
ctype_io(
"isalnum",
"Tests whether a character is alphanumeric (A-Z, a-z, 0-9).",
),
ctype_io(
"isalpha",
"Tests whether a character is alphabetic (A-Z, a-z).",
),
ctype_io(
"isblank",
"Tests whether a character is a blank (space or tab).",
),
ctype_io(
"iscntrl",
"Tests whether a character is a control character (0x00-0x1F or 0x7F).",
),
ctype_io(
"isdigit",
"Tests whether a character is a decimal digit (0-9).",
),
ctype_io(
"isgraph",
"Tests whether a character has a graphical representation (printing except space).",
),
ctype_io(
"islower",
"Tests whether a character is a lowercase letter (a-z).",
),
ctype_io(
"isprint",
"Tests whether a character is printable (including space).",
),
ctype_io(
"ispunct",
"Tests whether a character is punctuation (printable, not alphanumeric or space).",
),
ctype_io(
"isspace",
"Tests whether a character is whitespace (space, tab, newline, etc.).",
),
ctype_io(
"isupper",
"Tests whether a character is an uppercase letter (A-Z).",
),
ctype_io(
"isxdigit",
"Tests whether a character is a hexadecimal digit (0-9, A-F, a-f).",
),
ctype_io(
"tolower",
"Converts an uppercase letter to lowercase. Returns unchanged if not uppercase.",
),
ctype_io(
"toupper",
"Converts a lowercase letter to uppercase. Returns unchanged if not lowercase.",
),
]
}
fn build_math_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"HUGE_VAL",
LibcHeader::Math,
"(__builtin_huge_val())",
"Positive double infinity (or very large value if infinity unsupported).",
),
LibcMacroDef::simple(
"HUGE_VALF",
LibcHeader::Math,
"(__builtin_huge_valf())",
"Positive float infinity.",
)
.with_introduced("C99"),
LibcMacroDef::simple(
"HUGE_VALL",
LibcHeader::Math,
"(__builtin_huge_vall())",
"Positive long double infinity.",
)
.with_introduced("C99"),
LibcMacroDef::simple(
"INFINITY",
LibcHeader::Math,
"(__builtin_inff())",
"Float representation of positive infinity.",
)
.with_introduced("C99"),
LibcMacroDef::simple(
"NAN",
LibcHeader::Math,
"(__builtin_nanf(\"\"))",
"Float representation of quiet NaN.",
)
.with_introduced("C99"),
LibcMacroDef::simple(
"FP_INFINITE",
LibcHeader::Math,
"1",
"Classification for infinity.",
),
LibcMacroDef::simple("FP_NAN", LibcHeader::Math, "0", "Classification for NaN."),
LibcMacroDef::simple(
"FP_NORMAL",
LibcHeader::Math,
"2",
"Classification for normal finite numbers.",
),
LibcMacroDef::simple(
"FP_SUBNORMAL",
LibcHeader::Math,
"3",
"Classification for subnormal (denormalized) numbers.",
),
LibcMacroDef::simple("FP_ZERO", LibcHeader::Math, "4", "Classification for zero."),
LibcMacroDef::simple(
"M_E",
LibcHeader::Math,
"2.7182818284590452354",
"The base of natural logarithms.",
),
LibcMacroDef::simple(
"M_LOG2E",
LibcHeader::Math,
"1.4426950408889634074",
"Logarithm base 2 of e.",
),
LibcMacroDef::simple(
"M_LOG10E",
LibcHeader::Math,
"0.43429448190325182765",
"Logarithm base 10 of e.",
),
LibcMacroDef::simple(
"M_LN2",
LibcHeader::Math,
"0.69314718055994530942",
"Natural logarithm of 2.",
),
LibcMacroDef::simple(
"M_LN10",
LibcHeader::Math,
"2.30258509299404568402",
"Natural logarithm of 10.",
),
LibcMacroDef::simple(
"M_PI",
LibcHeader::Math,
"3.14159265358979323846",
"The constant pi.",
),
LibcMacroDef::simple(
"M_PI_2",
LibcHeader::Math,
"1.57079632679489661923",
"Pi divided by 2.",
),
LibcMacroDef::simple(
"M_PI_4",
LibcHeader::Math,
"0.78539816339744830962",
"Pi divided by 4.",
),
LibcMacroDef::simple(
"M_1_PI",
LibcHeader::Math,
"0.31830988618379067154",
"1 divided by pi.",
),
LibcMacroDef::simple(
"M_2_PI",
LibcHeader::Math,
"0.63661977236758134308",
"2 divided by pi.",
),
LibcMacroDef::simple(
"M_2_SQRTPI",
LibcHeader::Math,
"1.12837916709551257390",
"2 divided by sqrt(pi).",
),
LibcMacroDef::simple(
"M_SQRT2",
LibcHeader::Math,
"1.41421356237309504880",
"Square root of 2.",
),
LibcMacroDef::simple(
"M_SQRT1_2",
LibcHeader::Math,
"0.70710678118654752440",
"1 divided by sqrt(2).",
),
]
}
fn build_math_functions() -> Vec<LibcFunctionDecl> {
use CLibcType::*;
let math1 = |name: &'static str, doc: &'static str| -> LibcFunctionDecl {
LibcFunctionDecl::new(
name,
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
doc,
)
};
let math2 = |name: &'static str, doc: &'static str| -> LibcFunctionDecl {
LibcFunctionDecl::new(
name,
LibcHeader::Math,
Double,
vec![Double, Double],
false,
LibcBuiltinCategory::Math,
doc,
)
};
vec![
math1("sin", "Sine of x (radians)."),
math1("cos", "Cosine of x (radians)."),
math1("tan", "Tangent of x (radians)."),
math1("asin", "Arc sine of x, result in radians [-pi/2, pi/2]."),
math1("acos", "Arc cosine of x, result in radians [0, pi]."),
math1("atan", "Arc tangent of x, result in radians [-pi/2, pi/2]."),
math2(
"atan2",
"Arc tangent of y/x, using signs to determine quadrant.",
),
math1("sinh", "Hyperbolic sine of x."),
math1("cosh", "Hyperbolic cosine of x."),
math1("tanh", "Hyperbolic tangent of x."),
math1("asinh", "Inverse hyperbolic sine of x.").with_introduced("C99"),
math1("acosh", "Inverse hyperbolic cosine of x.").with_introduced("C99"),
math1("atanh", "Inverse hyperbolic tangent of x.").with_introduced("C99"),
math1("exp", "Base-e exponential function, e^x."),
math1("exp2", "Base-2 exponential function, 2^x.").with_introduced("C99"),
math1("expm1", "e^x - 1, accurate for small x.").with_introduced("C99"),
math1("log", "Natural logarithm, ln(x)."),
math1("log10", "Base-10 logarithm."),
math1("log2", "Base-2 logarithm.").with_introduced("C99"),
math1("log1p", "ln(1+x), accurate for small x.").with_introduced("C99"),
math2("pow", "x raised to the power y."),
math1("sqrt", "Square root of x."),
math1("cbrt", "Cube root of x.").with_introduced("C99"),
math2("hypot", "sqrt(x^2 + y^2) without undue overflow.").with_introduced("C99"),
math1("ceil", "Smallest integer not less than x."),
math1("floor", "Largest integer not greater than x."),
math2("fmod", "Floating-point remainder of x/y."),
math1("fabs", "Absolute value of a double."),
LibcFunctionDecl::new(
"frexp",
LibcHeader::Math,
Double,
vec![Double, IntPtr],
false,
LibcBuiltinCategory::Math,
"Decomposes x into a normalized fraction and an integral power of 2.",
),
LibcFunctionDecl::new(
"ldexp",
LibcHeader::Math,
Double,
vec![Double, Int],
false,
LibcBuiltinCategory::Math,
"Multiplies a floating-point number by an integral power of 2: x * 2^exp.",
),
LibcFunctionDecl::new(
"modf",
LibcHeader::Math,
Double,
vec![
Double,
CLibcType::Array {
elem: Box::new(Double),
},
],
false,
LibcBuiltinCategory::Math,
"Decomposes x into its integral and fractional parts.",
),
LibcFunctionDecl::new(
"sinf",
LibcHeader::Math,
Float,
vec![Float],
false,
LibcBuiltinCategory::Math,
"Single-precision sine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"cosf",
LibcHeader::Math,
Float,
vec![Float],
false,
LibcBuiltinCategory::Math,
"Single-precision cosine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"sqrtf",
LibcHeader::Math,
Float,
vec![Float],
false,
LibcBuiltinCategory::Math,
"Single-precision square root.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"fabsf",
LibcHeader::Math,
Float,
vec![Float],
false,
LibcBuiltinCategory::Math,
"Single-precision absolute value.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"sinl",
LibcHeader::Math,
LongDouble,
vec![LongDouble],
false,
LibcBuiltinCategory::Math,
"Extended-precision sine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"cosl",
LibcHeader::Math,
LongDouble,
vec![LongDouble],
false,
LibcBuiltinCategory::Math,
"Extended-precision cosine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"sqrtl",
LibcHeader::Math,
LongDouble,
vec![LongDouble],
false,
LibcBuiltinCategory::Math,
"Extended-precision square root.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"fabsl",
LibcHeader::Math,
LongDouble,
vec![LongDouble],
false,
LibcBuiltinCategory::Math,
"Extended-precision absolute value.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"lround",
LibcHeader::Math,
Long,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Rounds to nearest long (halfway cases away from zero).",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"llround",
LibcHeader::Math,
LongLong,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Rounds to nearest long long (halfway cases away from zero).",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"trunc",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Rounds to integer toward zero.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"round",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Rounds to nearest integer (halfway cases away from zero).",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"nearbyint",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Rounds to integer using current rounding direction without raising inexact.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"rint",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Rounds to integer using current rounding direction.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"erf",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Error function.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"erfc",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Complementary error function.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"tgamma",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Gamma function.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"lgamma",
LibcHeader::Math,
Double,
vec![Double],
false,
LibcBuiltinCategory::Math,
"Natural logarithm of the absolute value of the gamma function.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"isgreater",
LibcHeader::Math,
Int,
vec![Double, Double],
false,
LibcBuiltinCategory::Math,
"Macro: (x) > (y) without raising invalid for unordered.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"isgreaterequal",
LibcHeader::Math,
Int,
vec![Double, Double],
false,
LibcBuiltinCategory::Math,
"Macro: (x) >= (y) without raising invalid for unordered.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"isless",
LibcHeader::Math,
Int,
vec![Double, Double],
false,
LibcBuiltinCategory::Math,
"Macro: (x) < (y) without raising invalid for unordered.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"islessequal",
LibcHeader::Math,
Int,
vec![Double, Double],
false,
LibcBuiltinCategory::Math,
"Macro: (x) <= (y) without raising invalid for unordered.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"islessgreater",
LibcHeader::Math,
Int,
vec![Double, Double],
false,
LibcBuiltinCategory::Math,
"Macro: (x) < (y) || (x) > (y) without raising invalid.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"isunordered",
LibcHeader::Math,
Int,
vec![Double, Double],
false,
LibcBuiltinCategory::Math,
"Macro: true if either argument is NaN.",
)
.with_introduced("C99"),
]
}
fn build_errno_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"EDOM",
LibcHeader::Errno,
"1",
"Domain error (input argument is outside the function's domain).",
),
LibcMacroDef::simple(
"ERANGE",
LibcHeader::Errno,
"2",
"Range error (result is too large to be represented).",
),
LibcMacroDef::simple(
"EILSEQ",
LibcHeader::Errno,
"3",
"Illegal byte sequence (multibyte/wide-character conversion error).",
),
]
}
fn build_errno_globals() -> Vec<LibcGlobalDecl> {
vec![LibcGlobalDecl::new(
"errno",
LibcHeader::Errno,
CLibcType::Int,
"Global variable set by system calls and library functions to indicate what went wrong.",
)]
}
fn build_assert_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::function_like(
"assert", LibcHeader::Assert,
"If NDEBUG is not defined, evaluates expression and if false, prints diagnostic and calls abort().",
),
LibcMacroDef::simple(
"static_assert", LibcHeader::Assert,
"_Static_assert",
"Compile-time assertion (C11 keyword; provided for compatibility).",
),
]
}
fn build_time_types() -> Vec<LibcTypeDef> {
vec![
LibcTypeDef::new(
"clock_t", LibcHeader::Time, CLibcType::ClockT,
"Arithmetic type representing processor time.",
),
LibcTypeDef::new(
"time_t", LibcHeader::Time, CLibcType::TimeT,
"Arithmetic type representing calendar time.",
),
LibcTypeDef::new(
"size_t", LibcHeader::Time, CLibcType::SizeT,
"Unsigned integral type for object sizes.",
),
LibcTypeDef::new(
"struct tm", LibcHeader::Time, CLibcType::TmPtr,
"Structure holding broken-down calendar time (tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst).",
),
]
}
fn build_time_macros() -> Vec<LibcMacroDef> {
vec![LibcMacroDef::simple(
"CLOCKS_PER_SEC",
LibcHeader::Time,
"1000000",
"Number of clock ticks per second (POSIX: 1,000,000).",
)]
}
fn build_time_functions() -> Vec<LibcFunctionDecl> {
use CLibcType::*;
vec![
LibcFunctionDecl::new(
"clock", LibcHeader::Time, ClockT,
vec![], false,
LibcBuiltinCategory::Time,
"Returns the processor time consumed by the program.",
),
LibcFunctionDecl::new(
"time", LibcHeader::Time, TimeT,
vec![CLibcType::Array { elem: Box::new(TimeT) }], false,
LibcBuiltinCategory::Time,
"Returns the current calendar time. If arg is not NULL, also stores it there.",
),
LibcFunctionDecl::new(
"difftime", LibcHeader::Time, Double,
vec![TimeT, TimeT], false,
LibcBuiltinCategory::Time,
"Returns the difference between two calendar times in seconds as a double.",
),
LibcFunctionDecl::new(
"mktime", LibcHeader::Time, TimeT,
vec![TmPtr], false,
LibcBuiltinCategory::Time,
"Converts a struct tm (local time) to a calendar time (time_t) and normalizes fields.",
),
LibcFunctionDecl::new(
"asctime", LibcHeader::Time, CharPtr,
vec![TmPtr], false,
LibcBuiltinCategory::Time,
"Converts a struct tm to a string representation (DEPRECATED: use strftime).",
),
LibcFunctionDecl::new(
"ctime", LibcHeader::Time, CharPtr,
vec![CLibcType::Array { elem: Box::new(TimeT) }], false,
LibcBuiltinCategory::Time,
"Converts a calendar time to a string representation (DEPRECATED: use strftime).",
),
LibcFunctionDecl::new(
"gmtime", LibcHeader::Time, TmPtr,
vec![CLibcType::Array { elem: Box::new(TimeT) }], false,
LibcBuiltinCategory::Time,
"Converts a calendar time to UTC (broken-down time). Returns pointer to static storage.",
),
LibcFunctionDecl::new(
"localtime", LibcHeader::Time, TmPtr,
vec![CLibcType::Array { elem: Box::new(TimeT) }], false,
LibcBuiltinCategory::Time,
"Converts a calendar time to local broken-down time. Returns pointer to static storage.",
),
LibcFunctionDecl::new(
"strftime", LibcHeader::Time, SizeT,
vec![CharPtr, SizeT, ConstCharPtr, TmPtr], false,
LibcBuiltinCategory::Time,
"Formats a struct tm into a string according to a format specifier (%Y, %m, %d, %H:%M:%S, etc.).",
),
LibcFunctionDecl::new(
"clock_gettime", LibcHeader::Time, Int,
vec![ClockT, CLibcType::FnPtr {
ret: Box::new(Void),
params: vec![],
}],
false,
LibcBuiltinCategory::Time,
"Gets the current time of a specified clock (POSIX extension).",
).with_introduced("POSIX"),
LibcFunctionDecl::new(
"timespec_get", LibcHeader::Time, Int,
vec![TmPtr, Int], false,
LibcBuiltinCategory::Time,
"Sets the timespec to the current calendar time based on a time base.",
).with_introduced("C11"),
]
}
fn build_setjmp_types() -> Vec<LibcTypeDef> {
vec![LibcTypeDef::new(
"jmp_buf",
LibcHeader::Setjmp,
CLibcType::JmpBuf,
"Array type suitable for holding the information needed to restore a calling environment.",
)]
}
fn build_setjmp_macros() -> Vec<LibcMacroDef> {
vec![]
}
fn build_setjmp_functions() -> Vec<LibcFunctionDecl> {
vec![
LibcFunctionDecl::new(
"setjmp",
LibcHeader::Setjmp,
Int,
vec![CLibcType::JmpBuf],
false,
LibcBuiltinCategory::Other,
"Saves the calling environment for later use by longjmp.",
)
.with_returns_twice(),
LibcFunctionDecl::new(
"longjmp",
LibcHeader::Setjmp,
Void,
vec![CLibcType::JmpBuf, Int],
false,
LibcBuiltinCategory::Process,
"Restores the environment saved by setjmp.",
)
.with_noreturn(),
]
}
fn build_setjmp_globals() -> Vec<LibcGlobalDecl> {
vec![]
}
fn build_signal_types() -> Vec<LibcTypeDef> {
vec![
LibcTypeDef::new("sig_atomic_t", LibcHeader::Signal, CLibcType::SigAtomicT,
"Integer type that can be accessed as an atomic entity even in the presence of asynchronous interrupts."),
]
}
fn build_signal_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"SIG_DFL",
LibcHeader::Signal,
"((void(*)(int))0)",
"Default signal handling.",
),
LibcMacroDef::simple(
"SIG_ERR",
LibcHeader::Signal,
"((void(*)(int))-1)",
"Signal error return value.",
),
LibcMacroDef::simple(
"SIG_IGN",
LibcHeader::Signal,
"((void(*)(int))1)",
"Ignore signal.",
),
LibcMacroDef::simple(
"SIGABRT",
LibcHeader::Signal,
"6",
"Abnormal termination signal.",
),
LibcMacroDef::simple(
"SIGFPE",
LibcHeader::Signal,
"8",
"Erroneous arithmetic operation signal.",
),
LibcMacroDef::simple(
"SIGILL",
LibcHeader::Signal,
"4",
"Illegal instruction signal.",
),
LibcMacroDef::simple(
"SIGINT",
LibcHeader::Signal,
"2",
"Interactive attention signal (Ctrl+C).",
),
LibcMacroDef::simple(
"SIGSEGV",
LibcHeader::Signal,
"11",
"Invalid memory access signal.",
),
LibcMacroDef::simple(
"SIGTERM",
LibcHeader::Signal,
"15",
"Termination request signal.",
),
]
}
fn build_signal_functions() -> Vec<LibcFunctionDecl> {
vec![
LibcFunctionDecl::new(
"signal",
LibcHeader::Signal,
CLibcType::FnPtr {
ret: Box::new(Void),
params: vec![Int],
},
vec![
Int,
CLibcType::FnPtr {
ret: Box::new(Void),
params: vec![Int],
},
],
false,
LibcBuiltinCategory::Other,
"Sets a function to handle a signal.",
),
LibcFunctionDecl::new(
"raise",
LibcHeader::Signal,
Int,
vec![Int],
false,
LibcBuiltinCategory::Process,
"Sends a signal to the executing program.",
),
]
}
fn build_signal_globals() -> Vec<LibcGlobalDecl> {
vec![]
}
fn build_locale_types() -> Vec<LibcTypeDef> {
vec![LibcTypeDef::new(
"struct lconv",
LibcHeader::Locale,
CLibcType::LconvPtr,
"Structure containing formatting information for numeric and monetary values.",
)]
}
fn build_locale_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"LC_ALL",
LibcHeader::Locale,
"0",
"Selects the entire locale.",
),
LibcMacroDef::simple(
"LC_COLLATE",
LibcHeader::Locale,
"1",
"Selects the collation category of the locale.",
),
LibcMacroDef::simple(
"LC_CTYPE",
LibcHeader::Locale,
"2",
"Selects the character classification category of the locale.",
),
LibcMacroDef::simple(
"LC_MONETARY",
LibcHeader::Locale,
"3",
"Selects the monetary formatting category of the locale.",
),
LibcMacroDef::simple(
"LC_NUMERIC",
LibcHeader::Locale,
"4",
"Selects the numeric formatting category of the locale.",
),
LibcMacroDef::simple(
"LC_TIME",
LibcHeader::Locale,
"5",
"Selects the time formatting category of the locale.",
),
]
}
fn build_locale_functions() -> Vec<LibcFunctionDecl> {
vec![
LibcFunctionDecl::new(
"setlocale",
LibcHeader::Locale,
CharPtr,
vec![Int, ConstCharPtr],
false,
LibcBuiltinCategory::Other,
"Sets or queries the program's current locale.",
),
LibcFunctionDecl::new(
"localeconv",
LibcHeader::Locale,
CLibcType::LconvPtr,
vec![],
false,
LibcBuiltinCategory::Other,
"Returns a pointer to a struct lconv with locale formatting information.",
),
]
}
fn build_locale_globals() -> Vec<LibcGlobalDecl> {
vec![]
}
fn build_wchar_types() -> Vec<LibcTypeDef> {
use crate::clang::clang_libc::CLibcType::*;
vec![
LibcTypeDef::new("wchar_t", LibcHeader::Wchar, WCharT, "Wide character type."),
LibcTypeDef::new(
"wint_t",
LibcHeader::Wchar,
WIntT,
"Integer type that can hold any wide character and WEOF.",
),
LibcTypeDef::new(
"size_t",
LibcHeader::Wchar,
SizeT,
"Unsigned integer type for sizes (also defined in stddef.h).",
),
LibcTypeDef::new(
"mbstate_t",
LibcHeader::Wchar,
MbstateT,
"Type representing multibyte conversion state.",
),
LibcTypeDef::new(
"FILE",
LibcHeader::Wchar,
FilePtr,
"Type for file streams (also defined in stdio.h).",
),
]
}
fn build_wchar_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"WEOF",
LibcHeader::Wchar,
"((wint_t)(-1))",
"Non-character value returned by several wide functions to indicate end-of-file.",
),
LibcMacroDef::simple(
"NULL",
LibcHeader::Wchar,
"((void*)0)",
"Null pointer constant (also defined in stddef.h).",
),
]
}
fn build_wchar_functions() -> Vec<LibcFunctionDecl> {
vec![
LibcFunctionDecl::new(
"iswalnum",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is alphanumeric.",
),
LibcFunctionDecl::new(
"iswalpha",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is alphabetic.",
),
LibcFunctionDecl::new(
"iswblank",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is blank.",
),
LibcFunctionDecl::new(
"iswcntrl",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is a control character.",
),
LibcFunctionDecl::new(
"iswdigit",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is a decimal digit.",
),
LibcFunctionDecl::new(
"iswgraph",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character has a graphical representation.",
),
LibcFunctionDecl::new(
"iswlower",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is lowercase.",
),
LibcFunctionDecl::new(
"iswprint",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is printable.",
),
LibcFunctionDecl::new(
"iswpunct",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is punctuation.",
),
LibcFunctionDecl::new(
"iswspace",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is whitespace.",
),
LibcFunctionDecl::new(
"iswupper",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is uppercase.",
),
LibcFunctionDecl::new(
"iswxdigit",
LibcHeader::Wchar,
Int,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Checks if the wide character is a hexadecimal digit.",
),
LibcFunctionDecl::new(
"towlower",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Converts wide character to lowercase.",
),
LibcFunctionDecl::new(
"towupper",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![CLibcType::WIntT],
false,
LibcBuiltinCategory::Ctype,
"Converts wide character to uppercase.",
),
LibcFunctionDecl::new(
"fwprintf",
LibcHeader::Wchar,
Int,
vec![CLibcType::FilePtr, ConstCharPtr],
true,
LibcBuiltinCategory::Io,
"Writes formatted wide-character output to a file stream.",
),
LibcFunctionDecl::new(
"fwscanf",
LibcHeader::Wchar,
Int,
vec![CLibcType::FilePtr, ConstCharPtr],
true,
LibcBuiltinCategory::Io,
"Reads formatted wide-character input from a file stream.",
),
LibcFunctionDecl::new(
"wprintf",
LibcHeader::Wchar,
Int,
vec![ConstCharPtr],
true,
LibcBuiltinCategory::Io,
"Prints formatted wide-character output to stdout.",
),
LibcFunctionDecl::new(
"wscanf",
LibcHeader::Wchar,
Int,
vec![ConstCharPtr],
true,
LibcBuiltinCategory::Io,
"Reads formatted wide-character input from stdin.",
),
LibcFunctionDecl::new(
"swprintf",
LibcHeader::Wchar,
Int,
vec![CLibcType::WCharT, CLibcType::SizeT, ConstCharPtr],
true,
LibcBuiltinCategory::Io,
"Writes formatted wide-character output to a wide string.",
),
LibcFunctionDecl::new(
"swscanf",
LibcHeader::Wchar,
Int,
vec![CLibcType::WCharT, ConstCharPtr],
true,
LibcBuiltinCategory::Io,
"Reads formatted wide-character input from a wide string.",
),
LibcFunctionDecl::new(
"fgetwc",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![CLibcType::FilePtr],
false,
LibcBuiltinCategory::Io,
"Reads a wide character from a file stream.",
),
LibcFunctionDecl::new(
"fputwc",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![CLibcType::WCharT, CLibcType::FilePtr],
false,
LibcBuiltinCategory::Io,
"Writes a wide character to a file stream.",
),
LibcFunctionDecl::new(
"getwc",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![CLibcType::FilePtr],
false,
LibcBuiltinCategory::Io,
"Reads a wide character from a file stream (macro version).",
),
LibcFunctionDecl::new(
"putwc",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![CLibcType::WCharT, CLibcType::FilePtr],
false,
LibcBuiltinCategory::Io,
"Writes a wide character to a file stream (macro version).",
),
LibcFunctionDecl::new(
"getwchar",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![],
false,
LibcBuiltinCategory::Io,
"Reads a wide character from stdin.",
),
LibcFunctionDecl::new(
"putwchar",
LibcHeader::Wchar,
CLibcType::WIntT,
vec![CLibcType::WCharT],
false,
LibcBuiltinCategory::Io,
"Writes a wide character to stdout.",
),
LibcFunctionDecl::new(
"mblen",
LibcHeader::Wchar,
Int,
vec![ConstCharPtr, CLibcType::SizeT],
false,
LibcBuiltinCategory::Conversion,
"Determines the length of a multibyte character.",
),
LibcFunctionDecl::new(
"mbtowc",
LibcHeader::Wchar,
Int,
vec![CLibcType::WCharT, ConstCharPtr, CLibcType::SizeT],
false,
LibcBuiltinCategory::Conversion,
"Converts a multibyte character to a wide character.",
),
LibcFunctionDecl::new(
"wctomb",
LibcHeader::Wchar,
Int,
vec![CharPtr, CLibcType::WCharT],
false,
LibcBuiltinCategory::Conversion,
"Converts a wide character to multibyte.",
),
LibcFunctionDecl::new(
"mbstowcs",
LibcHeader::Wchar,
CLibcType::SizeT,
vec![CLibcType::WCharT, ConstCharPtr, CLibcType::SizeT],
false,
LibcBuiltinCategory::Conversion,
"Converts a multibyte string to wide-character string.",
),
LibcFunctionDecl::new(
"wcstombs",
LibcHeader::Wchar,
CLibcType::SizeT,
vec![CharPtr, CLibcType::WCharT, CLibcType::SizeT],
false,
LibcBuiltinCategory::Conversion,
"Converts a wide-character string to multibyte string.",
),
]
}
fn build_wchar_globals() -> Vec<LibcGlobalDecl> {
vec![]
}
fn build_inttypes_types() -> Vec<LibcTypeDef> {
vec![LibcTypeDef::new(
"imaxdiv_t",
LibcHeader::Inttypes,
CLibcType::IMaxDivT,
"Structure type returned by imaxdiv.",
)]
}
fn build_inttypes_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"PRId8",
LibcHeader::Inttypes,
"\"d\"",
"printf format for int8_t",
),
LibcMacroDef::simple(
"PRId16",
LibcHeader::Inttypes,
"\"d\"",
"printf format for int16_t",
),
LibcMacroDef::simple(
"PRId32",
LibcHeader::Inttypes,
"\"d\"",
"printf format for int32_t",
),
LibcMacroDef::simple(
"PRId64",
LibcHeader::Inttypes,
"\"ld\"",
"printf format for int64_t",
),
LibcMacroDef::simple(
"PRIi8",
LibcHeader::Inttypes,
"\"i\"",
"printf format for int8_t",
),
LibcMacroDef::simple(
"PRIi16",
LibcHeader::Inttypes,
"\"i\"",
"printf format for int16_t",
),
LibcMacroDef::simple(
"PRIi32",
LibcHeader::Inttypes,
"\"i\"",
"printf format for int32_t",
),
LibcMacroDef::simple(
"PRIi64",
LibcHeader::Inttypes,
"\"li\"",
"printf format for int64_t",
),
LibcMacroDef::simple(
"PRIu8",
LibcHeader::Inttypes,
"\"u\"",
"printf format for uint8_t",
),
LibcMacroDef::simple(
"PRIu16",
LibcHeader::Inttypes,
"\"u\"",
"printf format for uint16_t",
),
LibcMacroDef::simple(
"PRIu32",
LibcHeader::Inttypes,
"\"u\"",
"printf format for uint32_t",
),
LibcMacroDef::simple(
"PRIu64",
LibcHeader::Inttypes,
"\"lu\"",
"printf format for uint64_t",
),
LibcMacroDef::simple(
"PRIx8",
LibcHeader::Inttypes,
"\"x\"",
"printf hex format for uint8_t",
),
LibcMacroDef::simple(
"PRIx16",
LibcHeader::Inttypes,
"\"x\"",
"printf hex format for uint16_t",
),
LibcMacroDef::simple(
"PRIx32",
LibcHeader::Inttypes,
"\"x\"",
"printf hex format for uint32_t",
),
LibcMacroDef::simple(
"PRIx64",
LibcHeader::Inttypes,
"\"lx\"",
"printf hex format for uint64_t",
),
LibcMacroDef::simple(
"SCNd8",
LibcHeader::Inttypes,
"\"hhd\"",
"scanf format for int8_t",
),
LibcMacroDef::simple(
"SCNd16",
LibcHeader::Inttypes,
"\"hd\"",
"scanf format for int16_t",
),
LibcMacroDef::simple(
"SCNd32",
LibcHeader::Inttypes,
"\"d\"",
"scanf format for int32_t",
),
LibcMacroDef::simple(
"SCNd64",
LibcHeader::Inttypes,
"\"ld\"",
"scanf format for int64_t",
),
LibcMacroDef::simple(
"PRIdMAX",
LibcHeader::Inttypes,
"\"jd\"",
"printf format for intmax_t",
),
LibcMacroDef::simple(
"PRIuMAX",
LibcHeader::Inttypes,
"\"ju\"",
"printf format for uintmax_t",
),
LibcMacroDef::simple(
"SCNdMAX",
LibcHeader::Inttypes,
"\"jd\"",
"scanf format for intmax_t",
),
LibcMacroDef::simple(
"SCNuMAX",
LibcHeader::Inttypes,
"\"ju\"",
"scanf format for uintmax_t",
),
LibcMacroDef::simple(
"PRIdPTR",
LibcHeader::Inttypes,
"\"ld\"",
"printf format for intptr_t",
),
LibcMacroDef::simple(
"PRIuPTR",
LibcHeader::Inttypes,
"\"lu\"",
"printf format for uintptr_t",
),
]
}
fn build_inttypes_functions() -> Vec<LibcFunctionDecl> {
vec![
LibcFunctionDecl::new(
"imaxabs",
LibcHeader::Inttypes,
CLibcType::LongLong,
vec![CLibcType::LongLong],
false,
LibcBuiltinCategory::Math,
"Computes the absolute value of an intmax_t.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"imaxdiv",
LibcHeader::Inttypes,
CLibcType::IMaxDivT,
vec![CLibcType::LongLong, CLibcType::LongLong],
false,
LibcBuiltinCategory::Math,
"Computes quotient and remainder of intmax_t division.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"strtoimax",
LibcHeader::Inttypes,
CLibcType::LongLong,
vec![ConstCharPtr, CharPtr, Int],
false,
LibcBuiltinCategory::Conversion,
"Converts a string to intmax_t.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"strtoumax",
LibcHeader::Inttypes,
CLibcType::ULongLong,
vec![ConstCharPtr, CharPtr, Int],
false,
LibcBuiltinCategory::Conversion,
"Converts a string to uintmax_t.",
)
.with_introduced("C99"),
]
}
fn build_inttypes_globals() -> Vec<LibcGlobalDecl> {
vec![]
}
fn build_stdatomic_types() -> Vec<LibcTypeDef> {
vec![
LibcTypeDef::new(
"atomic_flag",
LibcHeader::Stdatomic,
CLibcType::Int,
"Lock-free atomic boolean flag.",
),
LibcTypeDef::new(
"memory_order",
LibcHeader::Stdatomic,
CLibcType::Int,
"Enumeration type for memory ordering constraints.",
),
]
}
fn build_stdatomic_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"ATOMIC_BOOL_LOCK_FREE",
LibcHeader::Stdatomic,
"1",
"Indicates lock-free property of atomic_bool.",
),
LibcMacroDef::simple(
"ATOMIC_CHAR_LOCK_FREE",
LibcHeader::Stdatomic,
"1",
"Indicates lock-free property of atomic_char.",
),
LibcMacroDef::simple(
"ATOMIC_INT_LOCK_FREE",
LibcHeader::Stdatomic,
"1",
"Indicates lock-free property of atomic_int.",
),
LibcMacroDef::simple(
"ATOMIC_LONG_LOCK_FREE",
LibcHeader::Stdatomic,
"1",
"Indicates lock-free property of atomic_long.",
),
LibcMacroDef::simple(
"ATOMIC_POINTER_LOCK_FREE",
LibcHeader::Stdatomic,
"1",
"Indicates lock-free property of atomic pointers.",
),
LibcMacroDef::simple(
"ATOMIC_FLAG_INIT",
LibcHeader::Stdatomic,
"{0}",
"Initializer for atomic_flag.",
),
LibcMacroDef::simple(
"ATOMIC_VAR_INIT",
LibcHeader::Stdatomic,
"(value)",
"Initializer for atomic variables.",
),
LibcMacroDef::simple(
"kill_dependency",
LibcHeader::Stdatomic,
"(y)",
"Breaks a dependency chain for memory_order_consume.",
),
LibcMacroDef::simple(
"memory_order_relaxed",
LibcHeader::Stdatomic,
"0",
"Relaxed memory order.",
),
LibcMacroDef::simple(
"memory_order_consume",
LibcHeader::Stdatomic,
"1",
"Consume memory order.",
),
LibcMacroDef::simple(
"memory_order_acquire",
LibcHeader::Stdatomic,
"2",
"Acquire memory order.",
),
LibcMacroDef::simple(
"memory_order_release",
LibcHeader::Stdatomic,
"3",
"Release memory order.",
),
LibcMacroDef::simple(
"memory_order_acq_rel",
LibcHeader::Stdatomic,
"4",
"Acquire-release memory order.",
),
LibcMacroDef::simple(
"memory_order_seq_cst",
LibcHeader::Stdatomic,
"5",
"Sequentially consistent memory order.",
),
]
}
fn build_stdatomic_functions() -> Vec<LibcFunctionDecl> {
vec![
LibcFunctionDecl::new(
"atomic_flag_test_and_set",
LibcHeader::Stdatomic,
CLibcType::Int,
vec![CLibcType::IntPtr],
false,
LibcBuiltinCategory::Other,
"Atomically tests and sets an atomic flag.",
),
LibcFunctionDecl::new(
"atomic_flag_clear",
LibcHeader::Stdatomic,
Void,
vec![CLibcType::IntPtr],
false,
LibcBuiltinCategory::Other,
"Atomically clears an atomic flag.",
),
LibcFunctionDecl::new(
"atomic_init",
LibcHeader::Stdatomic,
Void,
vec![VoidPtr, CLibcType::Int],
false,
LibcBuiltinCategory::Other,
"Initializes an atomic object (generic).",
),
LibcFunctionDecl::new(
"atomic_load",
LibcHeader::Stdatomic,
CLibcType::Int,
vec![VoidPtr],
false,
LibcBuiltinCategory::Other,
"Atomically loads a value from an atomic object (generic).",
),
LibcFunctionDecl::new(
"atomic_store",
LibcHeader::Stdatomic,
Void,
vec![VoidPtr, CLibcType::Int],
false,
LibcBuiltinCategory::Other,
"Atomically stores a value in an atomic object (generic).",
),
LibcFunctionDecl::new(
"atomic_exchange",
LibcHeader::Stdatomic,
CLibcType::Int,
vec![VoidPtr, CLibcType::Int],
false,
LibcBuiltinCategory::Other,
"Atomically exchanges a value with an atomic object (generic).",
),
LibcFunctionDecl::new(
"atomic_compare_exchange_strong",
LibcHeader::Stdatomic,
Int,
vec![VoidPtr, VoidPtr, CLibcType::Int],
false,
LibcBuiltinCategory::Other,
"Atomically compares and exchanges (strong) (generic).",
),
LibcFunctionDecl::new(
"atomic_compare_exchange_weak",
LibcHeader::Stdatomic,
Int,
vec![VoidPtr, VoidPtr, CLibcType::Int],
false,
LibcBuiltinCategory::Other,
"Atomically compares and exchanges (weak) (generic).",
),
LibcFunctionDecl::new(
"atomic_fetch_add",
LibcHeader::Stdatomic,
CLibcType::Int,
vec![VoidPtr, CLibcType::Int],
false,
LibcBuiltinCategory::Other,
"Atomically adds a value (generic).",
),
LibcFunctionDecl::new(
"atomic_fetch_sub",
LibcHeader::Stdatomic,
CLibcType::Int,
vec![VoidPtr, CLibcType::Int],
false,
LibcBuiltinCategory::Other,
"Atomically subtracts a value (generic).",
),
]
}
fn build_stdatomic_globals() -> Vec<LibcGlobalDecl> {
vec![]
}
fn build_complex_types() -> Vec<LibcTypeDef> {
vec![]
}
fn build_complex_macros() -> Vec<LibcMacroDef> {
vec![
LibcMacroDef::simple(
"complex",
LibcHeader::Complex,
"_Complex",
"Convenience macro for _Complex.",
),
LibcMacroDef::simple(
"_Complex_I",
LibcHeader::Complex,
"((const float _Complex){0.0f, 1.0f})",
"The imaginary unit, as a const float _Complex.",
),
LibcMacroDef::simple(
"I",
LibcHeader::Complex,
"_Complex_I",
"The imaginary unit (same as _Complex_I).",
),
LibcMacroDef::simple(
"CMPLX",
LibcHeader::Complex,
"((double _Complex)((double)(x) + _Complex_I * (double)(y)))",
"Constructs a double _Complex from real and imaginary parts.",
),
LibcMacroDef::simple(
"CMPLXF",
LibcHeader::Complex,
"((float _Complex)((float)(x) + _Complex_I * (float)(y)))",
"Constructs a float _Complex from real and imaginary parts.",
),
LibcMacroDef::simple(
"CMPLXL",
LibcHeader::Complex,
"((long double _Complex)((long double)(x) + _Complex_I * (long double)(y)))",
"Constructs a long double _Complex from real and imaginary parts.",
),
]
}
fn build_complex_functions() -> Vec<LibcFunctionDecl> {
vec![
LibcFunctionDecl::new(
"cabs",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex absolute value.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"carg",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex argument (phase angle).",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"cimag",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Imaginary part of a complex number.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"creal",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Real part of a complex number.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"conj",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex conjugate.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"cproj",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Riemann sphere projection.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"cexp",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex exponential.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"clog",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex natural logarithm.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"csqrt",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex square root.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"cpow",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex power function.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"csin",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex sine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"ccos",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex cosine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"ctan",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex tangent.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"casin",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex arc sine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"cacos",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex arc cosine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"catan",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex arc tangent.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"csinh",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex hyperbolic sine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"ccosh",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex hyperbolic cosine.",
)
.with_introduced("C99"),
LibcFunctionDecl::new(
"ctanh",
LibcHeader::Complex,
Double,
vec![],
false,
LibcBuiltinCategory::Math,
"Complex hyperbolic tangent.",
)
.with_introduced("C99"),
]
}
fn build_complex_globals() -> Vec<LibcGlobalDecl> {
vec![]
}
#[derive(Debug, Clone)]
pub struct LibcRegistry {
pub types: Vec<LibcTypeDef>,
pub macros: Vec<LibcMacroDef>,
pub functions: Vec<LibcFunctionDecl>,
pub globals: Vec<LibcGlobalDecl>,
}
impl LibcRegistry {
pub fn new() -> Self {
let mut types = Vec::new();
types.extend(build_stdio_types());
types.extend(build_stdlib_types());
types.extend(build_string_types());
types.extend(build_time_types());
types.extend(build_setjmp_types());
types.extend(build_signal_types());
types.extend(build_locale_types());
types.extend(build_wchar_types());
types.extend(build_inttypes_types());
types.extend(build_stdatomic_types());
types.extend(build_complex_types());
let mut macros = Vec::new();
macros.extend(build_stdio_macros());
macros.extend(build_stdlib_macros());
macros.extend(build_math_macros());
macros.extend(build_errno_macros());
macros.extend(build_assert_macros());
macros.extend(build_time_macros());
macros.extend(build_setjmp_macros());
macros.extend(build_signal_macros());
macros.extend(build_locale_macros());
macros.extend(build_wchar_macros());
macros.extend(build_inttypes_macros());
macros.extend(build_stdatomic_macros());
macros.extend(build_complex_macros());
let mut functions = Vec::new();
functions.extend(build_stdio_functions());
functions.extend(build_stdlib_functions());
functions.extend(build_string_functions());
functions.extend(build_ctype_functions());
functions.extend(build_math_functions());
functions.extend(build_time_functions());
functions.extend(build_setjmp_functions());
functions.extend(build_signal_functions());
functions.extend(build_locale_functions());
functions.extend(build_wchar_functions());
functions.extend(build_inttypes_functions());
functions.extend(build_stdatomic_functions());
functions.extend(build_complex_functions());
let mut globals = Vec::new();
globals.extend(build_stdio_globals());
globals.extend(build_errno_globals());
globals.extend(build_setjmp_globals());
globals.extend(build_signal_globals());
globals.extend(build_locale_globals());
globals.extend(build_wchar_globals());
globals.extend(build_inttypes_globals());
globals.extend(build_stdatomic_globals());
globals.extend(build_complex_globals());
LibcRegistry {
types,
macros,
functions,
globals,
}
}
pub fn functions_in_header(&self, header: LibcHeader) -> Vec<&LibcFunctionDecl> {
self.functions
.iter()
.filter(|f| f.header == header)
.collect()
}
pub fn lookup_function(&self, name: &str) -> Option<&LibcFunctionDecl> {
self.functions.iter().find(|f| f.name == name)
}
pub fn lookup_macro(&self, name: &str) -> Option<&LibcMacroDef> {
self.macros.iter().find(|m| m.name == name)
}
pub fn lookup_type(&self, name: &str) -> Option<&LibcTypeDef> {
self.types.iter().find(|t| t.name == name)
}
pub fn lookup_global(&self, name: &str) -> Option<&LibcGlobalDecl> {
self.globals.iter().find(|g| g.name == name)
}
pub fn function_count(&self) -> usize {
self.functions.len()
}
pub fn macro_count(&self) -> usize {
self.macros.len()
}
pub fn type_count(&self) -> usize {
self.types.len()
}
pub fn global_count(&self) -> usize {
self.globals.len()
}
pub fn function_counts_by_header(&self) -> Vec<(LibcHeader, usize)> {
LibcHeader::all()
.iter()
.map(|h| (*h, self.functions.iter().filter(|f| f.header == *h).count()))
.collect()
}
pub fn generate_header(&self, header: LibcHeader) -> String {
let mut s = String::new();
let guard = format!("_LIBC_BUILTIN_{}_H_", header.short_name().to_uppercase());
s.push_str(&format!("#ifndef {}\n", guard));
s.push_str(&format!("#define {}\n\n", guard));
if header != LibcHeader::Assert && header != LibcHeader::Ctype {
s.push_str("#define NULL ((void*)0)\n\n");
}
for t in &self.types {
if t.header == header {
if t.name == "struct tm" {
s.push_str("struct tm {\n");
s.push_str(" int tm_sec; /* seconds [0,60] */\n");
s.push_str(" int tm_min; /* minutes [0,59] */\n");
s.push_str(" int tm_hour; /* hours [0,23] */\n");
s.push_str(" int tm_mday; /* day of month [1,31] */\n");
s.push_str(" int tm_mon; /* month [0,11] */\n");
s.push_str(" int tm_year; /* years since 1900 */\n");
s.push_str(" int tm_wday; /* day of week [0,6] (Sun=0) */\n");
s.push_str(" int tm_yday; /* day of year [0,365] */\n");
s.push_str(" int tm_isdst; /* daylight saving flag */\n");
s.push_str("};\n\n");
} else if t.name == "div_t" {
s.push_str("typedef struct { int quot; int rem; } div_t;\n\n");
} else if t.name == "ldiv_t" {
s.push_str("typedef struct { long quot; long rem; } ldiv_t;\n\n");
} else if t.name == "lldiv_t" {
s.push_str("typedef struct { long long quot; long long rem; } lldiv_t;\n\n");
} else if t.name == "FILE" {
s.push_str("typedef struct __FILE FILE;\n\n");
} else {
s.push_str(&format!(
"typedef {} {}; /* {} */\n\n",
t.underlying.as_str(),
t.name,
t.doc
));
}
}
}
for m in &self.macros {
if m.header == header {
if let Some(ref repl) = m.replacement {
s.push_str(&format!("#define {} {}\n", m.name, repl));
} else if m.is_function_like {
if m.name == "assert" {
s.push_str("#ifdef NDEBUG\n");
s.push_str("#define assert(expr) ((void)0)\n");
s.push_str("#else\n");
s.push_str("#define assert(expr) \\\n");
s.push_str(" ((expr) ? (void)0 : \\\n");
s.push_str(" __assert_fail(#expr, __FILE__, __LINE__, __func__))\n");
s.push_str("#endif\n\n");
} else {
s.push_str(&format!("/* function-like macro: {} */\n", m.name));
}
}
}
}
for g in &self.globals {
if g.header == header {
s.push_str(&format!(
"extern {} {}; /* {} */\n",
g.ty.as_str(),
g.name,
g.doc
));
}
}
if self.globals.iter().any(|g| g.header == header) {
s.push('\n');
}
for f in &self.functions {
if f.header == header {
s.push_str(&format!("{}; /* {} */\n", f.to_prototype(), f.doc));
}
}
s.push_str(&format!("\n#endif /* {} */\n", guard));
s
}
pub fn generate_all_headers(&self) -> Vec<(LibcHeader, String)> {
LibcHeader::all()
.iter()
.map(|h| (*h, self.generate_header(*h)))
.collect()
}
}
impl Default for LibcRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn header_for_include(filename: &str) -> Option<LibcHeader> {
match filename {
"<stdio.h>" | "stdio.h" => Some(LibcHeader::Stdio),
"<stdlib.h>" | "stdlib.h" => Some(LibcHeader::Stdlib),
"<string.h>" | "string.h" => Some(LibcHeader::String),
"<ctype.h>" | "ctype.h" => Some(LibcHeader::Ctype),
"<math.h>" | "math.h" => Some(LibcHeader::Math),
"<errno.h>" | "errno.h" => Some(LibcHeader::Errno),
"<assert.h>" | "assert.h" => Some(LibcHeader::Assert),
"<time.h>" | "time.h" => Some(LibcHeader::Time),
"<setjmp.h>" | "setjmp.h" => Some(LibcHeader::Setjmp),
"<signal.h>" | "signal.h" => Some(LibcHeader::Signal),
"<locale.h>" | "locale.h" => Some(LibcHeader::Locale),
"<wchar.h>" | "wchar.h" => Some(LibcHeader::Wchar),
"<inttypes.h>" | "inttypes.h" => Some(LibcHeader::Inttypes),
"<stdatomic.h>" | "stdatomic.h" => Some(LibcHeader::Stdatomic),
"<complex.h>" | "complex.h" => Some(LibcHeader::Complex),
_ => None,
}
}
pub fn is_standard_header(filename: &str) -> bool {
header_for_include(filename).is_some()
}
pub fn standard_header_names() -> Vec<&'static str> {
LibcHeader::all().iter().map(|h| h.filename()).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_header_for_include_stdio() {
assert_eq!(header_for_include("<stdio.h>"), Some(LibcHeader::Stdio));
}
#[test]
fn test_header_for_include_stdlib() {
assert_eq!(header_for_include("<stdlib.h>"), Some(LibcHeader::Stdlib));
}
#[test]
fn test_header_for_include_string() {
assert_eq!(header_for_include("<string.h>"), Some(LibcHeader::String));
}
#[test]
fn test_header_for_include_ctype() {
assert_eq!(header_for_include("<ctype.h>"), Some(LibcHeader::Ctype));
}
#[test]
fn test_header_for_include_math() {
assert_eq!(header_for_include("<math.h>"), Some(LibcHeader::Math));
}
#[test]
fn test_header_for_include_errno() {
assert_eq!(header_for_include("<errno.h>"), Some(LibcHeader::Errno));
}
#[test]
fn test_header_for_include_assert() {
assert_eq!(header_for_include("<assert.h>"), Some(LibcHeader::Assert));
}
#[test]
fn test_header_for_include_time() {
assert_eq!(header_for_include("<time.h>"), Some(LibcHeader::Time));
}
#[test]
fn test_header_for_include_unknown() {
assert_eq!(header_for_include("<unistd.h>"), None);
}
#[test]
fn test_header_for_include_no_brackets() {
assert_eq!(header_for_include("stdio.h"), Some(LibcHeader::Stdio));
}
#[test]
fn test_header_filename() {
assert_eq!(LibcHeader::Stdio.filename(), "<stdio.h>");
assert_eq!(LibcHeader::Math.filename(), "<math.h>");
}
#[test]
fn test_header_short_name() {
assert_eq!(LibcHeader::Stdio.short_name(), "stdio");
assert_eq!(LibcHeader::Stdlib.short_name(), "stdlib");
}
#[test]
fn test_header_all_count() {
assert_eq!(LibcHeader::all().len(), 8);
}
#[test]
fn test_is_standard_header() {
assert!(is_standard_header("<stdio.h>"));
assert!(is_standard_header("stdlib.h"));
assert!(!is_standard_header("<unistd.h>"));
}
#[test]
fn test_standard_header_names() {
let names = standard_header_names();
assert!(names.contains(&"<stdio.h>"));
assert!(names.contains(&"<stdlib.h>"));
}
#[test]
fn test_clibc_type_as_str() {
assert_eq!(CLibcType::Void.as_str(), "void");
assert_eq!(CLibcType::Int.as_str(), "int");
assert_eq!(CLibcType::CharPtr.as_str(), "char *");
assert_eq!(CLibcType::SizeT.as_str(), "size_t");
}
#[test]
fn test_clibc_type_is_pointer() {
assert!(CLibcType::CharPtr.is_pointer());
assert!(CLibcType::FilePtr.is_pointer());
assert!(!CLibcType::Int.is_pointer());
}
#[test]
fn test_clibc_type_is_integer() {
assert!(CLibcType::Int.is_integer());
assert!(CLibcType::SizeT.is_integer());
assert!(!CLibcType::Float.is_integer());
}
#[test]
fn test_clibc_type_display() {
assert_eq!(format!("{}", CLibcType::Double), "double");
}
#[test]
fn test_function_decl_prototype_simple() {
let f = LibcFunctionDecl::new(
"getchar",
LibcHeader::Stdio,
CLibcType::Int,
vec![],
false,
LibcBuiltinCategory::Io,
"Reads a character from stdin.",
);
assert_eq!(f.to_prototype(), "int getchar(void)");
}
#[test]
fn test_function_decl_prototype_vararg() {
let f = LibcFunctionDecl::new(
"printf",
LibcHeader::Stdio,
CLibcType::Int,
vec![CLibcType::ConstCharPtr],
true,
LibcBuiltinCategory::Io,
"Formatted output.",
);
assert_eq!(f.to_prototype(), "int printf(const char *, ...)");
}
#[test]
fn test_function_decl_prototype_multi_param() {
let f = LibcFunctionDecl::new(
"fwrite",
LibcHeader::Stdio,
CLibcType::SizeT,
vec![
CLibcType::ConstVoidPtr,
CLibcType::SizeT,
CLibcType::SizeT,
CLibcType::FilePtr,
],
false,
LibcBuiltinCategory::Io,
"Binary write.",
);
let proto = f.to_prototype();
assert!(proto.starts_with("size_t fwrite("));
}
#[test]
fn test_function_decl_is_zero_arity() {
let f = LibcFunctionDecl::new(
"getchar",
LibcHeader::Stdio,
CLibcType::Int,
vec![],
false,
LibcBuiltinCategory::Io,
"",
);
assert!(f.is_zero_arity());
}
#[test]
fn test_function_decl_fixed_param_count() {
let f = LibcFunctionDecl::new(
"fopen",
LibcHeader::Stdio,
CLibcType::FilePtr,
vec![CLibcType::ConstCharPtr, CLibcType::ConstCharPtr],
false,
LibcBuiltinCategory::Io,
"",
);
assert_eq!(f.fixed_param_count(), 2);
}
#[test]
fn test_function_decl_noreturn() {
let f = LibcFunctionDecl::new(
"exit",
LibcHeader::Stdlib,
CLibcType::Void,
vec![CLibcType::Int],
false,
LibcBuiltinCategory::Process,
"",
)
.with_noreturn();
assert!(f.is_noreturn);
}
#[test]
fn test_function_decl_introduced() {
let f = LibcFunctionDecl::new(
"snprintf",
LibcHeader::Stdio,
CLibcType::Int,
vec![
CLibcType::CharPtr,
CLibcType::SizeT,
CLibcType::ConstCharPtr,
],
true,
LibcBuiltinCategory::Io,
"",
)
.with_introduced("C99");
assert_eq!(f.introduced, "C99");
}
#[test]
fn test_macro_simple() {
let m = LibcMacroDef::simple("EOF", LibcHeader::Stdio, "(-1)", "End of file.");
assert_eq!(m.name, "EOF");
assert_eq!(m.replacement, Some("(-1)".to_string()));
assert!(!m.is_function_like);
}
#[test]
fn test_macro_function_like() {
let m = LibcMacroDef::function_like("assert", LibcHeader::Assert, "Assertion macro.");
assert!(m.is_function_like);
assert!(m.replacement.is_none());
}
#[test]
fn test_typedef_new() {
let t = LibcTypeDef::new(
"size_t",
LibcHeader::Stdio,
CLibcType::SizeT,
"Object size.",
);
assert_eq!(t.name, "size_t");
assert_eq!(t.header, LibcHeader::Stdio);
}
#[test]
fn test_global_new() {
let g = LibcGlobalDecl::new(
"stdin",
LibcHeader::Stdio,
CLibcType::FilePtr,
"Standard input.",
);
assert_eq!(g.name, "stdin");
assert_eq!(g.ty, CLibcType::FilePtr);
}
#[test]
fn test_registry_creation() {
let reg = LibcRegistry::new();
assert!(reg.function_count() > 0);
assert!(reg.macro_count() > 0);
assert!(reg.type_count() > 0);
assert!(reg.global_count() > 0);
}
#[test]
fn test_registry_lookup_function() {
let reg = LibcRegistry::new();
let f = reg.lookup_function("printf");
assert!(f.is_some());
assert_eq!(f.unwrap().header, LibcHeader::Stdio);
}
#[test]
fn test_registry_lookup_function_stdlib() {
let reg = LibcRegistry::new();
assert!(reg.lookup_function("malloc").is_some());
assert!(reg.lookup_function("free").is_some());
assert!(reg.lookup_function("exit").is_some());
}
#[test]
fn test_registry_lookup_function_string() {
let reg = LibcRegistry::new();
assert!(reg.lookup_function("strlen").is_some());
assert!(reg.lookup_function("memcpy").is_some());
assert!(reg.lookup_function("strcmp").is_some());
}
#[test]
fn test_registry_lookup_function_math() {
let reg = LibcRegistry::new();
assert!(reg.lookup_function("sin").is_some());
assert!(reg.lookup_function("sqrt").is_some());
assert!(reg.lookup_function("pow").is_some());
}
#[test]
fn test_registry_lookup_function_ctype() {
let reg = LibcRegistry::new();
assert!(reg.lookup_function("isalpha").is_some());
assert!(reg.lookup_function("toupper").is_some());
}
#[test]
fn test_registry_lookup_function_time() {
let reg = LibcRegistry::new();
assert!(reg.lookup_function("time").is_some());
assert!(reg.lookup_function("clock").is_some());
}
#[test]
fn test_registry_lookup_function_unknown() {
let reg = LibcRegistry::new();
assert!(reg.lookup_function("nonexistent").is_none());
}
#[test]
fn test_registry_lookup_macro() {
let reg = LibcRegistry::new();
assert!(reg.lookup_macro("EOF").is_some());
assert!(reg.lookup_macro("NULL").is_none()); assert!(reg.lookup_macro("EXIT_SUCCESS").is_some());
assert!(reg.lookup_macro("RAND_MAX").is_some());
}
#[test]
fn test_registry_lookup_type() {
let reg = LibcRegistry::new();
assert!(reg.lookup_type("size_t").is_some());
assert!(reg.lookup_type("FILE").is_some());
}
#[test]
fn test_registry_lookup_global() {
let reg = LibcRegistry::new();
assert!(reg.lookup_global("stdin").is_some());
assert!(reg.lookup_global("stdout").is_some());
assert!(reg.lookup_global("stderr").is_some());
assert!(reg.lookup_global("errno").is_some());
}
#[test]
fn test_registry_functions_in_header() {
let reg = LibcRegistry::new();
let stdio_funcs = reg.functions_in_header(LibcHeader::Stdio);
assert!(stdio_funcs.iter().any(|f| f.name == "printf"));
assert!(stdio_funcs.iter().any(|f| f.name == "fopen"));
}
#[test]
fn test_registry_function_counts_by_header() {
let reg = LibcRegistry::new();
let counts = reg.function_counts_by_header();
for (header, count) in &counts {
if *header != LibcHeader::Assert && *header != LibcHeader::Errno {
assert!(
*count > 0,
"Header {:?} should have at least one function",
header
);
}
}
}
#[test]
fn test_registry_count_assertions() {
let reg = LibcRegistry::new();
let stdio_funcs = reg.functions_in_header(LibcHeader::Stdio);
assert!(
stdio_funcs.len() >= 35,
"Expected >= 35 stdio functions, got {}",
stdio_funcs.len()
);
let stdlib_funcs = reg.functions_in_header(LibcHeader::Stdlib);
assert!(
stdlib_funcs.len() >= 25,
"Expected >= 25 stdlib functions, got {}",
stdlib_funcs.len()
);
let string_funcs = reg.functions_in_header(LibcHeader::String);
assert!(
string_funcs.len() >= 20,
"Expected >= 20 string functions, got {}",
string_funcs.len()
);
let ctype_funcs = reg.functions_in_header(LibcHeader::Ctype);
assert_eq!(ctype_funcs.len(), 14);
let time_funcs = reg.functions_in_header(LibcHeader::Time);
assert!(
time_funcs.len() >= 8,
"Expected >= 8 time functions, got {}",
time_funcs.len()
);
}
#[test]
fn test_registry_total_function_count() {
let reg = LibcRegistry::new();
assert!(
reg.function_count() >= 130,
"Expected >= 130 total functions, got {}",
reg.function_count()
);
}
#[test]
fn test_generate_header_stdio() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::Stdio);
assert!(content.contains("#ifndef _LIBC_BUILTIN_STDIO_H_"));
assert!(content.contains("printf"));
assert!(content.contains("fopen"));
assert!(content.contains("EOF"));
assert!(content.contains("stdin"));
}
#[test]
fn test_generate_header_stdlib() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::Stdlib);
assert!(content.contains("malloc"));
assert!(content.contains("free"));
assert!(content.contains("EXIT_SUCCESS"));
}
#[test]
fn test_generate_header_string() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::String);
assert!(content.contains("strlen"));
assert!(content.contains("memcpy"));
assert!(content.contains("strcmp"));
}
#[test]
fn test_generate_header_math() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::Math);
assert!(content.contains("sin"));
assert!(content.contains("sqrt"));
assert!(content.contains("M_PI"));
}
#[test]
fn test_generate_header_ctype() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::Ctype);
assert!(content.contains("isalpha"));
assert!(content.contains("toupper"));
}
#[test]
fn test_generate_header_errno() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::Errno);
assert!(content.contains("EDOM"));
assert!(content.contains("errno"));
}
#[test]
fn test_generate_header_assert() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::Assert);
assert!(content.contains("assert"));
}
#[test]
fn test_generate_header_time() {
let reg = LibcRegistry::new();
let content = reg.generate_header(LibcHeader::Time);
assert!(content.contains("tm_sec"));
assert!(content.contains("time_t"));
assert!(content.contains("CLOCKS_PER_SEC"));
}
#[test]
fn test_generate_all_headers() {
let reg = LibcRegistry::new();
let headers = reg.generate_all_headers();
assert_eq!(headers.len(), 8);
for (header, content) in &headers {
assert!(
!content.is_empty(),
"Header {:?} should not be empty",
header
);
}
}
#[test]
fn test_builtin_category_is_pure() {
assert!(LibcBuiltinCategory::Math.is_pure());
assert!(LibcBuiltinCategory::Ctype.is_pure());
assert!(!LibcBuiltinCategory::Io.is_pure());
assert!(!LibcBuiltinCategory::Memory.is_pure());
}
#[test]
fn test_builtin_category_may_throw() {
assert!(LibcBuiltinCategory::Io.may_throw());
assert!(LibcBuiltinCategory::Memory.may_throw());
assert!(!LibcBuiltinCategory::String.may_throw());
}
#[test]
fn test_registry_default() {
let reg = LibcRegistry::default();
assert!(reg.function_count() > 0);
}
#[test]
fn test_stdio_has_required_functions() {
let reg = LibcRegistry::new();
let required = [
"printf", "fprintf", "sprintf", "snprintf", "scanf", "fscanf", "sscanf", "fopen",
"fclose", "fread", "fwrite", "fseek", "ftell", "rewind", "fgetc", "fputc", "fgets",
"fputs", "getc", "putc", "getchar", "putchar", "ungetc", "perror", "remove", "rename",
"tmpfile", "tmpnam", "setbuf", "setvbuf", "fflush", "feof", "ferror", "clearerr",
];
for name in &required {
assert!(
reg.lookup_function(name).is_some(),
"Missing required stdio function: {}",
name
);
}
}
#[test]
fn test_stdio_has_file_and_streams() {
let reg = LibcRegistry::new();
assert!(reg.lookup_type("FILE").is_some());
assert!(reg.lookup_global("stdin").is_some());
assert!(reg.lookup_global("stdout").is_some());
assert!(reg.lookup_global("stderr").is_some());
assert!(reg.lookup_macro("EOF").is_some());
}
#[test]
fn test_stdlib_has_required_functions() {
let reg = LibcRegistry::new();
let required = [
"malloc", "calloc", "realloc", "free", "atoi", "atol", "atoll", "strtol", "strtoul",
"strtoll", "strtoull", "strtof", "strtod", "strtold", "rand", "srand", "abort", "exit",
"atexit", "_Exit", "getenv", "system", "bsearch", "qsort", "abs", "labs", "llabs",
"div", "ldiv", "lldiv",
];
for name in &required {
assert!(
reg.lookup_function(name).is_some(),
"Missing required stdlib function: {}",
name
);
}
}
#[test]
fn test_string_has_required_functions() {
let reg = LibcRegistry::new();
let required = [
"memcpy", "memmove", "memcmp", "memchr", "memset", "strcpy", "strncpy", "strcat",
"strncat", "strcmp", "strncmp", "strcoll", "strchr", "strrchr", "strspn", "strcspn",
"strpbrk", "strstr", "strlen", "strerror", "strtok", "strxfrm",
];
for name in &required {
assert!(
reg.lookup_function(name).is_some(),
"Missing required string function: {}",
name
);
}
}
#[test]
fn test_ctype_has_all_classifiers() {
let reg = LibcRegistry::new();
let classifiers = [
"isalnum", "isalpha", "isblank", "iscntrl", "isdigit", "isgraph", "islower", "isprint",
"ispunct", "isspace", "isupper", "isxdigit", "tolower", "toupper",
];
for name in &classifiers {
assert!(
reg.lookup_function(name).is_some(),
"Missing ctype function: {}",
name
);
}
}
#[test]
fn test_math_has_required_functions() {
let reg = LibcRegistry::new();
let required = [
"sin", "cos", "tan", "asin", "acos", "atan", "atan2", "sinh", "cosh", "tanh", "exp",
"log", "log10", "pow", "sqrt", "ceil", "floor", "fabs", "fmod", "frexp", "ldexp",
"modf",
];
for name in &required {
assert!(
reg.lookup_function(name).is_some(),
"Missing required math function: {}",
name
);
}
}
#[test]
fn test_math_has_macros() {
let reg = LibcRegistry::new();
assert!(reg.lookup_macro("M_PI").is_some());
assert!(reg.lookup_macro("HUGE_VAL").is_some());
assert!(reg.lookup_macro("INFINITY").is_some());
assert!(reg.lookup_macro("NAN").is_some());
}
#[test]
fn test_errno_has_macros() {
let reg = LibcRegistry::new();
assert!(reg.lookup_macro("EDOM").is_some());
assert!(reg.lookup_macro("ERANGE").is_some());
assert!(reg.lookup_macro("EILSEQ").is_some());
assert!(reg.lookup_global("errno").is_some());
}
#[test]
fn test_time_has_required_functions() {
let reg = LibcRegistry::new();
let required = [
"clock",
"time",
"difftime",
"mktime",
"asctime",
"ctime",
"gmtime",
"localtime",
"strftime",
];
for name in &required {
assert!(
reg.lookup_function(name).is_some(),
"Missing required time function: {}",
name
);
}
}
}