/* * Copyright (c) 2024 DTB * SPDX-License-Identifier: AGPL-3.0-or-later * * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * This program 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 Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see https://www.gnu.org/licenses/. */ use std::{ env::args, io::{ stdin, stdout, Error, ErrorKind, Read, Write }, process::ExitCode, vec::Vec }; extern crate getopt; use getopt::{ Opt, Parser }; extern crate sysexits; use sysexits::{ EX_OK, EX_OSERR, EX_USAGE }; fn oserr(s: &str, e: Error) -> ExitCode { eprintln!("{}: {}", s, e); ExitCode::from(EX_OSERR as u8) } fn usage(s: &str) -> ExitCode { eprintln!("Usage: {} (-f) (-w [wordsize])", s); ExitCode::from(EX_USAGE as u8) } fn main() -> ExitCode { let argv = args().collect::>(); let mut buf: Vec = Vec::new(); let mut input = stdin(); let mut output = stdout().lock(); let mut opts = Parser::new(&argv, "fw:"); let mut force = false; let mut wordsize: usize = 2; loop { match opts.next() { None => break, Some(opt) => match opt { Ok(Opt('f', None)) => force = true, Ok(Opt('w', Some(arg))) => { match arg.parse::() { Ok(w) if w % 2 == 0 => { wordsize = w; () }, _ => { return usage(&argv[0]); }, } }, _ => { return usage(&argv[0]); } } } } buf.resize(wordsize, 0); loop { match input.read(&mut buf) { Ok(0) => break ExitCode::from(EX_OK as u8), Ok(v) if v == wordsize => { let (left, right) = buf.split_at(v/2); if let Err(e) = output.write(&right) .and_then(|_| output.write(&left)) { break oserr(&argv[0], e) } }, Ok(v) => { if let Err(e) = output.write(&buf[..v]) { break oserr(&argv[0], e) } }, Err(e) if e.kind() == ErrorKind::Interrupted && force => continue, Err(e) => break oserr(&argv[0], e) } } }