initial commit

This commit is contained in:
2023-03-23 13:54:10 -04:00
commit 76dfb73238
14 changed files with 2112 additions and 0 deletions

165
src/entry/args.rs Normal file
View File

@@ -0,0 +1,165 @@
/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of SPD.
*
* SPD is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SPD is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPD. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and permission
* notice:
*
* Copyright 2023 Douman
*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or
* organization obtaining a copy of the software and accompanying
* documentation covered by this license (the "Software") to use, reproduce,
* display, distribute, execute, and transmit the Software, and to prepare
* derivative works of the Software, and to permit third-parties to whom the
* Software is furnished to do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement,
* including the above license grant, this restriction and the following
* disclaimer, must be included in all copies of the Software, in whole or
* in part, and all derivative works of the Software, unless such copies or
* derivative works are solely in the form of machine-executable object code
* generated by a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
* DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,
* WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
//! C style arguments wrapper
use super::{
c_str_to_rust,
c_str_to_rust_unchecked,
};
#[derive(Copy, Clone, Debug)]
///Wrapper over C-style arguments
pub struct Args {
argc: usize,
argv: *const *const u8,
}
impl Args {
///Creates new instance, but verifies that each string inside are UTF-8.
///
///On error returns pair: `(string index, Utf8Error)`
///
///The function is safe as long as you pass C style main function arguments.
pub unsafe fn new(argc: isize, argv: *const *const u8) -> Result<Self, (usize, core::str::Utf8Error)> {
assert!(argc > 0);
assert!(!argv.is_null());
let this = Args {
argc: argc as usize,
argv,
};
let args = this.as_slice();
for idx in 0..this.argc {
let arg = *args.get_unchecked(idx);
if let Err(error) = c_str_to_rust(arg) {
return Err((idx, error));
}
}
Ok(this)
}
#[inline(always)]
///Unchecked version of `Args::new`
///
///Do it on your own risk
pub unsafe fn new_unchecked(argc: isize, argv: *const *const u8) -> Self {
Args {
argc: argc as usize,
argv,
}
}
#[inline(always)]
///Returns slice of raw C strings
pub fn as_slice(&self) -> &[*const u8] {
unsafe {
core::slice::from_raw_parts(self.argv, self.argc)
}
}
#[inline(always)]
///Retrieves string by index.
///
///No checks, 100% unsafe.
pub unsafe fn get_str_by_index(&self, index: usize) -> &str {
let elem = *self.as_slice().get_unchecked(index);
c_str_to_rust_unchecked(elem)
}
}
impl<'a> IntoIterator for &'a Args {
type Item = &'a str;
type IntoIter = IntoIter<'a>;
#[inline(always)]
fn into_iter(self) -> Self::IntoIter {
Self::IntoIter {
inner: self,
index: 0,
}
}
}
///Iterator over [Args](struct.Args.html)
///
///Comparing to normal iterators can be iterated back and forth.
pub struct IntoIter<'a> {
inner: &'a Args,
index: usize,
}
impl<'a> Iterator for IntoIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.inner.argc {
return None;
}
let elem = unsafe {
self.inner.get_str_by_index(self.index)
};
self.index += 1;
Some(elem)
}
#[inline(always)]
fn size_hint(&self) -> (usize, Option<usize>) {
let count = self.inner.argc - self.index;
(count, Some(count))
}
#[inline(always)]
fn count(self) -> usize {
self.inner.argc - self.index
}
}

109
src/entry/mod.rs Normal file
View File

