1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-08-01 13:37:48 +00:00

Merge pull request #589 from jbcrail/fix-nl

Fix nl.
This commit is contained in:
Heather 2015-05-11 08:08:50 +03:00
commit c94a5ce808
3 changed files with 71 additions and 66 deletions

View file

@ -8,8 +8,8 @@ fn parse_style(chars: &[char]) -> Result<::NumberingStyle, String> {
['t'] => { Ok(::NumberingStyle::NumberForNonEmpty) }, ['t'] => { Ok(::NumberingStyle::NumberForNonEmpty) },
['n'] => { Ok(::NumberingStyle::NumberForNone) }, ['n'] => { Ok(::NumberingStyle::NumberForNone) },
['p', rest..] => { ['p', rest..] => {
let s : String = rest.iter().map(|c| *c).collect(); let s: String = rest.iter().map(|c| *c).collect();
match regex::Regex::new(s.as_slice()) { match regex::Regex::new(&s) {
Ok(re) => Ok(::NumberingStyle::NumberForRegularExpression(re)), Ok(re) => Ok(::NumberingStyle::NumberForRegularExpression(re)),
Err(_) => Err(String::from_str("Illegal regular expression")), Err(_) => Err(String::from_str("Illegal regular expression")),
} }
@ -32,7 +32,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
} }
match opts.opt_str("n") { match opts.opt_str("n") {
None => {}, None => {},
Some(val) => match val.as_slice() { Some(val) => match val.as_ref() {
"ln" => { settings.number_format = ::NumberFormat::Left; }, "ln" => { settings.number_format = ::NumberFormat::Left; },
"rn" => { settings.number_format = ::NumberFormat::Right; }, "rn" => { settings.number_format = ::NumberFormat::Right; },
"rz" => { settings.number_format = ::NumberFormat::RightZero; }, "rz" => { settings.number_format = ::NumberFormat::RightZero; },
@ -42,8 +42,8 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
match opts.opt_str("b") { match opts.opt_str("b") {
None => {}, None => {},
Some(val) => { Some(val) => {
let chars: Vec<char> = val.as_slice().chars().collect(); let chars: Vec<char> = val.chars().collect();
match parse_style(chars.as_slice()) { match parse_style(&chars) {
Ok(s) => { settings.body_numbering = s; } Ok(s) => { settings.body_numbering = s; }
Err(message) => { errs.push(message); } Err(message) => { errs.push(message); }
} }
@ -52,8 +52,8 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
match opts.opt_str("f") { match opts.opt_str("f") {
None => {}, None => {},
Some(val) => { Some(val) => {
let chars: Vec<char> = val.as_slice().chars().collect(); let chars: Vec<char> = val.chars().collect();
match parse_style(chars.as_slice()) { match parse_style(&chars) {
Ok(s) => { settings.footer_numbering = s; } Ok(s) => { settings.footer_numbering = s; }
Err(message) => { errs.push(message); } Err(message) => { errs.push(message); }
} }
@ -62,8 +62,8 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
match opts.opt_str("h") { match opts.opt_str("h") {
None => {}, None => {},
Some(val) => { Some(val) => {
let chars: Vec<char> = val.as_slice().chars().collect(); let chars: Vec<char> = val.chars().collect();
match parse_style(chars.as_slice()) { match parse_style(&chars) {
Ok(s) => { settings.header_numbering = s; } Ok(s) => { settings.header_numbering = s; }
Err(message) => { errs.push(message); } Err(message) => { errs.push(message); }
} }
@ -72,7 +72,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
match opts.opt_str("i") { match opts.opt_str("i") {
None => {} None => {}
Some(val) => { Some(val) => {
let conv: Option<u64> = val.as_slice().parse().ok(); let conv: Option<u64> = val.parse().ok();
match conv { match conv {
None => { None => {
errs.push(String::from_str("Illegal value for -i")); errs.push(String::from_str("Illegal value for -i"));
@ -84,7 +84,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
match opts.opt_str("w") { match opts.opt_str("w") {
None => {} None => {}
Some(val) => { Some(val) => {
let conv: Option<usize> = val.as_slice().parse().ok(); let conv: Option<usize> = val.parse().ok();
match conv { match conv {
None => { None => {
errs.push(String::from_str("Illegal value for -w")); errs.push(String::from_str("Illegal value for -w"));
@ -96,7 +96,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
match opts.opt_str("v") { match opts.opt_str("v") {
None => {} None => {}
Some(val) => { Some(val) => {
let conv: Option<u64> = val.as_slice().parse().ok(); let conv: Option<u64> = val.parse().ok();
match conv { match conv {
None => { None => {
errs.push(String::from_str("Illegal value for -v")); errs.push(String::from_str("Illegal value for -v"));
@ -108,7 +108,7 @@ pub fn parse_options(settings: &mut ::Settings, opts: &getopts::Matches) -> Vec<
match opts.opt_str("l") { match opts.opt_str("l") {
None => {} None => {}
Some(val) => { Some(val) => {
let conv: Option<u64> = val.as_slice().parse().ok(); let conv: Option<u64> = val.parse().ok();
match conv { match conv {
None => { None => {
errs.push(String::from_str("Illegal value for -l")); errs.push(String::from_str("Illegal value for -l"));

View file

@ -1,5 +1,5 @@
#![crate_name = "nl"] #![crate_name = "nl"]
#![feature(collections, core, old_io, old_path, rustc_private)] #![feature(collections, rustc_private, slice_patterns)]
#![plugin(regex_macros)] #![plugin(regex_macros)]
/* /*
@ -16,13 +16,11 @@
extern crate regex; extern crate regex;
extern crate getopts; extern crate getopts;
use std::old_io::{stdin}; use std::fs::File;
use std::old_io::BufferedReader; use std::io::{BufRead, BufReader, Read, stdin, Write};
use std::old_io::fs::File;
use std::iter::repeat; use std::iter::repeat;
use std::num::Int; use std::path::Path;
use std::old_path::Path; use getopts::{getopts, optflag, OptGroup, optopt, usage};
use getopts::{optopt, optflag, getopts, usage, OptGroup};
#[path="../common/util.rs"] #[path="../common/util.rs"]
#[macro_use] #[macro_use]
@ -35,7 +33,7 @@ static USAGE: &'static str = "nl [OPTION]... [FILE]...";
static REGEX_DUMMY: &'static regex::Regex = &regex!(r".?"); static REGEX_DUMMY: &'static regex::Regex = &regex!(r".?");
// Settings store options used by nl to produce its output. // Settings store options used by nl to produce its output.
struct Settings { pub struct Settings {
// The variables corresponding to the options -h, -b, and -f. // The variables corresponding to the options -h, -b, and -f.
header_numbering: NumberingStyle, header_numbering: NumberingStyle,
body_numbering: NumberingStyle, body_numbering: NumberingStyle,
@ -109,7 +107,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
number_separator: String::from_str("\t"), number_separator: String::from_str("\t"),
}; };
let given_options = match getopts(args.tail(), &possible_options) { let given_options = match getopts(&args[1..], &possible_options) {
Ok (m) => { m } Ok (m) => { m }
Err(f) => { Err(f) => {
show_error!("{}", f); show_error!("{}", f);
@ -130,7 +128,7 @@ pub fn uumain(args: Vec<String>) -> i32 {
if parse_errors.len() > 0 { if parse_errors.len() > 0 {
show_error!("Invalid arguments supplied."); show_error!("Invalid arguments supplied.");
for message in parse_errors.iter() { for message in parse_errors.iter() {
println!("{}", message.as_slice()); println!("{}", message);
} }
return 1; return 1;
} }
@ -139,27 +137,27 @@ pub fn uumain(args: Vec<String>) -> i32 {
let mut read_stdin = files.is_empty(); let mut read_stdin = files.is_empty();
for file in files.iter() { for file in files.iter() {
if file.as_slice() == "-" { if file == "-" {
// If both file names and '-' are specified, we choose to treat first all // If both file names and '-' are specified, we choose to treat first all
// regular files, and then read from stdin last. // regular files, and then read from stdin last.
read_stdin = true; read_stdin = true;
continue continue
} }
let path = Path::new(file.as_slice()); let path = Path::new(file);
let reader = File::open(&path).unwrap(); let reader = File::open(path).unwrap();
let mut buffer = BufferedReader::new(reader); let mut buffer = BufReader::new(reader);
nl(&mut buffer, &settings); nl(&mut buffer, &settings);
} }
if read_stdin { if read_stdin {
let mut buffer = BufferedReader::new(stdin()); let mut buffer = BufReader::new(stdin());
nl(&mut buffer, &settings); nl(&mut buffer, &settings);
} }
0 0
} }
// nl implements the main functionality for an individual buffer. // nl implements the main functionality for an individual buffer.
fn nl<T: Reader> (reader: &mut BufferedReader<T>, settings: &Settings) { fn nl<T: Read> (reader: &mut BufReader<T>, settings: &Settings) {
let mut line_no = settings.starting_line_number; let mut line_no = settings.starting_line_number;
// The current line number's width as a string. Using to_string is inefficient // The current line number's width as a string. Using to_string is inefficient
// but since we only do it once, it should not hurt. // but since we only do it once, it should not hurt.
@ -167,7 +165,7 @@ fn nl<T: Reader> (reader: &mut BufferedReader<T>, settings: &Settings) {
let line_no_width_initial = line_no_width; let line_no_width_initial = line_no_width;
// Stores the smallest integer with one more digit than line_no, so that // Stores the smallest integer with one more digit than line_no, so that
// when line_no >= line_no_threshold, we need to use one more digit. // when line_no >= line_no_threshold, we need to use one more digit.
let mut line_no_threshold = Int::pow(10u64, line_no_width as u32); let mut line_no_threshold = 10u64.pow(line_no_width as u32);
let mut empty_line_count: u64 = 0; let mut empty_line_count: u64 = 0;
let fill_char = match settings.number_format { let fill_char = match settings.number_format {
NumberFormat::RightZero => '0', NumberFormat::RightZero => '0',
@ -181,13 +179,13 @@ fn nl<T: Reader> (reader: &mut BufferedReader<T>, settings: &Settings) {
let mut line_filter : fn(&str, &regex::Regex) -> bool = pass_regex; let mut line_filter : fn(&str, &regex::Regex) -> bool = pass_regex;
for mut l in reader.lines().map(|r| r.unwrap()) { for mut l in reader.lines().map(|r| r.unwrap()) {
// Sanitize the string. We want to print the newline ourselves. // Sanitize the string. We want to print the newline ourselves.
if l.as_slice().chars().rev().next().unwrap() == '\n' { if l.len() > 0 && l.chars().rev().next().unwrap() == '\n' {
l.pop(); l.pop();
} }
// Next we iterate through the individual chars to see if this // Next we iterate through the individual chars to see if this
// is one of the special lines starting a new "section" in the // is one of the special lines starting a new "section" in the
// document. // document.
let line = l.as_slice(); let line = l;
let mut odd = false; let mut odd = false;
// matched_group counts how many copies of section_delimiter // matched_group counts how many copies of section_delimiter
// this string consists of (0 if there's anything else) // this string consists of (0 if there's anything else)
@ -229,7 +227,7 @@ fn nl<T: Reader> (reader: &mut BufferedReader<T>, settings: &Settings) {
if settings.renumber { if settings.renumber {
line_no = settings.starting_line_number; line_no = settings.starting_line_number;
line_no_width = line_no_width_initial; line_no_width = line_no_width_initial;
line_no_threshold = Int::pow(10u64, line_no_width as u32); line_no_threshold = 10u64.pow(line_no_width as u32);
} }
&settings.header_numbering &settings.header_numbering
}, },
@ -268,7 +266,7 @@ fn nl<T: Reader> (reader: &mut BufferedReader<T>, settings: &Settings) {
// in the next selector. // in the next selector.
empty_line_count = 0; empty_line_count = 0;
} }
if !line_filter(line, regex_filter) if !line_filter(&line, regex_filter)
|| ( empty_line_count > 0 && empty_line_count < settings.join_blank_lines) { || ( empty_line_count > 0 && empty_line_count < settings.join_blank_lines) {
// No number is printed for this line. Either we did not // No number is printed for this line. Either we did not
// want to print one in the first place, or it is a blank // want to print one in the first place, or it is a blank

View file

@ -1,48 +1,57 @@
#![allow(unstable)] use std::io::Write;
use std::process::{Command, Stdio};
use std::old_io::process::Command;
use std::str; use std::str;
static PROGNAME: &'static str = "./nl"; static PROGNAME: &'static str = "./nl";
fn run(args: &[&'static str]) -> String {
let po = Command::new(PROGNAME)
.args(args)
.output()
.unwrap_or_else(|e| panic!("{}", e));
str::from_utf8(&po.stdout).unwrap().to_string()
}
fn run_with_stdin(input: &str, args: &[&'static str]) -> String {
let mut process = Command::new(PROGNAME)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.unwrap_or_else(|e| panic!("{}", e));
process.stdin
.take()
.unwrap_or_else(|| panic!("Could not take child process stdin"))
.write_all(input.as_bytes())
.unwrap_or_else(|e| panic!("{}", e));
let po = process.wait_with_output().unwrap_or_else(|e| panic!("{}", e));
str::from_utf8(&po.stdout).unwrap().to_string()
}
#[test] #[test]
fn test_stdin_nonewline() { fn test_stdin_nonewline() {
let mut process = Command::new(PROGNAME).spawn().unwrap(); let out = run_with_stdin("No Newline", &[]);
process.stdin.take().unwrap().write_all(b"No Newline").unwrap(); assert_eq!(&out, " 1\tNo Newline\n");
let po = process.wait_with_output().unwrap();
let out = str::from_utf8(po.output.as_slice()).unwrap();
assert_eq!(out, " 1\tNo Newline\n");
} }
#[test] #[test]
fn test_stdin_newline() { fn test_stdin_newline() {
let mut process = Command::new(PROGNAME).arg("-s").arg("-") let out = run_with_stdin("Line One\nLine Two\n", &["-s", "-", "-w", "1"]);
.arg("-w").arg("1").spawn().unwrap(); assert_eq!(&out, "1-Line One\n2-Line Two\n");
process.stdin.take().unwrap().write_all(b"Line One\nLine Two\n").unwrap();
let po = process.wait_with_output().unwrap();
let out = str::from_utf8(po.output.as_slice()).unwrap();
assert_eq!(out, "1-Line One\n2-Line Two\n");
} }
#[test] #[test]
fn test_padding_without_overflow() { fn test_padding_without_overflow() {
let po = Command::new(PROGNAME).arg("-i").arg("1000").arg("-s").arg("x") let out = run(&["-i", "1000", "-s", "x", "-n", "rz", "simple.txt"]);
.arg("-n").arg("rz").arg("simple.txt").output().unwrap(); assert_eq!(&out, "000001xL1\n001001xL2\n002001xL3\n003001xL4\n004001xL5\n005001xL6\n006001xL7\n007001xL8\n008001xL9\n009001xL10\n010001xL11\n011001xL12\n012001xL13\n013001xL14\n014001xL15\n");
let out = str::from_utf8(po.output.as_slice()).unwrap();
assert_eq!(out, "000001xL1\n001001xL2\n002001xL3\n003001xL4\n004001xL5\n005001xL6\n006001xL7\n007001xL8\n008001xL9\n009001xL10\n010001xL11\n011001xL12\n012001xL13\n013001xL14\n014001xL15\n");
} }
#[test] #[test]
fn test_padding_with_overflow() { fn test_padding_with_overflow() {
let po = Command::new(PROGNAME).arg("-i").arg("1000").arg("-s").arg("x") let out = run(&["-i", "1000", "-s", "x", "-n", "rz", "-w", "4", "simple.txt"]);
.arg("-n").arg("rz").arg("-w").arg("4") assert_eq!(&out, "0001xL1\n1001xL2\n2001xL3\n3001xL4\n4001xL5\n5001xL6\n6001xL7\n7001xL8\n8001xL9\n9001xL10\n10001xL11\n11001xL12\n12001xL13\n13001xL14\n14001xL15\n");
.arg("simple.txt").output().unwrap();
let out = str::from_utf8(po.output.as_slice()).unwrap();
assert_eq!(out, "0001xL1\n1001xL2\n2001xL3\n3001xL4\n4001xL5\n5001xL6\n6001xL7\n7001xL8\n8001xL9\n9001xL10\n10001xL11\n11001xL12\n12001xL13\n13001xL14\n14001xL15\n");
} }
#[test] #[test]
@ -57,9 +66,7 @@ fn test_sections_and_styles() {
"1 |Nonempty\n2 |Nonempty\n3 |Followed by 10x empty\n\n\n\n\n4 |\n\n\n\n\n5 |\n6 |Followed by 5x empty\n\n\n\n\n7 |\n8 |Followed by 4x empty\n\n\n\n\n9 |Nonempty\n10 |Nonempty\n11 |Nonempty.\n" "1 |Nonempty\n2 |Nonempty\n3 |Followed by 10x empty\n\n\n\n\n4 |\n\n\n\n\n5 |\n6 |Followed by 5x empty\n\n\n\n\n7 |\n8 |Followed by 4x empty\n\n\n\n\n9 |Nonempty\n10 |Nonempty\n11 |Nonempty.\n"
), ),
].iter() { ].iter() {
let po = Command::new(PROGNAME).arg("-s").arg("|").arg("-n").arg("ln") let out = run(&["-s", "|", "-n", "ln", "-w", "3", "-b", "a", "-l", "5", fixture]);
.arg("-w").arg("3").arg("-b").arg("a").arg("-l").arg("5") assert_eq!(&out, output);
.arg(fixture).output().unwrap();
assert_eq!(str::from_utf8(po.output.as_slice()).unwrap(), output);
} }
} }