Compare commits

...

2 commits

Author SHA1 Message Date
fdfddec576
Add aliases 2025-05-02 17:30:25 -05:00
2bb2c69269
Implement man page generation 2025-05-02 17:30:14 -05:00
3 changed files with 73 additions and 22 deletions

View file

@ -1,4 +1,6 @@
#![allow(unused_imports)]
use roff::{Roff, bold, italic, roman}; use roff::{Roff, bold, italic, roman};
use std::{env, path::PathBuf};
fn main() { fn main() {
let page = Roff::new() let page = Roff::new()
@ -6,27 +8,23 @@ fn main() {
.control("SH", ["NAME"]) .control("SH", ["NAME"])
.text([roman("sesh - Semantic Shell")]) .text([roman("sesh - Semantic Shell")])
.control("SH", ["SYNOPSIS"]) .control("SH", ["SYNOPSIS"])
.text([ .text([bold("sesh"), roman(" [options]")])
bold("sesh"),
roman(" [options]"),
])
.control("SH", ["DESCRIPTION"]) .control("SH", ["DESCRIPTION"])
.text([ .text([
bold("sesh"), bold("sesh"),
roman("is a shell designed to be as semantic to use as possible"), roman("is a shell designed to be as semantic to use as possible"),
]) ])
.control("SH", ["OPTIONS"])
.control("TP", [])
.text([
bold("-n"),
roman(", "),
bold("--bits"),
roman("="),
italic("BITS"),
])
.text([roman(
"Set the number of bits to modify. Default is one bit.",
)])
.render(); .render();
print!("{}", page); std::fs::write(
PathBuf::from(env::var_os("OUT_DIR").unwrap())
.parent()
.unwrap()
.parent()
.unwrap()
.parent()
.unwrap()
.join("sesh.1"),
page,
)
.unwrap();
} }

View file

@ -2,7 +2,10 @@
#![allow(clippy::type_complexity)] #![allow(clippy::type_complexity)]
/// List of builtins /// List of builtins
pub const BUILTINS: [(&str, fn (args: Vec<String>, unsplit_args: String, state: &mut super::State) -> i32); 3] = [("cd", cd), ("exit", exit), ("echo", echo)]; pub const BUILTINS: [(
&str,
fn(args: Vec<String>, unsplit_args: String, state: &mut super::State) -> i32,
); 4] = [("cd", cd), ("exit", exit), ("echo", echo), ("alias", alias)];
/// Change the directory /// Change the directory
pub fn cd(args: Vec<String>, _: String, state: &mut super::State) -> i32 { pub fn cd(args: Vec<String>, _: String, state: &mut super::State) -> i32 {
@ -25,7 +28,7 @@ pub fn exit(_: Vec<String>, _: String, _: &mut super::State) -> i32 {
/// Echo a string /// Echo a string
pub fn echo(args: Vec<String>, mut unsplit_args: String, _: &mut super::State) -> i32 { pub fn echo(args: Vec<String>, mut unsplit_args: String, _: &mut super::State) -> i32 {
unsplit_args = unsplit_args[5..].to_string(); unsplit_args = unsplit_args[(args[0].len() + 1)..].to_string();
if args.len() != 1 && args[1] == "-e" { if args.len() != 1 && args[1] == "-e" {
unsplit_args = unsplit_args[3..].to_string(); unsplit_args = unsplit_args[3..].to_string();
let escaped = crate::escapes::interpret_escaped_string(&unsplit_args); let escaped = crate::escapes::interpret_escaped_string(&unsplit_args);
@ -38,3 +41,29 @@ pub fn echo(args: Vec<String>, mut unsplit_args: String, _: &mut super::State) -
println!("{}", unsplit_args); println!("{}", unsplit_args);
0 0
} }
/// Add an alias
pub fn alias(args: Vec<String>, _: String, state: &mut super::State) -> i32 {
if args.len() == 1 {
for alias in &state.aliases {
println!("`{}`: `{}`", alias.name, alias.to);
}
return 0;
}
if args.len() == 2 {
for alias in &state.aliases {
if alias.name != args[1] {
continue;
}
println!("`{}`: `{}`", alias.name, alias.to);
}
return 0;
}
state.aliases.push(super::Alias {
name: args[1].clone(),
to: args[2].clone(),
});
0
}

View file

@ -48,6 +48,14 @@ enum Variable {
Nonlocal(OsString), Nonlocal(OsString),
} }
/// A single alias
#[derive(Clone, Debug, PartialEq, Eq)]
struct Alias {
/// alias from
name: String,
/// to
to: String
}
/// The state of the shell /// The state of the shell
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct State { struct State {
@ -61,6 +69,8 @@ struct State {
history: Vec<State>, history: Vec<State>,
/// Current working directory. /// Current working directory.
working_dir: PathBuf, working_dir: PathBuf,
/// A list of aliases from name to actual
aliases: Vec<Alias>
} }
unsafe impl Sync for State {} unsafe impl Sync for State {}
@ -160,13 +170,26 @@ fn eval(statement: &str, state: &mut State) {
let statements = split_statements(&statement); let statements = split_statements(&statement);
for statement in statements { for statement in statements {
let statement_split = split_statement(&statement); let mut statement_split = split_statement(&statement);
if statement.is_empty() || statement_split[0].is_empty() { if statement.is_empty() || statement_split[0].is_empty() {
continue; continue;
} }
let mut program_name = statement_split[0].clone();
for alias in &state.aliases {
if program_name == alias.name {
let to_split = split_statement(&alias.to);
for (i, item) in to_split[1..].iter().enumerate() {
statement_split.insert(i+1, (*item).clone());
}
program_name = to_split[0].clone();
continue;
}
}
if let Some(builtin) = builtins::BUILTINS if let Some(builtin) = builtins::BUILTINS
.iter() .iter()
.find(|v| v.0 == statement_split[0]) .find(|v| v.0 == program_name)
{ {
let status = builtin.1(statement_split, statement.to_string(), state); let status = builtin.1(statement_split, statement.to_string(), state);
for (i, var) in state.shell_env.clone().into_iter().enumerate() { for (i, var) in state.shell_env.clone().into_iter().enumerate() {
@ -181,7 +204,7 @@ fn eval(statement: &str, state: &mut State) {
}); });
continue; continue;
} }
match std::process::Command::new(statement_split[0].clone()) match std::process::Command::new(program_name.clone())
.args(&statement_split[1..]) .args(&statement_split[1..])
.current_dir(state.working_dir.clone()) .current_dir(state.working_dir.clone())
.spawn() .spawn()
@ -258,6 +281,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
history: Vec::new(), history: Vec::new(),
working_dir: std::env::current_dir() working_dir: std::env::current_dir()
.unwrap_or(std::env::home_dir().unwrap_or(PathBuf::from("/"))), .unwrap_or(std::env::home_dir().unwrap_or(PathBuf::from("/"))),
aliases: Vec::new(),
}; };
state.shell_env.push(ShellVar { state.shell_env.push(ShellVar {
name: "PROMPT1".to_string(), name: "PROMPT1".to_string(),