@@ -0,0 +1,109 @@
/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of SPD.
*
* SPD is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SPD is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPD. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and permission
* notice:
*
* Copyright 2023 Douman
*
* Boost Software License - Version 1.0 - August 17th, 2003
*
* Permission is hereby granted, free of charge, to any person or
* organization obtaining a copy of the software and accompanying
* documentation covered by this license (the "Software") to use, reproduce,
* display, distribute, execute, and transmit the Software, and to prepare
* derivative works of the Software, and to permit third-parties to whom the
* Software is furnished to do so, all subject to the following:
*
* The copyright notices in the Software and this entire statement,
* including the above license grant, this restriction and the following
* disclaimer, must be included in all copies of the Software, in whole or
* in part, and all derivative works of the Software, unless such copies or
* derivative works are solely in the form of machine-executable object code
* generated by a source language processor.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
* DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY,
* WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
//! C main entry point with nice wrapper for arguments.
//!
//!```rust
//!#![no_main]
//!
//!#[no_mangle]
//!pub fn rust_main(args: c_main::Args) -> isize {
//! for arg in args.into_iter().skip(1) {
//! println!("arg={:?}", arg);
//! }
//! 0
//!}
//!```
#![cfg_attr(feature = "cargo-clippy", allow(clippy::style))]
#![cfg_attr(rustfmt, rustfmt_skip)]
mod args;
use args::*;
#[allow(unused)]
#[cold]
#[inline(never)]
unsafe fn invalid_cli_args_error() -> libc::c_int {
libc::printf("Unable to parse C argv as utf-8 string\n\0".as_ptr() as _);
255
}
///Converts C string to Rust's, verifying it is UTF-8
///
///It is UB to pass non-C string as it requires \0
pub unsafe fn c_str_to_rust(ptr: *const u8) -> Result<&'static str, core::str::Utf8Error> {
let len = libc::strlen(ptr as *const i8);
let parts = core::slice::from_raw_parts(ptr, len);
core::str::from_utf8(parts)
}
///Converts C string to Rust's one assuming it is UTF-8
///
///It is UB to pass non-C string as it requires \0
pub unsafe fn c_str_to_rust_unchecked(ptr: *const u8) -> &'static str {
let len = libc::strlen(ptr as *const i8);
let parts = core::slice::from_raw_parts(ptr, len);
core::str::from_utf8_unchecked(parts)
}
extern "Rust" {
fn rust_main(args: Args) -> isize;
}
#[doc(hidden)]
#[cfg(not(test))]
#[no_mangle]
pub unsafe extern fn main(argc: libc::c_int, argv: *const *const u8) -> libc::c_int {
match Args::new(argc as isize, argv) {
Ok(args) => rust_main(args) as _,
Err(_) => invalid_cli_args_error(),
}
}

64
src/lib.rs Normal file
View File

@@ -0,0 +1,64 @@
/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of SPD.
*
* SPD is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SPD is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPD. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and permission
* notice:
*
* MIT License
*
* Copyright (c) 2023 Matt Mastracci and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#![no_std]
pub mod entry;
// pub mod env;
mod macros;
pub mod process;
pub use macros::*;
pub use libc_dbg as dbg;
pub use libc_eprint as eprint;
pub use libc_eprintln as eprintln;
pub use libc_ewriteln as ewriteln;
pub use libc_print as print;
pub use libc_println as println;
pub use libc_writeln as writeln;
pub const SOURCE: &str = "https://git.tebibyte.media/emma/spd.git";

370
src/macros.rs Normal file
View File

