added comments and cleaned code

This commit is contained in:
Lu Baumann 2023-07-02 23:04:56 +02:00
parent 37702c02e3
commit c0171c0986
2 changed files with 23 additions and 27 deletions

View File

@ -1,3 +1,5 @@
//! Constant values
use std::process::exit; use std::process::exit;
use lazy_static::lazy_static; use lazy_static::lazy_static;
@ -30,6 +32,8 @@ If -e is in effect, the following sequences are recognized:
\0NNN byte with octal value NNN (1 to 3 digits) \0NNN byte with octal value NNN (1 to 3 digits)
\xHH byte with hexadecimal value HH (1 to 2 digits) \xHH byte with hexadecimal value HH (1 to 2 digits)
"#; "#;
/// Special characters, that can be replaced without further logic
pub const SIMPLE_SPECIAL_SEQUENCES: [(&str, &str); 8] = [ pub const SIMPLE_SPECIAL_SEQUENCES: [(&str, &str); 8] = [
(r#"\a"#, "\x07"), (r#"\a"#, "\x07"),
(r#"\b"#, "\x08"), (r#"\b"#, "\x08"),
@ -41,6 +45,7 @@ pub const SIMPLE_SPECIAL_SEQUENCES: [(&str, &str); 8] = [
(r#"\v"#, "\x0b"), (r#"\v"#, "\x0b"),
]; ];
lazy_static! { lazy_static! {
// \NNN or \0NNN with N in octal
pub static ref OCTAL_REGEX: Regex = Regex::new(r#"\\(?:([1-7][0-7]{0,2}|0[0-7]{0,3}))"#) pub static ref OCTAL_REGEX: Regex = Regex::new(r#"\\(?:([1-7][0-7]{0,2}|0[0-7]{0,3}))"#)
.map_or_else( .map_or_else(
|e| { |e| {
@ -52,7 +57,8 @@ lazy_static! {
}, },
|v| v |v| v
); );
pub static ref HEX_REGEX: Regex = Regex::new(r#"\\x([0-9A-F]{0,2})"#).map_or_else( // \xFF with F in octal
pub static ref HEX_REGEX: Regex = Regex::new(r#"\\x([0-9A-F]{1,2})"#).map_or_else(
|e| { |e| {
eprintln!( eprintln!(
"programming error: cannot compile regex pattern for hex regex match: {}", "programming error: cannot compile regex pattern for hex regex match: {}",

View File

@ -1,3 +1,5 @@
//! Basic rewrite of the echo GNU core utility, maintaining compatability
use std::{ use std::{
env::args, env::args,
io::{self, Write}, io::{self, Write},
@ -8,6 +10,7 @@ mod consts;
use crate::consts::{HELP_DIALOG, HEX_REGEX, OCTAL_REGEX, SIMPLE_SPECIAL_SEQUENCES, VERSION}; use crate::consts::{HELP_DIALOG, HEX_REGEX, OCTAL_REGEX, SIMPLE_SPECIAL_SEQUENCES, VERSION};
/// Settings to be read from cli
struct Settings { struct Settings {
trailing_newline: bool, trailing_newline: bool,
interpret_backslash_escapes: bool, interpret_backslash_escapes: bool,
@ -22,6 +25,7 @@ impl Default for Settings {
} }
} }
/// Replaces escaped octal representations with corresponding characters
fn replace_octal(string: String) -> String { fn replace_octal(string: String) -> String {
let mut res = string; let mut res = string;
while let Some(captures) = OCTAL_REGEX.captures( while let Some(captures) = OCTAL_REGEX.captures(
@ -29,19 +33,12 @@ fn replace_octal(string: String) -> String {
&res.clone(), &res.clone(),
) { ) {
if let (Some(entire_match), Some(capture)) = (captures.get(0), captures.get(1)) { if let (Some(entire_match), Some(capture)) = (captures.get(0), captures.get(1)) {
let mut contents = capture.as_str();
if contents.is_empty() {
contents = "0" // GNU echo interprets \0 as \00
}
res = format!( res = format!(
"{}{}", "{}{}{}",
&res[..entire_match.start()], &res[..entire_match.start()],
OCTAL_REGEX.replace( &u8::from_str_radix(capture.as_str(), 8)
&res[entire_match.start()..], .map_or_else(|_| 255 as char, |v| v as char),
&u8::from_str_radix(contents, 8) &res[entire_match.end()..],
.map_or_else(|_| 255 as char, |v| v as char)
.to_string(),
)
); );
} else { } else {
println!("error matching octal regex. aborting"); println!("error matching octal regex. aborting");
@ -51,29 +48,20 @@ fn replace_octal(string: String) -> String {
res res
} }
/// Replaces escaped hex representations with corresponding characters
fn replace_hex(string: String) -> String { fn replace_hex(string: String) -> String {
let mut res = string; let mut res = string;
let mut search_at = 0; while let Some(captures) = HEX_REGEX.captures(
while let Some(captures) = HEX_REGEX.captures_at(
#[allow(clippy::redundant_clone)] #[allow(clippy::redundant_clone)]
&res.clone(), &res.clone(),
search_at,
) { ) {
if let (Some(entire_match), Some(capture)) = (captures.get(0), captures.get(1)) { if let (Some(entire_match), Some(capture)) = (captures.get(0), captures.get(1)) {
let contents = capture.as_str();
if contents.is_empty() {
search_at = entire_match.end();
continue; // GNU echo does not interpret \x
}
res = format!( res = format!(
"{}{}", "{}{}{}",
&res[..entire_match.start()], &res[..entire_match.start()],
HEX_REGEX.replace( &u8::from_str_radix(capture.as_str(), 16)
&res[entire_match.start()..], .map_or_else(|_| 255 as char, |v| v as char),
&u8::from_str_radix(contents, 16) &res[entire_match.end()..],
.map_or_else(|_| 255 as char, |v| v as char)
.to_string(),
)
); );
} else { } else {
println!("error matching hex regex. aborting"); println!("error matching hex regex. aborting");
@ -83,6 +71,7 @@ fn replace_hex(string: String) -> String {
res res
} }
/// Format passed argument based on settings
fn format_arg(arg: String, settings: &mut Settings) -> String { fn format_arg(arg: String, settings: &mut Settings) -> String {
if settings.interpret_backslash_escapes { if settings.interpret_backslash_escapes {
let mut res = arg; let mut res = arg;
@ -116,6 +105,7 @@ fn format_arg(arg: String, settings: &mut Settings) -> String {
} }
} }
/// Write string to stdout as unicode characters
fn write_as_unicode(string: String) { fn write_as_unicode(string: String) {
let stdout = io::stdout(); let stdout = io::stdout();
if let Err(e) = stdout.lock().write_all( if let Err(e) = stdout.lock().write_all(