This commit updates `cut` to build on rust nightly.

In addition, it adds support for null input and output delimiters,
and fixes a bug in the `cut_characters()` function that would cause
incorrect output when two adjacent fields were specified in the range
list.
This commit is contained in:
kwantam
2015-05-11 21:15:39 -04:00
parent c94a5ce808
commit aabbf83d88
5 changed files with 356 additions and 285 deletions
+17 -1
View File
@@ -62,7 +62,23 @@ macro_rules! crash_if_err(
($exitcode:expr, $exp:expr) => (
match $exp {
Ok(m) => m,
Err(f) => crash!($exitcode, "{}", f.to_string())
Err(f) => crash!($exitcode, "{}", f),
}
)
);
#[macro_export]
macro_rules! pipe_crash_if_err(
($exitcode:expr, $exp:expr) => (
match $exp {
Ok(_) => (),
Err(f) => {
if f.kind() == ::std::io::ErrorKind::BrokenPipe {
()
} else {
crash!($exitcode, "{}", f)
}
},
}
)
);
+114 -105
View File
@@ -1,142 +1,151 @@
use std;
use std::old_io::{IoResult, IoError};
/*
* This file is part of the uutils coreutils package.
*
* (c) Rolf Morel <rolfmorel@gmail.com>
* (c) kwantam <kwantam@gmail.com>
* substantially rewritten to use the stdlib BufReader trait
* rather than re-implementing it here.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
pub struct BufReader<R> {
reader: R,
buffer: [u8; 4096],
start: usize,
end: usize, // exclusive
}
use std::io::{BufRead, BufReader, Read, Write};
use std::io::Result as IoResult;
#[allow(non_snake_case)]
pub mod Bytes {
use std::io::Write;
pub trait Select {
fn select<'a>(&'a mut self, bytes: usize) -> Selected<'a>;
fn select<W: Write>(&mut self, bytes: usize, out: Option<&mut W>) -> Selected;
}
pub enum Selected<'a> {
NewlineFound(&'a [u8]),
Complete(&'a [u8]),
Partial(&'a [u8]),
#[derive(PartialEq, Eq, Debug)]
pub enum Selected {
NewlineFound,
Complete(usize),
Partial(usize),
EndOfFile,
}
}
impl<R: Reader> BufReader<R> {
pub fn new(reader: R) -> BufReader<R> {
let empty_buffer = unsafe {
std::mem::uninitialized::<[u8; 4096]>()
};
#[derive(Debug)]
pub struct ByteReader<R> where R: Read {
inner: BufReader<R>,
}
BufReader {
reader: reader,
buffer: empty_buffer,
start: 0,
end: 0,
impl<R: Read> ByteReader<R> {
pub fn new(read: R) -> ByteReader<R> {
ByteReader {
inner: BufReader::with_capacity(4096, read),
}
}
}
#[inline]
fn read(&mut self) -> IoResult<usize> {
let buffer_fill = &mut self.buffer[self.end..];
impl<R: Read> Read for ByteReader<R> {
fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
self.inner.read(buf)
}
}
match self.reader.read(buffer_fill) {
Ok(nread) => {
self.end += nread;
Ok(nread)
}
error => error
}
impl<R: Read> BufRead for ByteReader<R> {
fn fill_buf(&mut self) -> IoResult<&[u8]> {
self.inner.fill_buf()
}
#[inline]
fn maybe_fill_buf(&mut self) -> IoResult<usize> {
if self.end == self.start {
self.start = 0;
self.end = 0;
self.read()
} else {
Ok(0)
}
fn consume(&mut self, amt: usize) {
self.inner.consume(amt)
}
}
impl<R: Read> ByteReader<R> {
pub fn consume_line(&mut self) -> usize {
let mut bytes_consumed = 0;
let mut consume_val;
loop {
match self.maybe_fill_buf() {
Ok(0) | Err(IoError { kind: std::old_io::EndOfFile, .. })
if self.start == self.end => return bytes_consumed,
Err(err) => panic!("read error: {}", err.desc),
_ => ()
}
{ // need filled_buf to go out of scope
let filled_buf = match self.fill_buf() {
Ok(b) => {
if b.len() == 0 {
return bytes_consumed
} else {
b
}
},
Err(e) => crash!(1, "read error: {}", e),
};
let filled_buf = &self.buffer[self.start..self.end];
match filled_buf.position_elem(&b'\n') {
Some(idx) => {
self.start += idx + 1;
return bytes_consumed + idx + 1;
match filled_buf.position_elem(&b'\n') {
Some(idx) => {
consume_val = idx + 1;
bytes_consumed += consume_val;
break;
}
_ => ()
}
_ => ()
consume_val = filled_buf.len();
}
bytes_consumed += filled_buf.len();
self.start = 0;
self.end = 0;
bytes_consumed += consume_val;
self.consume(consume_val);
}
self.consume(consume_val);
return bytes_consumed;
}
}
impl<R: Reader> Bytes::Select for BufReader<R> {
fn select<'a>(&'a mut self, bytes: usize) -> Bytes::Selected<'a> {
match self.maybe_fill_buf() {
Err(IoError { kind: std::old_io::EndOfFile, .. }) => (),
Err(err) => panic!("read error: {}", err.desc),
_ => ()
}
let newline_idx = match self.end - self.start {
0 => return Bytes::Selected::EndOfFile,
buf_used if bytes < buf_used => {
// because the output delimiter should only be placed between
// segments check if the byte after bytes is a newline
let buf_slice = &self.buffer[self.start..self.start + bytes + 1];
match buf_slice.position_elem(&b'\n') {
Some(idx) => idx,
None => {
let segment = &self.buffer[self.start..self.start + bytes];
self.start += bytes;
return Bytes::Selected::Complete(segment);
}
}
}
_ => {
let buf_filled = &self.buffer[self.start..self.end];
match buf_filled.position_elem(&b'\n') {
Some(idx) => idx,
None => {
let segment = &self.buffer[self.start..self.end];
self.start = 0;
self.end = 0;
return Bytes::Selected::Partial(segment);
}
}
}
impl<R: Read> self::Bytes::Select for ByteReader<R> {
fn select<W: Write>(&mut self, bytes: usize, out: Option<&mut W>) -> Bytes::Selected {
enum SRes {
Comp,
Part,
Newl,
};
let new_start = self.start + newline_idx + 1;
let segment = &self.buffer[self.start..new_start];
use self::Bytes::Selected::*;
self.start = new_start;
Bytes::Selected::NewlineFound(segment)
let (res, consume_val) = {
let buffer = match self.fill_buf() {
Err(e) => crash!(1, "read error: {}", e),
Ok(b) => b,
};
let (res, consume_val) = match buffer.len() {
0 => return EndOfFile,
buf_used if bytes < buf_used => {
// because the output delimiter should only be placed between
// segments check if the byte after bytes is a newline
let buf_slice = &buffer[0..bytes + 1];
match buf_slice.position_elem(&b'\n') {
Some(idx) => (SRes::Newl, idx+1),
None => (SRes::Comp, bytes),
}
},
_ => {
match buffer.position_elem(&b'\n') {
Some(idx) => (SRes::Newl, idx+1),
None => (SRes::Part, buffer.len()),
}
},
};
match out {
Some(out) => pipe_crash_if_err!(1, out.write_all(&buffer[0..consume_val])),
None => (),
}
(res, consume_val)
};
self.consume(consume_val);
match res {
SRes::Comp => Complete(consume_val),
SRes::Part => Partial(consume_val),
SRes::Newl => NewlineFound,
}
}
}
+146 -164
View File
File diff suppressed because it is too large Load Diff
+25 -15
View File
@@ -7,7 +7,7 @@
* file that was distributed with this source code.
*/
use std;
use std::str::FromStr;
#[derive(PartialEq,Eq,PartialOrd,Ord,Debug)]
pub struct Range {
@@ -15,42 +15,52 @@ pub struct Range {
pub high: usize,
}
impl std::str::FromStr for Range {
impl FromStr for Range {
type Err = &'static str;
fn from_str(s: &str) -> Result<Range, &'static str> {
use std::usize::MAX;
let mut parts = s.splitn(1, '-');
let mut parts = s.splitn(2, '-');
let field = "fields and positions are numbered from 1";
let order = "high end of range less than low end";
let inval = "failed to parse range";
match (parts.next(), parts.next()) {
(Some(nm), None) => {
if let Ok(nm) = nm.parse::<usize>() {
if nm > 0 { Ok(Range{ low: nm, high: nm}) } else { Err("invalid range") }
if nm > 0 { Ok(Range{ low: nm, high: nm}) } else { Err(field) }
} else {
Err("invalid range")
Err(inval)
}
}
(Some(n), Some(m)) if m.len() == 0 => {
if let Ok(low) = n.parse::<usize>() {
if low > 0 { Ok(Range{ low: low, high: MAX}) } else { Err("invalid range") }
if low > 0 { Ok(Range{ low: low, high: MAX}) } else { Err(field) }
} else {
Err("invalid range")
Err(inval)
}
}
(Some(n), Some(m)) if n.len() == 0 => {
if let Ok(high) = m.parse::<usize>() {
if high > 0 { Ok(Range{ low: 1, high: high}) } else { Err("invalid range") }
if high > 0 { Ok(Range{ low: 1, high: high}) } else { Err(field) }
} else {
Err("invalid range")
Err(inval)
}
}
(Some(n), Some(m)) => {
match (n.parse::<usize>(), m.parse::<usize>()) {
(Ok(low), Ok(high)) if low > 0 && low <= high => {
Ok(Range { low: low, high: high })
}
_ => Err("invalid range")
(Ok(low), Ok(high)) => {
if low > 0 && low <= high {
Ok(Range { low: low, high: high })
} else if low == 0 {
Err(field)
} else {
Err(order)
}
},
_ => Err(inval),
}
}
_ => unreachable!()
@@ -65,7 +75,7 @@ impl Range {
let mut ranges : Vec<Range> = vec!();
for item in list.split(',') {
match std::str::FromStr::from_str(item) {
match FromStr::from_str(item) {
Ok(range_item) => ranges.push(range_item),
Err(e)=> return Err(format!("range '{}' was invalid: {}", item, e))
}
@@ -74,7 +84,7 @@ impl Range {
ranges.sort();
// merge overlapping ranges
for i in range(0, ranges.len()) {
for i in 0..ranges.len() {
let j = i + 1;
while j < ranges.len() && ranges[j].low <= ranges[i].high {
+54
View File
@@ -0,0 +1,54 @@
/*
* This file is part of the uutils coreutils package.
*
* (c) Rolf Morel <rolfmorel@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
#[derive(Clone)]
pub struct Searcher<'a> {
haystack: &'a [u8],
needle: &'a [u8],
position: usize
}
impl<'a> Searcher<'a> {
pub fn new(haystack: &'a [u8], needle: &'a [u8]) -> Searcher<'a> {
Searcher {
haystack: haystack,
needle: needle,
position: 0
}
}
}
impl<'a> Iterator for Searcher<'a> {
type Item = (usize, usize);
fn next(&mut self) -> Option<(usize, usize)> {
if self.needle.len() == 1 {
for offset in self.position..self.haystack.len() {
if self.haystack[offset] == self.needle[0] {
self.position = offset + 1;
return Some((offset, offset + 1));
}
}
self.position = self.haystack.len();
return None;
}
while self.position + self.needle.len() <= self.haystack.len() {
if &self.haystack[self.position..self.position + self.needle.len()] == self.needle {
let match_pos = self.position;
self.position += self.needle.len();
return Some((match_pos, match_pos + self.needle.len()));
} else {
self.position += 1;
}
}
None
}
}