pub(crate) enum LibcFile {}
#[cfg(not(target_arch = "wasm32"))]
#[allow(unsafe_code)]
#[cfg_attr(target_env = "msvc", link(name = "ucrt"))]
#[cfg_attr(target_env = "msvc", link(name = "legacy_stdio_definitions"))]
unsafe extern "C" {
pub(crate) fn fopen(filename: *const u8, mode: *const u8) -> *mut LibcFile;
pub(crate) fn fclose(file: *mut LibcFile) -> i32;
pub(crate) fn fflush(file: *mut LibcFile) -> i32;
pub(crate) fn fread(ptr: *mut u8, size: usize, nmemb: usize, stream: *mut LibcFile) -> usize;
pub(crate) fn fwrite(ptr: *const u8, size: usize, nmemb: usize, stream: *mut LibcFile)
-> usize;
pub(crate) fn fgets(s: *mut u8, n: i32, stream: *mut LibcFile) -> *mut u8;
pub(crate) fn fseek(stream: *mut LibcFile, offset: i64, whence: i32) -> i32;
pub(crate) fn ftell(stream: *mut LibcFile) -> i64;
pub(crate) fn ferror(stream: *mut LibcFile) -> i32;
pub(crate) fn clearerr(stream: *mut LibcFile);
#[allow(dead_code)]
pub(crate) fn feof(stream: *mut LibcFile) -> i32;
pub(crate) fn getc(stream: *mut LibcFile) -> i32;
pub(crate) fn ungetc(c: i32, stream: *mut LibcFile) -> i32;
pub(crate) fn setvbuf(stream: *mut LibcFile, buf: *mut u8, mode: i32, size: usize) -> i32;
pub(crate) fn tmpfile() -> *mut LibcFile;
pub(crate) fn fscanf(stream: *mut LibcFile, format: *const u8, ...) -> i32;
pub(crate) fn fprintf(stream: *mut LibcFile, format: *const u8, ...) -> i32;
pub(crate) fn strlen(s: *const u8) -> usize;
pub(crate) fn strtod(nptr: *const u8, endptr: *mut *mut u8) -> f64;
pub(crate) fn localeconv() -> *const LConv;
pub(crate) fn strcoll(s1: *const u8, s2: *const u8) -> i32;
pub(crate) fn clock() -> ClockT;
pub(crate) fn mktime(tm: *mut Tm) -> TimeT;
pub(crate) fn strftime(s: *mut u8, max: usize, format: *const u8, tm: *const Tm) -> usize;
pub(crate) fn setlocale(category: i32, locale: *const u8) -> *const u8;
pub(crate) fn isalpha(c: i32) -> i32;
pub(crate) fn iscntrl(c: i32) -> i32;
pub(crate) fn isdigit(c: i32) -> i32;
pub(crate) fn islower(c: i32) -> i32;
pub(crate) fn ispunct(c: i32) -> i32;
pub(crate) fn isspace(c: i32) -> i32;
pub(crate) fn isupper(c: i32) -> i32;
pub(crate) fn isalnum(c: i32) -> i32;
pub(crate) fn isxdigit(c: i32) -> i32;
pub(crate) fn tolower(c: i32) -> i32;
pub(crate) fn toupper(c: i32) -> i32;
}
#[cfg(target_arch = "wasm32")]
#[allow(unsafe_code)]
pub(crate) mod wasm_stubs {
use super::*;
pub(crate) unsafe fn fopen(_filename: *const u8, _mode: *const u8) -> *mut LibcFile {
std::ptr::null_mut()
}
pub(crate) unsafe fn fclose(_file: *mut LibcFile) -> i32 {
-1
}
pub(crate) unsafe fn fflush(_file: *mut LibcFile) -> i32 {
-1
}
pub(crate) unsafe fn fread(
_ptr: *mut u8,
_size: usize,
_nmemb: usize,
_stream: *mut LibcFile,
) -> usize {
0
}
pub(crate) unsafe fn fwrite(
_ptr: *const u8,
_size: usize,
_nmemb: usize,
_stream: *mut LibcFile,
) -> usize {
0
}
pub(crate) unsafe fn fgets(_s: *mut u8, _n: i32, _stream: *mut LibcFile) -> *mut u8 {
std::ptr::null_mut()
}
pub(crate) unsafe fn fseek(_stream: *mut LibcFile, _offset: i64, _whence: i32) -> i32 {
-1
}
pub(crate) unsafe fn ftell(_stream: *mut LibcFile) -> i64 {
-1
}
pub(crate) unsafe fn ferror(_stream: *mut LibcFile) -> i32 {
1
}
pub(crate) unsafe fn clearerr(_stream: *mut LibcFile) {}
#[allow(dead_code)]
pub(crate) unsafe fn feof(_stream: *mut LibcFile) -> i32 {
1
}
pub(crate) unsafe fn getc(_stream: *mut LibcFile) -> i32 {
-1 }
pub(crate) unsafe fn ungetc(_c: i32, _stream: *mut LibcFile) -> i32 {
-1 }
pub(crate) unsafe fn setvbuf(
_stream: *mut LibcFile,
_buf: *mut u8,
_mode: i32,
_size: usize,
) -> i32 {
-1
}
pub(crate) unsafe fn tmpfile() -> *mut LibcFile {
std::ptr::null_mut()
}
pub(crate) unsafe fn strlen(s: *const u8) -> usize {
if s.is_null() {
return 0;
}
let mut len = 0;
unsafe {
while *s.add(len) != 0 {
len += 1;
}
}
len
}
pub(crate) unsafe fn strtod(nptr: *const u8, endptr: *mut *mut u8) -> f64 {
unsafe {
if nptr.is_null() {
if !endptr.is_null() {
*endptr = nptr.cast_mut();
}
return 0.0;
}
let mut len = 0;
while *nptr.add(len) != 0 {
len += 1;
}
let bytes = std::slice::from_raw_parts(nptr, len);
let start = bytes
.iter()
.position(|b| !b.is_ascii_whitespace())
.unwrap_or(len);
if start >= len {
if !endptr.is_null() {
*endptr = nptr.cast_mut();
}
return 0.0;
}
let rest = &bytes[start..];
let Ok(s) = std::str::from_utf8(rest) else {
if !endptr.is_null() {
*endptr = nptr.cast_mut();
}
return 0.0;
};
let mut best_len = 0;
let mut best_val = 0.0;
for end in (1..=s.len()).rev() {
if let Ok(val) = s[..end].parse::<f64>() {
best_len = end;
best_val = val;
break;
}
}
if !endptr.is_null() {
*endptr = nptr.add(start + best_len).cast_mut();
}
best_val
}
}
static WASM_LCONV: LConv = LConv {
decimal_point: c".".as_ptr().cast(),
};
pub(crate) unsafe fn localeconv() -> *const LConv {
&raw const WASM_LCONV
}
pub(crate) unsafe fn strcoll(s1: *const u8, s2: *const u8) -> i32 {
unsafe {
let len1 = strlen(s1);
let len2 = strlen(s2);
let a = std::slice::from_raw_parts(s1, len1);
let b = std::slice::from_raw_parts(s2, len2);
match a.cmp(b) {
std::cmp::Ordering::Less => -1,
std::cmp::Ordering::Equal => 0,
std::cmp::Ordering::Greater => 1,
}
}
}
pub(crate) unsafe fn clock() -> ClockT {
0
}
pub(crate) unsafe fn mktime(_tm: *mut Tm) -> TimeT {
-1
}
pub(crate) unsafe fn strftime(
_s: *mut u8,
_max: usize,
_format: *const u8,
_tm: *const Tm,
) -> usize {
0
}
pub(crate) unsafe fn setlocale(_category: i32, _locale: *const u8) -> *const u8 {
std::ptr::null()
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn isalpha(c: i32) -> i32 {
i32::from((c as u8).is_ascii_alphabetic())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn iscntrl(c: i32) -> i32 {
i32::from((c as u8).is_ascii_control())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn isdigit(c: i32) -> i32 {
i32::from((c as u8).is_ascii_digit())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn islower(c: i32) -> i32 {
i32::from((c as u8).is_ascii_lowercase())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn ispunct(c: i32) -> i32 {
i32::from((c as u8).is_ascii_punctuation())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn isspace(c: i32) -> i32 {
i32::from((c as u8).is_ascii_whitespace())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn isupper(c: i32) -> i32 {
i32::from((c as u8).is_ascii_uppercase())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn isalnum(c: i32) -> i32 {
i32::from((c as u8).is_ascii_alphanumeric())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn isxdigit(c: i32) -> i32 {
i32::from((c as u8).is_ascii_hexdigit())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn tolower(c: i32) -> i32 {
i32::from((c as u8).to_ascii_lowercase())
}
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
pub(crate) unsafe fn toupper(c: i32) -> i32 {
i32::from((c as u8).to_ascii_uppercase())
}
}
#[cfg(target_arch = "wasm32")]
pub(crate) use wasm_stubs::{
clearerr, clock, fclose, ferror, fflush, fgets, fopen, fread, fseek, ftell, fwrite, getc,
isalnum, isalpha, iscntrl, isdigit, islower, ispunct, isspace, isupper, isxdigit, localeconv,
mktime, setlocale, setvbuf, strcoll, strftime, strlen, strtod, tmpfile, tolower, toupper,
ungetc,
};
#[allow(unsafe_code)]
pub(crate) fn c_fprintf_number(stream: *mut LibcFile, d: f64) -> i32 {
#[cfg(target_arch = "wasm32")]
{
let _ = (stream, d);
-1
}
#[cfg(not(target_arch = "wasm32"))]
{
let fmt = b"%.14g\0";
unsafe { fprintf(stream, fmt.as_ptr(), d) }
}
}
#[allow(unsafe_code)]
pub(crate) fn c_fscanf_number(stream: *mut LibcFile) -> Option<f64> {
#[cfg(target_arch = "wasm32")]
{
let _ = stream;
None
}
#[cfg(not(target_arch = "wasm32"))]
{
let mut d: f64 = 0.0;
let fmt = b"%lf\0";
let result = unsafe { fscanf(stream, fmt.as_ptr(), &raw mut d) };
if result == 1 { Some(d) } else { None }
}
}
#[repr(C)]
pub(crate) struct LConv {
pub(crate) decimal_point: *const u8,
}
#[cfg(target_arch = "wasm32")]
#[allow(unsafe_code)]
unsafe impl Sync for LConv {}
pub(crate) type TimeT = i64;
#[cfg(not(target_os = "windows"))]
pub(crate) type ClockT = i64;
#[cfg(target_os = "windows")]
pub(crate) type ClockT = i32;
#[cfg(not(target_os = "windows"))]
pub(crate) const CLOCKS_PER_SEC: f64 = 1_000_000.0;
#[cfg(target_os = "windows")]
pub(crate) const CLOCKS_PER_SEC: f64 = 1_000.0;
#[repr(C)]
#[allow(clippy::struct_field_names)]
pub(crate) struct Tm {
pub(crate) tm_sec: i32,
pub(crate) tm_min: i32,
pub(crate) tm_hour: i32,
pub(crate) tm_mday: i32,
pub(crate) tm_mon: i32,
pub(crate) tm_year: i32,
pub(crate) tm_wday: i32,
pub(crate) tm_yday: i32,
pub(crate) tm_isdst: i32,
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
tm_gmtoff: i64,
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
tm_zone: *const i8,
}
#[allow(clippy::derivable_impls)]
impl Default for Tm {
fn default() -> Self {
Self {
tm_sec: 0,
tm_min: 0,
tm_hour: 0,
tm_mday: 0,
tm_mon: 0,
tm_year: 0,
tm_wday: 0,
tm_yday: 0,
tm_isdst: 0,
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
tm_gmtoff: 0,
#[cfg(not(any(target_os = "windows", target_arch = "wasm32")))]
tm_zone: std::ptr::null(),
}
}
}
pub(crate) fn current_time() -> TimeT {
#[cfg(not(target_arch = "wasm32"))]
{
use std::time::{SystemTime, UNIX_EPOCH};
#[allow(clippy::cast_possible_wrap)]
match SystemTime::now().duration_since(UNIX_EPOCH) {
Ok(d) => d.as_secs() as TimeT,
Err(_) => 0,
}
}
#[cfg(target_arch = "wasm32")]
{
0
}
}
#[cfg(target_os = "linux")]
#[allow(unsafe_code)]
unsafe extern "C" {
static stdin: *mut LibcFile;
static stdout: *mut LibcFile;
static stderr: *mut LibcFile;
}
#[cfg(target_os = "macos")]
#[allow(unsafe_code)]
unsafe extern "C" {
#[link_name = "__stdinp"]
static stdin: *mut LibcFile;
#[link_name = "__stdoutp"]
static stdout: *mut LibcFile;
#[link_name = "__stderrp"]
static stderr: *mut LibcFile;
}
#[cfg(target_os = "windows")]
#[allow(unsafe_code)]
#[link(name = "ucrt")]
unsafe extern "C" {
fn __acrt_iob_func(index: u32) -> *mut LibcFile;
}
#[allow(unsafe_code)]
pub(crate) fn c_stdin() -> *mut LibcFile {
#[cfg(target_arch = "wasm32")]
{
std::ptr::null_mut()
}
#[cfg(not(target_arch = "wasm32"))]
unsafe {
#[cfg(not(target_os = "windows"))]
{
stdin
}
#[cfg(target_os = "windows")]
{
__acrt_iob_func(0)
}
}
}
#[allow(unsafe_code)]
pub(crate) fn c_stdout() -> *mut LibcFile {
#[cfg(target_arch = "wasm32")]
{
std::ptr::null_mut()
}
#[cfg(not(target_arch = "wasm32"))]
unsafe {
#[cfg(not(target_os = "windows"))]
{
stdout
}
#[cfg(target_os = "windows")]
{
__acrt_iob_func(1)
}
}
}
#[allow(unsafe_code)]
pub(crate) fn c_stderr() -> *mut LibcFile {
#[cfg(target_arch = "wasm32")]
{
std::ptr::null_mut()
}
#[cfg(not(target_arch = "wasm32"))]
unsafe {
#[cfg(not(target_os = "windows"))]
{
stderr
}
#[cfg(target_os = "windows")]
{
__acrt_iob_func(2)
}
}
}
#[cfg(all(not(target_os = "windows"), not(target_arch = "wasm32")))]
#[allow(unsafe_code)]
unsafe extern "C" {
fn popen(command: *const u8, r#type: *const u8) -> *mut LibcFile;
fn pclose(stream: *mut LibcFile) -> i32;
}
#[cfg(target_os = "windows")]
#[allow(unsafe_code)]
#[link(name = "ucrt")]
unsafe extern "C" {
#[link_name = "_popen"]
fn popen(command: *const u8, r#type: *const u8) -> *mut LibcFile;
#[link_name = "_pclose"]
fn pclose(stream: *mut LibcFile) -> i32;
}
#[allow(unsafe_code)]
pub(crate) fn c_popen(command: *const u8, mode: *const u8) -> *mut LibcFile {
#[cfg(target_arch = "wasm32")]
{
let _ = (command, mode);
std::ptr::null_mut()
}
#[cfg(not(target_arch = "wasm32"))]
unsafe {
popen(command, mode)
}
}
#[allow(unsafe_code)]
pub(crate) fn c_pclose(stream: *mut LibcFile) -> i32 {
#[cfg(target_arch = "wasm32")]
{
let _ = stream;
-1
}
#[cfg(not(target_arch = "wasm32"))]
unsafe {
pclose(stream)
}
}
#[cfg(all(not(target_os = "windows"), not(target_arch = "wasm32")))]
#[allow(unsafe_code)]
unsafe extern "C" {
fn localtime_r(timep: *const TimeT, result: *mut Tm) -> *mut Tm;
fn gmtime_r(timep: *const TimeT, result: *mut Tm) -> *mut Tm;
}
#[cfg(target_os = "windows")]
#[allow(unsafe_code)]
#[link(name = "ucrt")]
unsafe extern "C" {
#[cfg_attr(target_env = "msvc", link_name = "_localtime64_s")]
fn localtime_s(result: *mut Tm, timep: *const TimeT) -> i32;
#[cfg_attr(target_env = "msvc", link_name = "_gmtime64_s")]
fn gmtime_s(result: *mut Tm, timep: *const TimeT) -> i32;
}
#[allow(unsafe_code)]
pub(crate) fn c_localtime(timep: *const TimeT, result: *mut Tm) -> bool {
#[cfg(target_arch = "wasm32")]
{
let _ = (timep, result);
false
}
#[cfg(not(target_arch = "wasm32"))]
unsafe {
#[cfg(not(target_os = "windows"))]
{
!localtime_r(timep, result).is_null()
}
#[cfg(target_os = "windows")]
{
localtime_s(result, timep) == 0
}
}
}
#[allow(unsafe_code)]
pub(crate) fn c_gmtime(timep: *const TimeT, result: *mut Tm) -> bool {
#[cfg(target_arch = "wasm32")]
{
let _ = (timep, result);
false
}
#[cfg(not(target_arch = "wasm32"))]
unsafe {
#[cfg(not(target_os = "windows"))]
{
!gmtime_r(timep, result).is_null()
}
#[cfg(target_os = "windows")]
{
gmtime_s(result, timep) == 0
}
}
}
pub(crate) fn c_tmpname() -> Option<Vec<u8>> {
#[cfg(target_arch = "wasm32")]
{
None
}
#[cfg(not(target_arch = "wasm32"))]
{
use std::fs::File;
use std::sync::atomic::{AtomicU32, Ordering};
static COUNTER: AtomicU32 = AtomicU32::new(0);
let tmp_dir = std::env::temp_dir();
let pid = std::process::id();
for _ in 0..16 {
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
let name = format!("lua_{pid}_{n}");
let path = tmp_dir.join(&name);
if File::create_new(&path).is_ok() {
return path.to_str().map(|s| s.as_bytes().to_vec());
}
}
None
}
}
pub(crate) mod locale {
#[cfg(not(target_os = "windows"))]
pub(crate) const LC_ALL: i32 = 6;
#[cfg(not(target_os = "windows"))]
pub(crate) const LC_COLLATE: i32 = 3;
#[cfg(not(target_os = "windows"))]
pub(crate) const LC_CTYPE: i32 = 0;
#[cfg(not(target_os = "windows"))]
pub(crate) const LC_MONETARY: i32 = 4;
#[cfg(not(target_os = "windows"))]
pub(crate) const LC_NUMERIC: i32 = 1;
#[cfg(not(target_os = "windows"))]
pub(crate) const LC_TIME: i32 = 2;
#[cfg(target_os = "windows")]
pub(crate) const LC_ALL: i32 = 0;
#[cfg(target_os = "windows")]
pub(crate) const LC_COLLATE: i32 = 1;
#[cfg(target_os = "windows")]
pub(crate) const LC_CTYPE: i32 = 2;
#[cfg(target_os = "windows")]
pub(crate) const LC_MONETARY: i32 = 3;
#[cfg(target_os = "windows")]
pub(crate) const LC_NUMERIC: i32 = 4;
#[cfg(target_os = "windows")]
pub(crate) const LC_TIME: i32 = 5;
}
pub(crate) mod bufmode {
#[cfg(not(target_os = "windows"))]
pub(crate) const IONBF: i32 = 2;
#[cfg(not(target_os = "windows"))]
pub(crate) const IOFBF: i32 = 0;
#[cfg(not(target_os = "windows"))]
pub(crate) const IOLBF: i32 = 1;
#[cfg(target_os = "windows")]
pub(crate) const IONBF: i32 = 0x0004;
#[cfg(target_os = "windows")]
pub(crate) const IOFBF: i32 = 0x0000;
#[cfg(target_os = "windows")]
pub(crate) const IOLBF: i32 = 0x0040;
}
#[cfg(feature = "dynmod")]
pub(crate) mod dynlib {
pub(crate) struct DynLib {
handle: *mut (),
path: String,
}
impl DynLib {
pub(crate) fn open(path: &str) -> Result<Self, String> {
let handle = sys::open(path)?;
Ok(Self {
handle,
path: path.to_string(),
})
}
pub(crate) fn symbol(&self, name: &str) -> Result<*mut (), String> {
sys::symbol(self.handle, name, &self.path)
}
pub(crate) fn path(&self) -> &str {
&self.path
}
}
impl Drop for DynLib {
fn drop(&mut self) {
if !self.handle.is_null() {
sys::close(self.handle);
self.handle = std::ptr::null_mut();
}
}
}
#[cfg(unix)]
mod sys {
use std::ffi::CString;
#[allow(unsafe_code)]
unsafe extern "C" {
fn dlopen(filename: *const i8, flags: i32) -> *mut ();
fn dlsym(handle: *mut (), symbol: *const i8) -> *mut ();
fn dlclose(handle: *mut ()) -> i32;
fn dlerror() -> *const i8;
}
const RTLD_NOW: i32 = 2;
fn last_error() -> String {
#[allow(unsafe_code)]
let msg = unsafe { dlerror() };
if msg.is_null() {
"unknown error".to_string()
} else {
#[allow(unsafe_code)]
let cstr = unsafe { std::ffi::CStr::from_ptr(msg) };
cstr.to_string_lossy().into_owned()
}
}
pub(super) fn open(path: &str) -> Result<*mut (), String> {
let cpath = CString::new(path).map_err(|_| format!("invalid path: {path}"))?;
#[allow(unsafe_code)]
let handle = unsafe { dlopen(cpath.as_ptr(), RTLD_NOW) };
if handle.is_null() {
Err(last_error())
} else {
Ok(handle)
}
}
pub(super) fn symbol(
handle: *mut (),
name: &str,
lib_path: &str,
) -> Result<*mut (), String> {
let cname = CString::new(name).map_err(|_| format!("invalid symbol name: {name}"))?;
#[allow(unsafe_code)]
unsafe {
dlerror();
}
#[allow(unsafe_code)]
let ptr = unsafe { dlsym(handle, cname.as_ptr()) };
#[allow(unsafe_code)]
let err = unsafe { dlerror() };
if err.is_null() {
Ok(ptr)
} else {
#[allow(unsafe_code)]
let cstr = unsafe { std::ffi::CStr::from_ptr(err) };
Err(format!(
"symbol '{}' not found in '{}': {}",
name,
lib_path,
cstr.to_string_lossy()
))
}
}
pub(super) fn close(handle: *mut ()) {
#[allow(unsafe_code)]
unsafe {
dlclose(handle);
}
}
}
#[cfg(windows)]
mod sys {
use std::ffi::CString;
#[allow(unsafe_code)]
unsafe extern "stdcall" {
fn LoadLibraryA(filename: *const i8) -> *mut ();
fn GetProcAddress(module: *mut (), name: *const i8) -> *mut ();
fn FreeLibrary(module: *mut ()) -> i32;
fn GetLastError() -> u32;
}
pub(super) fn open(path: &str) -> Result<*mut (), String> {
let cpath = CString::new(path).map_err(|_| format!("invalid path: {path}"))?;
#[allow(unsafe_code)]
let handle = unsafe { LoadLibraryA(cpath.as_ptr()) };
if handle.is_null() {
#[allow(unsafe_code)]
let err = unsafe { GetLastError() };
Err(format!("cannot load '{}': system error {}", path, err))
} else {
Ok(handle)
}
}
pub(super) fn symbol(
handle: *mut (),
name: &str,
lib_path: &str,
) -> Result<*mut (), String> {
let cname = CString::new(name).map_err(|_| format!("invalid symbol name: {name}"))?;
#[allow(unsafe_code)]
let ptr = unsafe { GetProcAddress(handle, cname.as_ptr()) };
if ptr.is_null() {
#[allow(unsafe_code)]
let err = unsafe { GetLastError() };
Err(format!(
"symbol '{}' not found in '{}': system error {}",
name, lib_path, err
))
} else {
Ok(ptr)
}
}
pub(super) fn close(handle: *mut ()) {
#[allow(unsafe_code)]
unsafe {
FreeLibrary(handle);
}
}
}
#[cfg(not(any(unix, windows)))]
mod sys {
pub(super) fn open(path: &str) -> Result<*mut (), String> {
Err("dynamic libraries not supported on this platform".to_string())
}
pub(super) fn symbol(
_handle: *mut (),
_name: &str,
_lib_path: &str,
) -> Result<*mut (), String> {
Err("dynamic libraries not supported on this platform".to_string())
}
pub(super) fn close(_handle: *mut ()) {}
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic, unsafe_code)]
mod tests {
use super::*;
#[test]
fn current_time_reasonable() {
let t = current_time();
assert!(t > 1_704_067_200);
}
#[test]
fn std_streams_not_null() {
assert!(!c_stdin().is_null());
assert!(!c_stdout().is_null());
assert!(!c_stderr().is_null());
}
#[test]
fn tmpname_succeeds() {
let name = c_tmpname().expect("c_tmpname should succeed");
assert!(!name.is_empty());
let name_str = std::str::from_utf8(&name).expect("valid utf8");
let _ = std::fs::remove_file(name_str);
}
#[test]
fn tm_default_zeroed() {
let tm = Tm::default();
assert_eq!(tm.tm_sec, 0);
assert_eq!(tm.tm_min, 0);
assert_eq!(tm.tm_hour, 0);
assert_eq!(tm.tm_mday, 0);
assert_eq!(tm.tm_mon, 0);
assert_eq!(tm.tm_year, 0);
assert_eq!(tm.tm_wday, 0);
assert_eq!(tm.tm_yday, 0);
assert_eq!(tm.tm_isdst, 0);
}
#[test]
fn c_localtime_roundtrip() {
let t: TimeT = 1_000_000_000; let mut tm = Tm::default();
assert!(c_localtime(&raw const t, &raw mut tm));
let t2 = unsafe { mktime(&raw mut tm) };
assert_eq!(t, t2);
}
#[test]
fn c_gmtime_epoch() {
let t: TimeT = 0; let mut tm = Tm::default();
assert!(c_gmtime(&raw const t, &raw mut tm));
assert_eq!(tm.tm_year, 70);
assert_eq!(tm.tm_mon, 0);
assert_eq!(tm.tm_mday, 1);
assert_eq!(tm.tm_hour, 0);
assert_eq!(tm.tm_min, 0);
assert_eq!(tm.tm_sec, 0);
}
#[test]
fn locale_constants_valid() {
let cats = [
locale::LC_ALL,
locale::LC_COLLATE,
locale::LC_CTYPE,
locale::LC_MONETARY,
locale::LC_NUMERIC,
locale::LC_TIME,
];
for &c in &cats {
assert!(c >= 0);
}
}
#[test]
fn bufmode_constants_distinct() {
assert_ne!(bufmode::IONBF, bufmode::IOFBF);
assert_ne!(bufmode::IONBF, bufmode::IOLBF);
assert_ne!(bufmode::IOFBF, bufmode::IOLBF);
}
#[cfg(not(target_os = "windows"))]
#[test]
fn popen_echo() {
let cmd = b"echo hello\0";
let mode = b"r\0";
let fp = c_popen(cmd.as_ptr(), mode.as_ptr());
assert!(!fp.is_null());
let mut buf = [0u8; 64];
let n = unsafe { fread(buf.as_mut_ptr(), 1, buf.len(), fp) };
assert!(n > 0);
assert!(
std::str::from_utf8(&buf[..n])
.expect("valid utf8")
.starts_with("hello")
);
assert_eq!(c_pclose(fp), 0);
}
}