154 lines
5.9 KiB
Rust
154 lines
5.9 KiB
Rust
use clap::Parser;
|
|
use std::fs::File;
|
|
use std::io::BufRead;
|
|
use std::io::BufReader;
|
|
|
|
#[derive(Parser, Debug)]
|
|
#[command(version)]
|
|
struct Args {
|
|
/// Numérotation des lignes non vides
|
|
#[arg(short = 'b', long)]
|
|
number_nonblank: bool,
|
|
|
|
/// Affichage d'un $ à la fin de chaque ligne
|
|
#[arg(short = 'E', long)]
|
|
show_ends: bool,
|
|
|
|
/// Numérotation des lignes
|
|
#[arg(short, long)]
|
|
number: bool,
|
|
|
|
/// Suppression des lignes vides répétées
|
|
#[arg(short, long)]
|
|
squeeze_blank: bool,
|
|
|
|
/// Affichage des tabulations avec la séquence ^I
|
|
#[arg(short = 'T', long)]
|
|
show_tabs: bool,
|
|
|
|
/// Ignoré (conservé par compatibilité)
|
|
#[arg(short = 'u')]
|
|
_u: bool,
|
|
|
|
/// Utilisation de la notation ^ et M- sauf pour LFD et TAB
|
|
#[arg(short = 'v', long)]
|
|
show_nonprinting: bool,
|
|
|
|
/// Equivalent à -vET
|
|
#[arg(short = 'A', long)]
|
|
show_all: bool,
|
|
|
|
/// Equivalent à -vE
|
|
#[arg(short = 'e')]
|
|
show_all_no_tabs: bool,
|
|
|
|
/// Equivalent à -vT
|
|
#[arg(short = 't')]
|
|
show_all_no_ends: bool,
|
|
|
|
/// Liste de fichiers
|
|
files: Vec<String>,
|
|
}
|
|
|
|
fn main() {
|
|
let mut args = Args::parse();
|
|
|
|
args.show_nonprinting = args.show_nonprinting || args.show_all || args.show_all_no_tabs || args.show_all_no_ends;
|
|
args.show_ends = args.show_ends || args.show_all || args.show_all_no_tabs;
|
|
args.show_tabs = args.show_tabs || args.show_all || args.show_all_no_ends;
|
|
args.number_nonblank = args.number_nonblank && !args.number;
|
|
|
|
let line_ends = if args.show_ends { "$" } else { "" };
|
|
let mut line_number = 0;
|
|
let mut line_prefix: String;
|
|
let mut last_was_blank = false;
|
|
|
|
for file_path in args.files {
|
|
let f = File::open(file_path).unwrap(); // File : structure pour accéder à un fichier
|
|
let mut reader = BufReader::new(f); // BufRead<File> : structure pour faire des lectures bufferisées
|
|
loop {
|
|
let mut line = String::new();
|
|
if let Ok(len) = reader.read_line(&mut line) { // read_line retourne une longueur nulle à la fin du fichier
|
|
if len==0 {
|
|
break;
|
|
}
|
|
|
|
let line_is_blank = line.len()==0;
|
|
let skip_line = args.squeeze_blank && last_was_blank && line_is_blank;
|
|
|
|
if args.number || (args.number_nonblank && !line_is_blank) {
|
|
line_number += 1;
|
|
line_prefix = format!("{:>6} ", line_number); // format! : macro de formatage (à la printf)
|
|
if line_prefix.len()<8 {
|
|
line_prefix.push(' ');
|
|
}
|
|
} else {
|
|
line_prefix = String::from("");
|
|
}
|
|
|
|
let mut line_to_display = String::from("");
|
|
|
|
for c in line.chars() {
|
|
if c.len_utf8() == 1 {
|
|
// Si le caractère est convertible en ASCII
|
|
let ascii_ch = c as u8;
|
|
if ascii_ch>=32 {
|
|
line_to_display.push(c)
|
|
} else if ascii_ch==9 && !args.show_tabs {
|
|
line_to_display.push(c)
|
|
} else if ascii_ch==10 {
|
|
line_to_display.push(c)
|
|
} else if ascii_ch==13 {
|
|
line_to_display.push_str("^M")
|
|
} else if ascii_ch!=13 && args.show_nonprinting {
|
|
line_to_display.push_str("^");
|
|
line_to_display.push((ascii_ch + 64) as char)
|
|
}
|
|
} else {
|
|
// On est sur un caractère UNICODE
|
|
// Conversion des caractères non imprimables selon https://github.com/coreutils/coreutils/blob/5cecd703e57b2e1301767d82cbe5bb01cae88472/src/cat.c#L412
|
|
if args.show_nonprinting {
|
|
let mut caracts_ut8 = [0; 4];
|
|
let _result = c.encode_utf8(&mut caracts_ut8);
|
|
|
|
for sub in caracts_ut8 {
|
|
if sub>=32 {
|
|
if sub < 127 {
|
|
line_to_display.push(char::from_u32(sub as u32).unwrap())
|
|
} else if sub == 127 {
|
|
line_to_display.push_str("^?")
|
|
} else {
|
|
line_to_display.push_str("M-");
|
|
if sub >= 128 + 32 {
|
|
if sub < 128 + 127 {
|
|
line_to_display.push(char::from_u32(sub as u32 - 128).unwrap());
|
|
} else {
|
|
line_to_display.push_str("^?")
|
|
}
|
|
} else {
|
|
line_to_display.push('^');
|
|
line_to_display.push(char::from_u32(sub as u32 - 128 + 64).unwrap());
|
|
}
|
|
}
|
|
} else {
|
|
if sub==0x9 && !args.show_tabs {
|
|
line_to_display.push(char::from_u32(sub as u32).unwrap())
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
line_to_display.push(c)
|
|
}
|
|
}
|
|
}
|
|
|
|
if !skip_line {
|
|
print!("{}{}{}", line_prefix, line_to_display, line_ends);
|
|
}
|
|
|
|
last_was_blank = line_is_blank;
|
|
}
|
|
}
|
|
}
|
|
}
|