mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 12:07:46 +00:00
basic version 1
This commit is contained in:
parent
6c897dc76c
commit
32259aadda
6 changed files with 286 additions and 0 deletions
1
Makefile
1
Makefile
|
@ -75,6 +75,7 @@ PROGS := \
|
|||
seq \
|
||||
shuf \
|
||||
sort \
|
||||
stdbuf \
|
||||
sum \
|
||||
sync \
|
||||
tac \
|
||||
|
|
7
src/stdbuf/libstdbuf.c
Normal file
7
src/stdbuf/libstdbuf.c
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "libstdbuf.h"
|
||||
|
||||
void __attribute ((constructor))
|
||||
stdbuf_init (void)
|
||||
{
|
||||
stdbuf();
|
||||
}
|
6
src/stdbuf/libstdbuf.h
Normal file
6
src/stdbuf/libstdbuf.h
Normal file
|
@ -0,0 +1,6 @@
|
|||
#ifndef UUTILS_LIBSTDBUF_H
|
||||
#define UUTILS_LIBSTDBUF_H
|
||||
|
||||
void stdbuf(void);
|
||||
|
||||
#endif
|
48
src/stdbuf/libstdbuf.rs
Normal file
48
src/stdbuf/libstdbuf.rs
Normal file
|
@ -0,0 +1,48 @@
|
|||
#![crate_type = "dylib"]
|
||||
|
||||
extern crate libc;
|
||||
use libc::{c_int, size_t, c_char, FILE, _IOFBF, _IONBF, _IOLBF, setvbuf};
|
||||
use std::ptr;
|
||||
use std::os;
|
||||
|
||||
extern {
|
||||
static stdin: *mut FILE;
|
||||
static stdout: *mut FILE;
|
||||
static stderr: *mut FILE;
|
||||
}
|
||||
|
||||
fn set_buffer(stream: *mut FILE, value: &str) {
|
||||
let (mode, size): (c_int, size_t) = match value {
|
||||
"0" => (_IONBF, 0 as size_t),
|
||||
"L" => (_IOLBF, 0 as size_t),
|
||||
input => {
|
||||
let buff_size: uint = match from_str(input) {
|
||||
Some(num) => num,
|
||||
None => panic!("incorrect size of buffer!")
|
||||
};
|
||||
(_IOFBF, buff_size as size_t)
|
||||
}
|
||||
};
|
||||
let mut res: c_int;
|
||||
unsafe {
|
||||
let buffer: *mut c_char = ptr::null_mut();
|
||||
assert!(buffer.is_null());
|
||||
res = libc::setvbuf(stream, buffer, mode, size);
|
||||
}
|
||||
if res != 0 {
|
||||
panic!("error while calling setvbuf!");
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn stdbuf() {
|
||||
if let Some(val) = os::getenv("_STDBUF_E") {
|
||||
set_buffer(stderr, val.as_slice());
|
||||
}
|
||||
if let Some(val) = os::getenv("_STDBUF_I") {
|
||||
set_buffer(stdin, val.as_slice());
|
||||
}
|
||||
if let Some(val) = os::getenv("_STDBUF_O") {
|
||||
set_buffer(stdout, val.as_slice());
|
||||
}
|
||||
}
|
9
src/stdbuf/prepare_libs.sh
Executable file
9
src/stdbuf/prepare_libs.sh
Executable file
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
cd $(dirname $0)
|
||||
rustc libstdbuf.rs
|
||||
gcc -c -Wall -Werror -fpic libstdbuf.c -L. -llibstdbuf.so
|
||||
gcc -shared -o libstdbuf.so libstdbuf.o
|
||||
mv *.so ../../build/
|
||||
rm *.o
|
||||
export LD_LIBRARY_PATH="$LD_LIBRARY_PATH":"$PWD"/../../build
|
215
src/stdbuf/stdbuf.rs
Normal file
215
src/stdbuf/stdbuf.rs
Normal file
|
@ -0,0 +1,215 @@
|
|||
#![crate_name = "stdbuf"]
|
||||
|
||||
/*
|
||||
* This file is part of the uutils coreutils package.
|
||||
*
|
||||
* (c) Dorota Kapturkiewicz <dokaptur@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
#![feature(macro_rules)]
|
||||
|
||||
extern crate getopts;
|
||||
extern crate libc;
|
||||
use getopts::{optopt, optflag, getopts, usage, Matches, OptGroup};
|
||||
use std::io::process::{Command, StdioContainer};
|
||||
use std::iter::range_inclusive;
|
||||
use std::num::Int;
|
||||
|
||||
#[path = "../common/util.rs"]
|
||||
mod util;
|
||||
|
||||
static NAME: &'static str = "stdbuf";
|
||||
static VERSION: &'static str = "1.0.0";
|
||||
static LIBSTDBUF_PATH: &'static str = "liblibstdbuf.so libstdbuf.so";
|
||||
|
||||
#[deriving(Show)]
|
||||
enum BufferType {
|
||||
Default,
|
||||
Line,
|
||||
Size(u64)
|
||||
}
|
||||
|
||||
#[deriving(Show)]
|
||||
struct ProgramOptions {
|
||||
stdin: BufferType,
|
||||
stdout: BufferType,
|
||||
stderr: BufferType,
|
||||
}
|
||||
|
||||
enum ErrMsg {
|
||||
Retry,
|
||||
Fatal
|
||||
}
|
||||
|
||||
enum OkMsg {
|
||||
Buffering,
|
||||
Help,
|
||||
Version
|
||||
}
|
||||
|
||||
fn print_version() {
|
||||
println!("{} version {}", NAME, VERSION);
|
||||
}
|
||||
|
||||
fn print_usage(opts: &[OptGroup]) {
|
||||
let brief =
|
||||
"Usage: stdbuf OPTION... COMMAND\n \
|
||||
Run COMMAND, with modified buffering operations for its standard streams\n \
|
||||
Mandatory arguments to long options are mandatory for short options too.";
|
||||
let explanation =
|
||||
"If MODE is 'L' the corresponding stream will be line buffered.\n \
|
||||
This option is invalid with standard input.\n\n \
|
||||
If MODE is '0' the corresponding stream will be unbuffered.\n\n \
|
||||
Otherwise MODE is a number which may be followed by one of the following:\n\n \
|
||||
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.\n \
|
||||
In this case the corresponding stream will be fully buffered with the buffer size set to MODE bytes.\n\n \
|
||||
NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for e.g.) then that will override \
|
||||
corresponding settings changed by 'stdbuf'.\n \
|
||||
Also some filters (like 'dd' and 'cat' etc.) don't use streams for I/O, \
|
||||
and are thus unaffected by 'stdbuf' settings.\n";
|
||||
println!("{}\n{}", getopts::usage(brief, opts), explanation);
|
||||
}
|
||||
|
||||
fn parse_size(size: &str) -> Option<u64> {
|
||||
let ext = size.trim_left_chars(|c: char| c.is_digit(10));
|
||||
let num = size.trim_right_chars(|c: char| c.is_alphabetic());
|
||||
let mut recovered = num.to_string();
|
||||
recovered.push_str(ext);
|
||||
if recovered.as_slice() != size {
|
||||
return None;
|
||||
}
|
||||
let buf_size: u64 = match from_str(num) {
|
||||
Some(m) => m,
|
||||
None => return None,
|
||||
};
|
||||
let (power, base): (uint, u64) = match ext {
|
||||
"" => (0, 0),
|
||||
"KB" => (1, 1024),
|
||||
"K" => (1, 1000),
|
||||
"MB" => (2, 1024),
|
||||
"M" => (2, 1000),
|
||||
"GB" => (3, 1024),
|
||||
"G" => (3, 1000),
|
||||
"TB" => (4, 1024),
|
||||
"T" => (4, 1000),
|
||||
"PB" => (5, 1024),
|
||||
"P" => (5, 1000),
|
||||
"EB" => (6, 1024),
|
||||
"E" => (6, 1000),
|
||||
"ZB" => (7, 1024),
|
||||
"Z" => (7, 1000),
|
||||
"YB" => (8, 1024),
|
||||
"Y" => (8, 1000),
|
||||
_ => return None,
|
||||
};
|
||||
Some(buf_size * base.pow(power))
|
||||
}
|
||||
|
||||
fn check_option(matches: &Matches, name: &str, modified: &mut bool) -> Option<BufferType> {
|
||||
match matches.opt_str(name) {
|
||||
Some(value) => {
|
||||
*modified = true;
|
||||
match value.as_slice() {
|
||||
"L" => {
|
||||
if name == "input" {
|
||||
show_info!("stdbuf: line buffering stdin is meaningless");
|
||||
None
|
||||
} else {
|
||||
Some(BufferType::Line)
|
||||
}
|
||||
},
|
||||
x => {
|
||||
let size = match parse_size(x) {
|
||||
Some(m) => m,
|
||||
None => { show_error!("Invalid mode {}", x); return None }
|
||||
};
|
||||
Some(BufferType::Size(size))
|
||||
},
|
||||
}
|
||||
},
|
||||
None => Some(BufferType::Default),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_options(args: &[String], options: &mut ProgramOptions, optgrps: &[OptGroup]) -> Result<OkMsg, ErrMsg> {
|
||||
let matches = match getopts(args, optgrps) {
|
||||
Ok(m) => m,
|
||||
Err(_) => return Err(ErrMsg::Retry)
|
||||
};
|
||||
if matches.opt_present("help") {
|
||||
return Ok(OkMsg::Help);
|
||||
}
|
||||
if matches.opt_present("version") {
|
||||
return Ok(OkMsg::Version);
|
||||
}
|
||||
let mut modified = false;
|
||||
options.stdin = try!(check_option(&matches, "input", &mut modified).ok_or(ErrMsg::Fatal));
|
||||
options.stdout = try!(check_option(&matches, "output", &mut modified).ok_or(ErrMsg::Fatal));
|
||||
options.stderr = try!(check_option(&matches, "error", &mut modified).ok_or(ErrMsg::Fatal));
|
||||
|
||||
if matches.free.len() != 1 {
|
||||
return Err(ErrMsg::Retry);
|
||||
}
|
||||
if !modified {
|
||||
show_error!("stdbuf: you must specify a buffering mode option");
|
||||
return Err(ErrMsg::Fatal);
|
||||
}
|
||||
Ok(OkMsg::Buffering)
|
||||
}
|
||||
|
||||
fn set_command_env(command: &mut Command, buffer_name: &str, buffer_type: BufferType) {
|
||||
match buffer_type {
|
||||
BufferType::Size(m) => { command.env(buffer_name, m.to_string()); },
|
||||
BufferType::Line => { command.env(buffer_name, "L"); },
|
||||
BufferType::Default => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn uumain(args: Vec<String>) -> int {
|
||||
let optgrps = [
|
||||
optopt("i", "input", "adjust standard input stream buffering", "MODE"),
|
||||
optopt("o", "output", "adjust standard output stream buffering", "MODE"),
|
||||
optopt("e", "error", "adjust standard error stream buffering", "MODE"),
|
||||
optflag("", "help", "display this help and exit"),
|
||||
optflag("", "version", "output version information and exit"),
|
||||
];
|
||||
let mut options = ProgramOptions {stdin: BufferType::Default, stdout: BufferType::Default, stderr: BufferType::Default};
|
||||
let mut command_idx = -1;
|
||||
for i in range_inclusive(1, args.len()) {
|
||||
match parse_options(args.slice(1, i), &mut options, &optgrps) {
|
||||
Ok(OkMsg::Buffering) => {
|
||||
command_idx = i-1;
|
||||
break;
|
||||
},
|
||||
Ok(OkMsg::Help) => {
|
||||
print_usage(&optgrps);
|
||||
return 0;
|
||||
},
|
||||
Ok(OkMsg::Version) => {
|
||||
print_version();
|
||||
return 0;
|
||||
},
|
||||
Err(ErrMsg::Fatal) => break,
|
||||
Err(ErrMsg::Retry) => continue,
|
||||
}
|
||||
};
|
||||
if command_idx == -1 {
|
||||
crash!(125, "Invalid options\nTry 'stdbuf --help' for more information.");
|
||||
}
|
||||
let ref command_name = args[command_idx];
|
||||
let mut command = Command::new(command_name);
|
||||
command.args(args.slice_from(command_idx+1)).env("LD_PRELOAD", LIBSTDBUF_PATH);
|
||||
command.stdin(StdioContainer::InheritFd(0)).stdout(StdioContainer::InheritFd(1)).stderr(StdioContainer::InheritFd(2));
|
||||
set_command_env(&mut command, "_STDBUF_I", options.stdin);
|
||||
set_command_env(&mut command, "_STDBUF_O", options.stdout);
|
||||
set_command_env(&mut command, "_STDBUF_E", options.stderr);
|
||||
match command.spawn() {
|
||||
Ok(_) => {},
|
||||
Err(e) => crash!(1, "failed to execute process: {}", e),
|
||||
};
|
||||
0
|
||||
}
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue