commit d9bba7c0c69b3050e3c785b4fabb4865f89e71a8 Author: femsci Date: Wed Feb 14 17:51:35 2024 +0100 ugly smol diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1dfbde1 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "urlenc" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..edc42df --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "urlenc" +version = "0.1.0" +edition = "2021" +authors = ["femsci"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/README.md b/README.md new file mode 100644 index 0000000..f8228c3 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# urlenc + +A quick utility for URL percent encoding and decoding diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f5064eb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,101 @@ +use std::{ + fs::File, + io::{BufRead, BufReader, Read}, +}; + +fn main() { + let decode = std::env::args().any(|f| f == "-d"); + + let mut stream: Box = match std::env::args().last() { + Some(x) => { + if x == "-" { + Box::new(BufReader::new(std::io::stdin())) + } else { + Box::new(BufReader::new( + File::open(&x).expect(&format!("No file found: {}", &x)), + )) + } + } + None => { + eprintln!("No file provided!"); + std::process::exit(1); + } + }; + + if decode { + url_decode_stream(&mut stream); + } else { + url_encode(&mut stream); + } +} + +fn url_decode_stream(stream: &mut Box) { + let mut str = String::new(); + let mut buf = Vec::new(); + let mut val_buf: [u8; 2] = [0; 2]; + + while stream.read_until(b'%', &mut buf).expect("Cannot read data") > 0 { + let string = String::from_utf8(buf.to_vec()).unwrap().replace('+', " "); + + if !string.ends_with('%') { + str.push_str(&string); + break; + } + + str.push_str(string.trim_end_matches('%')); + + stream + .read_exact(&mut val_buf) + .expect("Cannot retrieve value"); + + let chr = get_char(val_buf[0], val_buf[1]); + + str.push(chr); + + buf.clear(); + } + + println!("{}", &str); +} + +fn get_char(msc: u8, lsc: u8) -> char { + let extract_byte = |x: u8| { + let x_chr = (x as char).to_ascii_lowercase(); + + if x_chr.is_ascii_digit() { + (x_chr as u8) - b'0' + } else if x_chr.is_ascii_hexdigit() { + (x_chr as u8) - b'a' + 10 + } else { + eprintln!("Invalid character for encoding found: {}", x_chr); + std::process::exit(2); + } + }; + + let msb = extract_byte(msc); + let lsb = extract_byte(lsc); + + (msb * 16 + lsb) as char +} + +fn get_percent(chr: char) -> String { + format!("%{:x}", chr as u8) +} + +fn url_encode(stream: &mut Box) { + let mut line = String::new(); + while stream.read_line(&mut line).expect("Cannot read data") > 0 { + let encoded = line + .chars() + .map(|f| { + if f.is_alphanumeric() || ['-', '_', '.', '~'].contains(&f) { + f.to_string() + } else { + get_percent(f) + } + }) + .collect::(); + print!("{}", &encoded); + line.clear(); + } +}