Files
cat/src/main.rs

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;
}
}
}
}