138 lines
3.6 KiB
Rust
138 lines
3.6 KiB
Rust
|
/*
|
||
|
* Copyright (c) 2024 Emma Tebibyte <emma@tebibyte.media>
|
||
|
* 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,
|
||
|
fs::File,
|
||
|
io::{ stdin, stdout, stderr, BufWriter, Read, Write },
|
||
|
os::fd::{ AsRawFd, FromRawFd },
|
||
|
process::{ exit, ExitCode },
|
||
|
};
|
||
|
|
||
|
extern crate getopt;
|
||
|
extern crate strerror;
|
||
|
extern crate sysexits;
|
||
|
|
||
|
use getopt::GetOpt;
|
||
|
use strerror::StrError;
|
||
|
use sysexits::{ EX_IOERR, EX_USAGE };
|
||
|
|
||
|
fn main() -> ExitCode {
|
||
|
let argv = args().collect::<Vec<_>>();
|
||
|
let usage = format!("Usage: {} [-aeu] [-i input] [-o output]", argv[0]);
|
||
|
|
||
|
let mut a = false; /* append rather than update */
|
||
|
let mut e = false; /* use stderr as an output */
|
||
|
let mut u = false; /* unbuffer i/o */
|
||
|
let mut ins = Vec::new(); /* initial inputs vector */
|
||
|
let mut outs = Vec::new(); /* initial outputs vector */
|
||
|
|
||
|
while let Some(opt) = argv.getopt("aei:o:u") {
|
||
|
match opt.opt() {
|
||
|
Ok("a") => a = true,
|
||
|
Ok("e") => e = true,
|
||
|
Ok("u") => u = true,
|
||
|
Ok("i") => { /* add input */
|
||
|
let input = opt.arg().unwrap();
|
||
|
ins.push(input);
|
||
|
},
|
||
|
Ok("o") => { /* add output */
|
||
|
let output = opt.arg().unwrap();
|
||
|
outs.push(output);
|
||
|
},
|
||
|
Err(_) | Ok(_) => {
|
||
|
eprintln!("{}", usage);
|
||
|
return ExitCode::from(EX_USAGE as u8);
|
||
|
},
|
||
|
};
|
||
|
}
|
||
|
|
||
|
/* use stdin if no inputs are specified */
|
||
|
if ins.is_empty() { ins.push("-".to_string()); }
|
||
|
|
||
|
/* use stdout if no outputs are specified */
|
||
|
if outs.is_empty() { outs.push("-".to_string()); }
|
||
|
|
||
|
/* map all path strings to files */
|
||
|
let inputs = ins.iter().map(|file| {
|
||
|
/* if a file is “-”, it is stdin */
|
||
|
if *file == "-" {
|
||
|
return unsafe { File::from_raw_fd(stdin().as_raw_fd()) }; /* fd0 = stdin */
|
||
|
}
|
||
|
|
||
|
match File::options().append(a).open(file) {
|
||
|
Ok(f) => f,
|
||
|
Err(e) => {
|
||
|
eprintln!("{}: {}", argv[0], e.strerror());
|
||
|
exit(EX_IOERR);
|
||
|
},
|
||
|
}
|
||
|
}).collect::<Vec<_>>();
|
||
|
|
||
|
/* map all path strings to files */
|
||
|
let mut outputs = outs.iter().map(|file| {
|
||
|
/* of a file is “-”, it is stdout */
|
||
|
if *file == "-" {
|
||
|
return unsafe { File::from_raw_fd(stdout().as_raw_fd()) }; /* fd1 = stdout */
|
||
|
}
|
||
|
|
||
|
match File::options().append(a).open(file) {
|
||
|
Ok(f) => return f,
|
||
|
Err(e) => {
|
||
|
eprintln!("{}: {}", argv[0], e.strerror());
|
||
|
exit(EX_IOERR);
|
||
|
},
|
||
|
};
|
||
|
}).collect::<Vec<_>>();
|
||
|
|
||
|
/* if -e is specified, use stderr */
|
||
|
if e {
|
||
|
outputs.push(unsafe { File::from_raw_fd(stderr().as_raw_fd()) }); /* fd2 = stderr */
|
||
|
}
|
||
|
|
||
|
let mut outputs = outputs.iter().map(|o| {
|
||
|
if u {
|
||
|
BufWriter::with_capacity(0, o)
|
||
|
} else {
|
||
|
BufWriter::new(o)
|
||
|
}
|
||
|
}).collect::<Vec<_>>();
|
||
|
|
||
|
for file in inputs {
|
||
|
for byte in file.bytes().map(|b| {
|
||
|
b.unwrap_or_else(|e| {
|
||
|
eprintln!("{}: {}", argv[0], e.strerror());
|
||
|
exit(EX_IOERR);
|
||
|
})
|
||
|
}) {
|
||
|
for out in &mut outputs {
|
||
|
if let Err(e) = out.write(&[byte]) {
|
||
|
eprintln!("{}: {}", argv[0], e.strerror());
|
||
|
return ExitCode::from(EX_IOERR as u8);
|
||
|
}
|
||
|
if let Err(e) = out.flush() {
|
||
|
eprintln!("{}: {}", argv[0], e.strerror());
|
||
|
return ExitCode::from(EX_IOERR as u8);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ExitCode::SUCCESS
|
||
|
}
|