#![allow(clippy::missing_safety_doc)]
#![allow(non_camel_case_types)]
use std::ffi::{CStr, CString, c_char};
use std::path::PathBuf;
use std::ptr;
macro_rules! cstr {
($s:literal) => {{
const BYTES: &[u8] = concat!($s, "\0").as_bytes();
unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(BYTES).as_ptr() }
}};
}
macro_rules! assert_not_null {
($ptr:expr) => {{
let ptr = $ptr;
if ptr.is_null() {
return KconfqResult::KCONFQ_RESULT_NULL_PARAMETER;
}
}};
}
macro_rules! assert_not_null_and_set {
($ptr:expr, $val:expr) => {{
let ptr = $ptr;
let val = $val;
assert_not_null!(ptr);
unsafe {
*ptr = val;
}
}};
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum KconfqResult {
KCONFQ_RESULT_SUCCESS = 0,
KCONFQ_RESULT_NOT_FOUND = 1,
KCONFQ_RESULT_KERNEL_VERSION_ERROR = 2,
KCONFQ_RESULT_NULL_PARAMETER = 3,
KCONFQ_RESULT_MALFORMED_ARGUMENT = 4,
KCONFQ_RESULT_NON_UTF8_STRING = 5,
KCONFQ_RESULT_FAILED_TO_GET_READER = 6,
KCONFQ_RESULT_MISSING_ENTRY = 7,
KCONFQ_RESULT_IO_ERROR = 8,
KCONFQ_RESULT_FAILED_TO_PARSE_ENTRY_VALUE = 9,
KCONFQ_RESULT_UNKNOWN_ERROR = 255,
}
#[unsafe(no_mangle)]
#[rustfmt::skip]
pub unsafe extern "C" fn kconfq_result_strerror(result: KconfqResult) -> *const c_char {
match result {
KconfqResult::KCONFQ_RESULT_SUCCESS => cstr!("success"),
KconfqResult::KCONFQ_RESULT_NOT_FOUND => cstr!("not found"),
KconfqResult::KCONFQ_RESULT_KERNEL_VERSION_ERROR => cstr!("error getting Linux kernel version"),
KconfqResult::KCONFQ_RESULT_NULL_PARAMETER => cstr!("parameter is a NULL pointer"),
KconfqResult::KCONFQ_RESULT_MALFORMED_ARGUMENT => cstr!("value of the function's argument is malformed"),
KconfqResult::KCONFQ_RESULT_NON_UTF8_STRING => cstr!("failed while trying to parse non-UTF-8 string"),
KconfqResult::KCONFQ_RESULT_UNKNOWN_ERROR => cstr!("unknown internal error"),
KconfqResult::KCONFQ_RESULT_FAILED_TO_GET_READER => cstr!("failed to get reader to the config's file"),
KconfqResult::KCONFQ_RESULT_MISSING_ENTRY => cstr!("entry is missing from the config"),
KconfqResult::KCONFQ_RESULT_IO_ERROR => cstr!("I/O error"),
KconfqResult::KCONFQ_RESULT_FAILED_TO_PARSE_ENTRY_VALUE => cstr!("failed to parse the value of the config's entry"),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn kconfq_free_string(ptr: *const c_char) {
if !ptr.is_null() {
unsafe {
drop(CString::from_raw(ptr as *mut c_char));
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn kconfq_free_config(ptr: *const crate::Config) {
if !ptr.is_null() {
unsafe {
drop(Box::from_raw(ptr as *mut crate::Config));
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn kconfq_config_path(
config: *const crate::Config,
out_path: *mut *const c_char,
) -> KconfqResult {
assert_not_null!(config);
assert_not_null_and_set!(out_path, ptr::null_mut());
let config = unsafe { &*config };
match CString::new(config.path().to_string_lossy().as_bytes()) {
Ok(c_string) => unsafe {
*out_path = c_string.into_raw();
KconfqResult::KCONFQ_RESULT_SUCCESS
},
Err(_) => KconfqResult::KCONFQ_RESULT_NON_UTF8_STRING,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn kconfq_config_is_gzip(
config: *const crate::Config,
out_bool: *mut bool,
) -> KconfqResult {
assert_not_null!(config);
assert_not_null_and_set!(out_bool, false);
let config = unsafe { &*config };
match config.is_gzip() {
Ok(b) => unsafe {
*out_bool = b;
KconfqResult::KCONFQ_RESULT_SUCCESS
},
Err(_) => KconfqResult::KCONFQ_RESULT_IO_ERROR,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn kconfq_locate_config(
default_path: *const c_char,
out_config: *mut *const crate::Config,
) -> KconfqResult {
assert_not_null_and_set!(out_config, ptr::null_mut());
let path = if default_path.is_null() {
None
} else {
let default_path = unsafe { CStr::from_ptr(default_path) };
let default_path = match default_path.to_str() {
Ok(str) => str,
Err(_) => return KconfqResult::KCONFQ_RESULT_MALFORMED_ARGUMENT,
};
let default_path = PathBuf::from(default_path);
Some(default_path)
};
match crate::locate_config(path) {
Ok(Some(config)) => unsafe {
*out_config = Box::into_raw(Box::new(config));
KconfqResult::KCONFQ_RESULT_SUCCESS
},
Ok(None) => KconfqResult::KCONFQ_RESULT_NOT_FOUND,
Err(crate::error::LocateConfigError::FailedToGetLinuxKernelVersion(_)) => {
KconfqResult::KCONFQ_RESULT_KERNEL_VERSION_ERROR
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn kconfq_find_line(
config: *const crate::Config,
entry_name: *const c_char,
out_line: *mut *const c_char,
) -> KconfqResult {
assert_not_null!(config);
assert_not_null!(entry_name);
assert_not_null_and_set!(out_line, ptr::null_mut());
let entry_name = unsafe { CStr::from_ptr(entry_name) };
let entry_name = match entry_name.to_str() {
Ok(str) => str,
Err(_) => return KconfqResult::KCONFQ_RESULT_MALFORMED_ARGUMENT,
};
let config = unsafe { &*config };
let config_reader = match config.reader() {
Ok(reader) => reader,
Err(_) => return KconfqResult::KCONFQ_RESULT_FAILED_TO_GET_READER,
};
match crate::find_line(entry_name, config_reader) {
Ok(line) => match CString::new(line.as_bytes()) {
Ok(c_string) => unsafe {
*out_line = c_string.into_raw();
KconfqResult::KCONFQ_RESULT_SUCCESS
},
Err(_) => KconfqResult::KCONFQ_RESULT_NON_UTF8_STRING,
},
Err(err) => match err {
crate::error::FindLineError::EntryIsMissing(_) => {
KconfqResult::KCONFQ_RESULT_MISSING_ENTRY
}
crate::error::FindLineError::MalformedEntryName(_) => {
KconfqResult::KCONFQ_RESULT_MALFORMED_ARGUMENT
}
crate::error::FindLineError::FailedToReadConfigLine(_) => {
KconfqResult::KCONFQ_RESULT_IO_ERROR
}
},
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn kconfq_find_value(
config: *const crate::Config,
entry_name: *const c_char,
out_value: *mut *const c_char,
) -> KconfqResult {
assert_not_null!(config);
assert_not_null!(entry_name);
assert_not_null_and_set!(out_value, ptr::null_mut());
let entry_name = unsafe { CStr::from_ptr(entry_name) };
let entry_name = match entry_name.to_str() {
Ok(str) => str,
Err(_) => return KconfqResult::KCONFQ_RESULT_MALFORMED_ARGUMENT,
};
let config = unsafe { &*config };
let config_reader = match config.reader() {
Ok(reader) => reader,
Err(_) => return KconfqResult::KCONFQ_RESULT_FAILED_TO_GET_READER,
};
match crate::find_value(entry_name, config_reader) {
Ok(line) => match CString::new(line.as_bytes()) {
Ok(c_string) => unsafe {
*out_value = c_string.into_raw();
KconfqResult::KCONFQ_RESULT_SUCCESS
},
Err(_) => KconfqResult::KCONFQ_RESULT_NON_UTF8_STRING,
},
Err(err) => match err {
crate::error::FindValueError::FailedToFindLine(err) => match err {
crate::error::FindLineError::EntryIsMissing(_) => {
KconfqResult::KCONFQ_RESULT_MISSING_ENTRY
}
crate::error::FindLineError::MalformedEntryName(_) => {
KconfqResult::KCONFQ_RESULT_MALFORMED_ARGUMENT
}
crate::error::FindLineError::FailedToReadConfigLine(_) => {
KconfqResult::KCONFQ_RESULT_IO_ERROR
}
},
crate::error::FindValueError::FailedToParseLine(_) => {
KconfqResult::KCONFQ_RESULT_FAILED_TO_PARSE_ENTRY_VALUE
}
},
}
}