@@ -0,0 +1,370 @@
/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of SPD.
*
* SPD is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SPD is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPD. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and permission
* notice:
*
* MIT License
*
* Copyright (c) 2023 Matt Mastracci and contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
//! Implements `println!`, `eprintln!` and `dbg!` on top of the `libc `crate without requiring
//! the use of an allocator.
//!
//! Allows you to use these macros in a #!\[no_std\] context, or in a situation where the
//! traditional Rust streams might not be available (ie: at process shutdown time).
//!
//! [`libc_writeln`] and [`libc_ewriteln`] are provided for cases where you may not wish
//! to pull in the overhead of the formatter code and simply wish to print C-style strings.
//!
//! ## Usage
//!
//! Exactly as you'd use `println!`, `eprintln!` and `dbg!`.
//!
//! ```rust
//! # use libc_print::*;
//! // Use the default `libc_`-prefixed macros:
//! # fn test1()
//! # {
//! libc_println!("Hello {}!", "stdout");
//! libc_eprintln!("Hello {}!", "stderr");
//! let a = 2;
//! let b = libc_dbg!(a * 2) + 1;
//! assert_eq!(b, 5);
//! # }
//! ```
//!
//! Or you can import aliases to `std` names:
//!
//! ```rust
//! use libc_print::std_name::{println, eprintln, dbg};
//!
//! # fn test2()
//! # {
//! println!("Hello {}!", "stdout");
//! eprintln!("Hello {}!", "stderr");
//! let a = 2;
//! let b = dbg!(a * 2) + 1;
//! assert_eq!(b, 5);
//! # }
//! ```
#![no_std]
#![allow(dead_code)]
#![allow(unused)]
#![warn(unsafe_op_in_unsafe_fn)]
use core::{convert::TryFrom, file, line, stringify};
/// This forces a "C" library linkage
#[cfg(not(windows))]
#[link(name = "c")]
mod c {
extern "C" {}
}
// These constants are used by the macros but we don't want to expose
// them to library users.
#[doc(hidden)]
pub const __LIBC_NEWLINE: &str = "\n";
#[doc(hidden)]
pub const __LIBC_STDOUT: i32 = 1;
#[doc(hidden)]
pub const __LIBC_STDERR: i32 = 2;
#[doc(hidden)]
pub struct __LibCWriter(i32);
impl core::fmt::Write for __LibCWriter {
#[inline]
fn write_str(&mut self, s: &str) -> core::fmt::Result {
__libc_println(self.0, s)
}
}
impl __LibCWriter {
#[inline]
pub fn new(handle: i32) -> __LibCWriter {
__LibCWriter(handle)
}
#[inline]
pub fn write_fmt(&mut self, args: core::fmt::Arguments) -> core::fmt::Result {
core::fmt::Write::write_fmt(self, args)
}
#[inline]
pub fn write_str(&mut self, s: &str) -> core::fmt::Result {
__libc_println(self.0, s)
}
#[inline]
pub fn write_nl(&mut self) -> core::fmt::Result {
__libc_println(self.0, __LIBC_NEWLINE)
}
}
#[doc(hidden)]
#[inline]
pub fn __libc_println(handle: i32, msg: &str) -> core::fmt::Result {
let msg = msg.as_bytes();
let mut written = 0;
while written < msg.len() {
match unsafe { libc_write(handle, &msg[written..]) } {
// Ignore errors
None | Some(0) => break,
Some(res) => written += res,
}
}
Ok(())
}
#[cfg(not(windows))]
unsafe fn libc_write(handle: i32, bytes: &[u8]) -> Option<usize> {
usize::try_from(unsafe {
libc::write(
handle,
bytes.as_ptr().cast::<core::ffi::c_void>(),
bytes.len(),
)
})
.ok()
}
#[cfg(windows)]
unsafe fn libc_write(handle: i32, bytes: &[u8]) -> Option<usize> {
usize::try_from(unsafe {
libc::write(
handle,
bytes.as_ptr().cast::<core::ffi::c_void>(),
libc::c_uint::try_from(bytes.len()).unwrap_or(libc::c_uint::MAX),
)
})
.ok()
}
/// Macro for printing to the standard output, with a newline.
///
/// Does not panic on failure to write - instead silently ignores errors.
///
/// See [`println!`](https://doc.rust-lang.org/std/macro.println.html) for
/// full documentation.
///
/// You may wish to `use libc_print::std_name::*` to use a replacement
/// `println!` macro instead of this longer name.
#[macro_export]
macro_rules! libc_println {
() => { $crate::libc_println!("") };
($($arg:tt)*) => {
{
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
stm.write_fmt(format_args!($($arg)*));
stm.write_nl();
}
}
};
}
/// Macro for printing to the standard output.
///
/// Does not panic on failure to write - instead silently ignores errors.
///
/// See [`print!`](https://doc.rust-lang.org/std/macro.print.html) for
/// full documentation.
///
/// You may wish to `use libc_print::std_name::*` to use a replacement
/// `print!` macro instead of this longer name.
#[macro_export]
macro_rules! libc_print {
($($arg:tt)*) => {
{
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
stm.write_fmt(format_args!($($arg)*));
}
}
};
}
/// Macro for printing to the standard error, with a newline.
///
/// Does not panic on failure to write - instead silently ignores errors.
///
/// See [`eprintln!`](https://doc.rust-lang.org/std/macro.eprintln.html) for
/// full documentation.
///
/// You may wish to `use libc_print::std_name::*` to use a replacement
/// `eprintln!` macro instead of this longer name.
#[macro_export]
macro_rules! libc_eprintln {
() => { $crate::libc_eprintln!("") };
($($arg:tt)*) => {
{
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
stm.write_fmt(format_args!($($arg)*));
stm.write_nl();
}
}
};
}
/// Macro for printing to the standard error.
///
/// Does not panic on failure to write - instead silently ignores errors.
///
/// See [`eprint!`](https://doc.rust-lang.org/std/macro.eprint.html) for
/// full documentation.
///
/// You may wish to `use libc_print::std_name::*` to use a replacement
/// `eprint!` macro instead of this longer name.
#[macro_export]
macro_rules! libc_eprint {
($($arg:tt)*) => {
{
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
stm.write_fmt(format_args!($($arg)*));
}
}
};
}
/// Macro for printing a static string to the standard output.
///
/// Does not panic on failure to write - instead silently ignores errors.
#[macro_export]
macro_rules! libc_write {
($arg:expr) => {
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
stm.write_str($arg);
}
};
}
/// Macro for printing a static string to the standard error.
///
/// Does not panic on failure to write - instead silently ignores errors.
#[macro_export]
macro_rules! libc_ewrite {
($arg:expr) => {
{
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
stm.write_str($arg);
}
}
};
}
/// Macro for printing a static string to the standard output, with a newline.
///
/// Does not panic on failure to write - instead silently ignores errors.
#[macro_export]
macro_rules! libc_writeln {
($arg:expr) => {
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDOUT);
stm.write_str($arg);
stm.write_nl();
}
};
}
/// Macro for printing a static string to the standard error, with a newline.
///
/// Does not panic on failure to write - instead silently ignores errors.
#[macro_export]
macro_rules! libc_ewriteln {
($arg:expr) => {
{
#[allow(unused_must_use)]
{
let mut stm = $crate::__LibCWriter::new($crate::__LIBC_STDERR);
stm.write_str($arg);
stm.write_nl();
}
}
};
}
/// Prints and returns the value of a given expression for quick and dirty
/// debugging.
///
/// An example:
///
/// ```rust
/// let a = 2;
/// let b = dbg!(a * 2) + 1;
/// // ^-- prints: [src/main.rs:2] a * 2 = 4
/// assert_eq!(b, 5);
/// ```
///
/// See [dbg!](https://doc.rust-lang.org/std/macro.dbg.html) for full documentation.
///
/// You may wish to `use libc_print::std_name::*` to use a replacement
/// `dbg!` macro instead of this longer name.
#[macro_export]
macro_rules! libc_dbg {
() => {
$crate::libc_eprintln!("[{}:{}]", $file!(), $line!())
};
($val:expr $(,)?) => {
match $val {
tmp => {
$crate::libc_eprintln!("[{}:{}] {} = {:#?}", file!(), line!(), stringify!($val), &tmp);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::libc_dbg!($val)),+,)
};
}

42
src/process.rs Normal file
View File

@@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 Emma Tebibyte <emma@tebibyte.media>
* SPDX-License-Identifier: LGPL-3.0-or-later
*
* This file is part of SPD.
*
* SPD is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SPD is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SPD. If not, see <https://www.gnu.org/licenses/>.
*
* This file incorporates work covered by the following copyright and permission
* notice:
*
* Copyright (c) 2023 YAC
* SPDX-License-Identifier: FSFAP
*
* Copying and distribution of this file, with or without modification, are
* permitted in any medium without royalty provided the copyright notice and
* this notice are preserved. This file is offered as-is, without any
* warranty.
*/
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
extern crate core;
pub fn exit(code: u32) -> ! {
unsafe { libc::exit(code as i32 as libc::c_int); }
}