From 9bda8aa1d6f82feb9564cb2c4999568fbe43ee2e Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 28 Feb 2024 23:11:06 -0500 Subject: [PATCH 1/5] wc, echo: optimize string alloc --- display/src/echo.rs | 3 +-- text/src/wc.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/display/src/echo.rs b/display/src/echo.rs index 75abbc3..4c62656 100644 --- a/display/src/echo.rs +++ b/display/src/echo.rs @@ -13,8 +13,7 @@ use gettextrs::{bind_textdomain_codeset, textdomain}; use plib::PROJECT_NAME; fn translate_str(s: &str) -> String { - let mut output = String::new(); - output.reserve(s.len()); + let mut output = String::with_capacity(s.len()); let mut in_bs = false; let mut nl = true; diff --git a/text/src/wc.rs b/text/src/wc.rs index 5ed0f9f..048d5af 100644 --- a/text/src/wc.rs +++ b/text/src/wc.rs @@ -63,8 +63,7 @@ impl CountInfo { } fn build_display_str(args: &Args, count: &CountInfo, filename: &str) -> String { - let mut output = String::new(); - output.reserve(filename.len() + (3 * 10)); + let mut output = String::with_capacity(filename.len() + (3 * 10)); if args.lines { let numstr = format!("{:>8}", count.nl); From 870c106b881d34839edeaff63f8b13834dc1f510 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 28 Feb 2024 23:14:25 -0500 Subject: [PATCH 2/5] paste: support rotating delimiters --- text/src/paste.rs | 88 +++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 77 insertions(+), 11 deletions(-) diff --git a/text/src/paste.rs b/text/src/paste.rs index 8cde6d9..36e9071 100644 --- a/text/src/paste.rs +++ b/text/src/paste.rs @@ -6,7 +6,10 @@ // file in the root directory of this project. // SPDX-License-Identifier: MIT // -// TODO: stdin ("-"), delimiter list +// TODO: +// - stdin ("-") +// - fix: empty-string delimiters \0 +// - improve: don't open all files at once in --serial mode // extern crate clap; @@ -26,6 +29,10 @@ struct Args { #[arg(short, long)] serial: bool, + /// Delimiter list + #[arg(short, long)] + delims: Option, + /// One or more input files. files: Vec, } @@ -38,19 +45,71 @@ struct PasteFile { } struct PasteInfo { - delim: char, inputs: Vec, } impl PasteInfo { fn new() -> PasteInfo { - PasteInfo { - delim: '\t', - inputs: Vec::new(), + PasteInfo { inputs: Vec::new() } + } +} + +struct DelimInfo { + cur_delim: usize, + delims: String, +} + +impl DelimInfo { + fn new() -> DelimInfo { + DelimInfo { + cur_delim: 0, + delims: String::from("\t"), + } + } + + fn delim(&mut self) -> char { + let ch = self.delims.chars().nth(self.cur_delim).unwrap(); + + self.advance(); + + ch + } + + fn advance(&mut self) { + if self.delims.len() > 1 { + self.cur_delim = self.cur_delim + 1; + if self.cur_delim >= self.delims.len() { + self.cur_delim = 0; + } } } } +fn xlat_delim_str(s: &str) -> String { + let mut output = String::with_capacity(s.len() + 10); + + let mut in_escape = false; + for ch in s.chars() { + if in_escape { + let out_ch = match ch { + 'n' => '\n', + 't' => '\t', + '0' => '\0', + _ => ch, + }; + + output.push(out_ch); + in_escape = false; + } else if ch == '\\' { + in_escape = true; + } else { + output.push(ch); + } + } + + output +} + fn open_inputs(args: &Args, info: &mut PasteInfo) -> io::Result<()> { // open each input for filename in &args.files { @@ -79,7 +138,7 @@ fn open_inputs(args: &Args, info: &mut PasteInfo) -> io::Result<()> { Ok(()) } -fn paste_files_serial(info: &mut PasteInfo) -> io::Result<()> { +fn paste_files_serial(mut info: PasteInfo, mut dinfo: DelimInfo) -> io::Result<()> { // loop serially for each input file for input in &mut info.inputs { let mut first_line = true; @@ -107,7 +166,7 @@ fn paste_files_serial(info: &mut PasteInfo) -> io::Result<()> { if first_line { print!("{}", slice); } else { - print!("{}{}", info.delim, slice); + print!("{}{}", dinfo.delim(), slice); } } @@ -120,7 +179,7 @@ fn paste_files_serial(info: &mut PasteInfo) -> io::Result<()> { Ok(()) } -fn paste_files(info: &mut PasteInfo) -> io::Result<()> { +fn paste_files(mut info: PasteInfo, mut dinfo: DelimInfo) -> io::Result<()> { // for each input line, across N files loop { let mut output = String::new(); @@ -156,7 +215,7 @@ fn paste_files(info: &mut PasteInfo) -> io::Result<()> { // next delimiter } else { - output.push(info.delim); + output.push(dinfo.delim()); } } @@ -185,13 +244,20 @@ fn main() -> Result<(), Box> { bind_textdomain_codeset(PROJECT_NAME, "UTF-8")?; let mut state = PasteInfo::new(); + let mut delim_state = DelimInfo::new(); + match &args.delims { + None => {} + Some(dlm) => { + delim_state.delims = xlat_delim_str(dlm); + } + } open_inputs(&args, &mut state)?; if args.serial { - paste_files_serial(&mut state)?; + paste_files_serial(state, delim_state)?; } else { - paste_files(&mut state)?; + paste_files(state, delim_state)?; } Ok(()) From c46d67c048f2d4f3a432bd9e810bdcbe9dd513f0 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Wed, 28 Feb 2024 23:29:35 -0500 Subject: [PATCH 3/5] add util: tty --- README.md | 2 +- users/Cargo.toml | 4 ++++ users/src/tty.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 users/src/tty.rs diff --git a/README.md b/README.md index 0a5f96d..dfad3b8 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ A similar project from the author, written in C++, is https://github.com/jgarzik - [ ] tr - [x] true - [ ] tsort - - [ ] tty + - [x] tty - [ ] ulimit - [ ] uname - [ ] uncompress (compress cat.) diff --git a/users/Cargo.toml b/users/Cargo.toml index f9c03fa..419b822 100644 --- a/users/Cargo.toml +++ b/users/Cargo.toml @@ -18,3 +18,7 @@ path = "src/logname.rs" name = "logger" path = "src/logger.rs" +[[bin]] +name = "tty" +path = "src/tty.rs" + diff --git a/users/src/tty.rs b/users/src/tty.rs new file mode 100644 index 0000000..31f7d05 --- /dev/null +++ b/users/src/tty.rs @@ -0,0 +1,40 @@ +// +// Copyright (c) 2024 Jeff Garzik +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// + +extern crate libc; +use std::ffi::CStr; + +fn main() { + let is_tty = unsafe { libc::isatty(libc::STDIN_FILENO) }; + if is_tty == 0 { + println!("not a tty"); + std::process::exit(1); + } + + let ttyname = unsafe { + // Call getlogin to get the username as a *mut c_char + let c_str = libc::ttyname(libc::STDIN_FILENO); + + // Check if the pointer is not null + if c_str.is_null() { + panic!("Failed to get tty name"); + } + + // Convert the *mut c_char to a &CStr + let c_str = CStr::from_ptr(c_str); + + // Convert the &CStr to a Rust String + match c_str.to_str() { + Ok(s) => s.to_owned(), // Successfully converted CStr to Rust String + Err(e) => panic!("Failed to convert tty name to a Rust String: {}", e), + } + }; + + println!("{}", ttyname); +} From 94e58094199d7f362d1302a0a4a8505cf31e2713 Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 29 Feb 2024 00:02:06 -0500 Subject: [PATCH 4/5] add util: uname --- Cargo.lock | 27 +++++++++++-- Cargo.toml | 1 + README.md | 2 +- sys/Cargo.toml | 15 ++++++++ sys/src/uname.rs | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 sys/Cargo.toml create mode 100644 sys/src/uname.rs diff --git a/Cargo.lock b/Cargo.lock index 4b64de7..7afd312 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -226,9 +226,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "malloc_buf" @@ -349,6 +349,16 @@ dependencies = [ "plib", ] +[[package]] +name = "posixutils-sys" +version = "0.1.0" +dependencies = [ + "clap", + "gettext-rs", + "plib", + "uname", +] + [[package]] name = "posixutils-text" version = "0.1.0" @@ -471,9 +481,9 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "2.0.51" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ab617d94515e94ae53b8406c628598680aa0c9587474ecbe58188f7b345d66c" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -532,6 +542,15 @@ dependencies = [ "time-core", ] +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "unicode-ident" version = "1.0.12" diff --git a/Cargo.toml b/Cargo.toml index 96005d3..ddffa15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "misc", "pathnames", "plib", + "sys", "text", "tree", "users", diff --git a/README.md b/README.md index dfad3b8..473c1d0 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ A similar project from the author, written in C++, is https://github.com/jgarzik - [ ] tsort - [x] tty - [ ] ulimit - - [ ] uname + - [x] uname - [ ] uncompress (compress cat.) - [ ] unexpand - [ ] unget diff --git a/sys/Cargo.toml b/sys/Cargo.toml new file mode 100644 index 0000000..10cafc6 --- /dev/null +++ b/sys/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "posixutils-sys" +version = "0.1.0" +edition = "2021" + +[dependencies] +plib = { path = "../plib" } +clap = { version = "4", features = ["derive"] } +gettext-rs = { version = "0.7", features = ["gettext-system"] } +uname = "0.1" + +[[bin]] +name = "uname" +path = "src/uname.rs" + diff --git a/sys/src/uname.rs b/sys/src/uname.rs new file mode 100644 index 0000000..73f02e4 --- /dev/null +++ b/sys/src/uname.rs @@ -0,0 +1,98 @@ +// +// Copyright (c) 2024 Jeff Garzik +// +// This file is part of the posixutils-rs project covered under +// the MIT License. For the full license text, please see the LICENSE +// file in the root directory of this project. +// SPDX-License-Identifier: MIT +// +// TODO: +// - the "-v" option is specified by POSIX and cannot be clap default -v +// + +extern crate clap; +extern crate plib; +extern crate uname; + +use clap::Parser; +use gettextrs::{bind_textdomain_codeset, textdomain}; +use plib::PROJECT_NAME; + +/// uname - return system name +#[derive(Parser, Debug)] +#[command(author, version, about, long_about)] +struct Args { + /// Behave as though all of the options -mnrsv were specified. + #[arg(short, long)] + all: bool, + + /// Write the name of the hardware type on which the system is running to standard output. + #[arg(short, long)] + machine: bool, + + /// Write the name of this node within an implementation-defined communications network. + #[arg(short, long)] + node: bool, + + /// Write the current release level of the operating system implementation. + #[arg(short, long)] + release: bool, + + /// Write the name of the implementation of the operating system. + #[arg(short, long)] + system: bool, + + /// Write the current version level of this release of the operating system implementation. + #[arg(short, long)] + osversion: bool, +} + +fn print_info(args: &Args, info: uname::Info) { + let mut outs = Vec::new(); + + if args.system { + outs.push(info.sysname); + } + if args.node { + outs.push(info.nodename); + } + if args.release { + outs.push(info.release); + } + if args.osversion { + outs.push(info.version); + } + if args.machine { + outs.push(info.machine); + } + + println!("{}", outs.join(" ")); +} + +fn main() -> Result<(), Box> { + // parse command line arguments + let mut args = Args::parse(); + + if args.all { + args.machine = true; + args.node = true; + args.release = true; + args.system = true; + args.osversion = true; + } + + textdomain(PROJECT_NAME)?; + bind_textdomain_codeset(PROJECT_NAME, "UTF-8")?; + + let mut exit_code = 0; + + match uname::uname() { + Ok(info) => print_info(&args, info), + Err(e) => { + eprintln!("{}", e); + exit_code = 1; + } + } + + std::process::exit(exit_code) +} From 69f18ff16e6d8e3f62591564bd11f0dafe93945f Mon Sep 17 00:00:00 2001 From: Jeff Garzik Date: Thu, 29 Feb 2024 00:10:18 -0500 Subject: [PATCH 5/5] code docs cleanup --- display/NOTES.md | 11 ----------- display/src/echo.rs | 6 ++++++ file/NOTES.md | 10 ---------- file/src/cat.rs | 5 +++++ link/src/link.rs | 1 + link/src/ln.rs | 1 + link/src/unlink.rs | 2 ++ pathnames/src/basename.rs | 1 + pathnames/src/dirname.rs | 1 + xform/NOTES.md | 9 --------- xform/src/cksum.rs | 3 +++ 11 files changed, 20 insertions(+), 30 deletions(-) delete mode 100644 display/NOTES.md delete mode 100644 file/NOTES.md delete mode 100644 xform/NOTES.md diff --git a/display/NOTES.md b/display/NOTES.md deleted file mode 100644 index 2a6331a..0000000 --- a/display/NOTES.md +++ /dev/null @@ -1,11 +0,0 @@ - -# display programs: notes - -## to-do - -* echo needs to translate backslash-escaped octal numbers: -``` -\0num - Write an 8-bit value that is the zero, one, two, or three-digit octal number _num_. -``` - diff --git a/display/src/echo.rs b/display/src/echo.rs index 4c62656..0a7eea1 100644 --- a/display/src/echo.rs +++ b/display/src/echo.rs @@ -6,6 +6,12 @@ // file in the root directory of this project. // SPDX-License-Identifier: MIT // +// TODO: +// - echo needs to translate backslash-escaped octal numbers: +// ``` +// \0num +// Write an 8-bit value that is the 0, 1, 2 or 3-digit octal number _num_. +// extern crate plib; diff --git a/file/NOTES.md b/file/NOTES.md deleted file mode 100644 index 2983240..0000000 --- a/file/NOTES.md +++ /dev/null @@ -1,10 +0,0 @@ - -# cat: notes - -## to-do - -* Bug: if stdout write_all() produces Err, the program will erroneously -output the filename as the culprit, rather than the string "stdout" -* Questionable behavior: if write_all() produces Err, the program will -continue to the next file, rather than stopping. - diff --git a/file/src/cat.rs b/file/src/cat.rs index b8c0bf5..dd6d74a 100644 --- a/file/src/cat.rs +++ b/file/src/cat.rs @@ -6,6 +6,11 @@ // file in the root directory of this project. // SPDX-License-Identifier: MIT // +// TODO: +// - Bug: if stdout write_all() produces Err, the program will erroneously +// output the filename as the culprit, rather than the string "stdout" +// - Questionable behavior: if write_all() produces Err, the program will +// continue to the next file, rather than stopping. extern crate clap; extern crate plib; diff --git a/link/src/link.rs b/link/src/link.rs index d3a3cd1..579e15f 100644 --- a/link/src/link.rs +++ b/link/src/link.rs @@ -15,6 +15,7 @@ use gettextrs::{bind_textdomain_codeset, textdomain}; use plib::PROJECT_NAME; use std::{fs, io}; +/// link - call link function #[derive(Parser, Debug)] #[command(author, version, about, long_about)] struct Args { diff --git a/link/src/ln.rs b/link/src/ln.rs index f667624..97a2a24 100644 --- a/link/src/ln.rs +++ b/link/src/ln.rs @@ -16,6 +16,7 @@ use plib::PROJECT_NAME; use std::path::{Path, PathBuf}; use std::{fs, io}; +/// ln - link files #[derive(Parser, Debug)] #[command(author, version, about, long_about)] struct Args { diff --git a/link/src/unlink.rs b/link/src/unlink.rs index 44d267f..697597e 100644 --- a/link/src/unlink.rs +++ b/link/src/unlink.rs @@ -15,9 +15,11 @@ use gettextrs::{bind_textdomain_codeset, textdomain}; use plib::PROJECT_NAME; use std::{fs, io}; +/// unlink - call the unlink function #[derive(Parser, Debug)] #[command(author, version, about, long_about)] struct Args { + /// The pathname of an existing file. pathname: String, } diff --git a/pathnames/src/basename.rs b/pathnames/src/basename.rs index 3d24512..f3285ed 100644 --- a/pathnames/src/basename.rs +++ b/pathnames/src/basename.rs @@ -15,6 +15,7 @@ use gettextrs::{bind_textdomain_codeset, textdomain}; use plib::PROJECT_NAME; use std::path::Path; +/// basename - return non-directory portion of a pathname #[derive(Parser, Debug)] #[command(author, version, about, long_about)] struct Args { diff --git a/pathnames/src/dirname.rs b/pathnames/src/dirname.rs index 356237c..48c142d 100644 --- a/pathnames/src/dirname.rs +++ b/pathnames/src/dirname.rs @@ -15,6 +15,7 @@ use gettextrs::{bind_textdomain_codeset, textdomain}; use plib::PROJECT_NAME; use std::path::PathBuf; +/// dirname - return the directory portion of a pathname #[derive(Parser, Debug)] #[command(author, version, about, long_about)] struct Args { diff --git a/xform/NOTES.md b/xform/NOTES.md deleted file mode 100644 index 8c9be21..0000000 --- a/xform/NOTES.md +++ /dev/null @@ -1,9 +0,0 @@ - -# posixutils-xform notes - -## to-do - -* If no files specified, input shall be stdin - -* checksum values different from C++. Using wrong polynomial, vs POSIX std? - diff --git a/xform/src/cksum.rs b/xform/src/cksum.rs index ceb9663..f0a5b9e 100644 --- a/xform/src/cksum.rs +++ b/xform/src/cksum.rs @@ -6,6 +6,8 @@ // file in the root directory of this project. // SPDX-License-Identifier: MIT // +// FIXME !!! - checksum values do not match other cksum utils +// extern crate clap; extern crate plib; @@ -17,6 +19,7 @@ use plib::PROJECT_NAME; use std::fs; use std::io::{self, Read}; +/// cksum - write file checksums and sizes #[derive(Parser, Debug)] #[command(author, version, about, long_about)] struct Args {