diff --git a/Vagrantfile b/Vagrantfile index 20bf680..c412e8a 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -172,6 +172,16 @@ Vagrant.configure(2) do |config| echo -ne "#{test_dir}/file-names/invalid-utf8-2: [\\xc3\\x28]" | xargs -0 touch echo -ne "#{test_dir}/file-names/invalid-utf8-3: [\\xe2\\x82\\x28]" | xargs -0 touch echo -ne "#{test_dir}/file-names/invalid-utf8-4: [\\xf0\\x28\\x8c\\x28]" | xargs -0 touch + + echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]" | xargs -0 mkdir + echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/subfile" | xargs -0 touch + echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/another: [\\n]" | xargs -0 touch + echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/broken" | xargs -0 touch + + mkdir "#{test_dir}/file-names/links" + ln -s "#{test_dir}/file-names/new-line-dir"*/* "#{test_dir}/file-names/links" + + echo -ne "#{test_dir}/file-names/new-line-dir: [\\n]/broken" | xargs -0 rm EOF diff --git a/src/exa.rs b/src/exa.rs index dc858de..1d3d2d4 100644 --- a/src/exa.rs +++ b/src/exa.rs @@ -23,9 +23,12 @@ use std::ffi::OsStr; use std::io::{stderr, Write, Result as IOResult}; use std::path::{Component, Path}; +use ansi_term::{ANSIStrings, Style}; + use fs::{Dir, File}; use options::{Options, View}; pub use options::Misfire; +use output::escape; mod fs; mod info; @@ -116,7 +119,9 @@ impl<'w, W: Write + 'w> Exa<'w, W> { } if !is_only_dir { - writeln!(self.writer, "{}:", dir.path.display())?; + let mut bits = Vec::new(); + escape(dir.path.display().to_string(), &mut bits, Style::default(), Style::default()); + writeln!(self.writer, "{}:", ANSIStrings(&bits))?; } let mut children = Vec::new(); diff --git a/src/fs/file.rs b/src/fs/file.rs index b8c8c5d..c375aab 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1,7 +1,5 @@ //! Files, and methods and fields to access their metadata. -use std::ascii::AsciiExt; -use std::env::current_dir; use std::fs; use std::io::Error as IOError; use std::io::Result as IOResult; @@ -14,6 +12,7 @@ use fs::fields as f; #[cfg(any(target_os = "macos", target_os = "linux"))] use std::os::unix::fs::FileTypeExt; + /// Constant table copied from https://doc.rust-lang.org/src/std/sys/unix/ext/fs.rs.html#11-259 /// which is currently unstable and lacks vision for stabilization, /// see https://github.com/rust-lang/rust/issues/27712 @@ -344,6 +343,8 @@ impl<'dir> File<'dir> { /// directory, so will not work if this file has just been passed in on /// the command line. pub fn git_status(&self) -> f::Git { + use std::env::current_dir; + match self.dir { None => f::Git { staged: f::GitStatus::NotModified, unstaged: f::GitStatus::NotModified }, Some(d) => { @@ -421,6 +422,8 @@ impl<'a> AsRef> for File<'a> { /// against a pre-compiled list of extensions which are known to only exist /// within ASCII, so it's alright. fn ext(path: &Path) -> Option { + use std::ascii::AsciiExt; + let name = match path.file_name() { Some(f) => f.to_string_lossy().to_string(), None => return None, diff --git a/src/output/cell.rs b/src/output/cell.rs index 547e839..6465357 100644 --- a/src/output/cell.rs +++ b/src/output/cell.rs @@ -160,10 +160,21 @@ impl TextCellContents { ANSIStrings(&self.0) } + /// Calculates the width that a cell with these contents would take up, by + /// counting the number of characters in each unformatted ANSI string. pub fn width(&self) -> DisplayWidth { let foo = self.0.iter().map(|anstr| anstr.chars().count()).sum(); DisplayWidth(foo) } + + /// Promotes these contents to a full cell containing them alongside + /// their calculated width. + pub fn promote(self) -> TextCell { + TextCell { + width: self.width(), + contents: self, + } + } } diff --git a/src/output/details.rs b/src/output/details.rs index a9abac7..5e6688c 100644 --- a/src/output/details.rs +++ b/src/output/details.rs @@ -99,9 +99,9 @@ use fs::feature::xattr::{Attribute, FileAttributes}; use options::{FileFilter, RecurseOptions}; use output::colours::Colours; use output::column::{Alignment, Column, Columns, SizeFormat}; -use output::cell::{TextCell, DisplayWidth}; +use output::cell::{TextCell, TextCellContents, DisplayWidth}; use output::tree::TreeTrunk; -use super::filename; +use output::file_name::FileName; /// With the **Details** view, the output gets formatted into columns, with @@ -307,24 +307,10 @@ impl Details { let mut files = Vec::new(); let mut errors = egg.errors; - let filename = filename(&egg.file, &self.colours, true, self.classify); - let mut width = filename.width(); - - if egg.file.dir.is_none() { - if let Some(parent) = egg.file.path.parent() { - width = width + 1 + DisplayWidth::from(parent.to_string_lossy().as_ref()); - } - } - - let name = TextCell { - contents: filename, - width: width, - }; - let row = Row { depth: depth, cells: Some(egg.cells), - name: name, + name: FileName::new(&egg.file, &self.colours).paint(true, self.classify).promote(), last: index == num_eggs - 1, }; @@ -457,20 +443,8 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> { self.rows.push(row); } - pub fn filename_cell(&self, file: File, links: bool) -> TextCell { - let filename = filename(&file, &self.opts.colours, links, self.opts.classify); - let mut width = filename.width(); - - if file.dir.is_none() { - if let Some(parent) = file.path.parent() { - width = width + 1 + DisplayWidth::from(parent.to_string_lossy().as_ref()); - } - } - - TextCell { - contents: filename, - width: width, - } + pub fn filename(&self, file: File, links: bool) -> TextCellContents { + FileName::new(&file, &self.opts.colours).paint(links, self.opts.classify) } pub fn add_file_with_cells(&mut self, cells: Vec, name_cell: TextCell, depth: usize, last: bool) { diff --git a/src/output/escape.rs b/src/output/escape.rs new file mode 100644 index 0000000..161c393 --- /dev/null +++ b/src/output/escape.rs @@ -0,0 +1,25 @@ +use ansi_term::{ANSIString, Style}; + + +pub fn escape<'a>(string: String, bits: &mut Vec>, good: Style, bad: Style) { + if string.chars().all(|c| c >= 0x20 as char) { + bits.push(good.paint(string)); + } + else { + for c in string.chars() { + // The `escape_default` method on `char` is *almost* what we want here, but + // it still escapes non-ASCII UTF-8 characters, which are still printable. + + if c >= 0x20 as char { + // TODO: This allocates way too much, + // hence the `all` check above. + let mut s = String::new(); + s.push(c); + bits.push(good.paint(s)); + } else { + let s = c.escape_default().collect::(); + bits.push(bad.paint(s)); + } + } + } +} diff --git a/src/output/file_name.rs b/src/output/file_name.rs new file mode 100644 index 0000000..63b5a80 --- /dev/null +++ b/src/output/file_name.rs @@ -0,0 +1,151 @@ +use std::path::Path; + +use ansi_term::{ANSIString, Style}; + +use fs::{File, FileTarget}; +use output::Colours; +use output::escape; +use output::cell::TextCellContents; + + +pub struct FileName<'a, 'dir: 'a> { + file: &'a File<'dir>, + colours: &'a Colours, +} + +impl<'a, 'dir> FileName<'a, 'dir> { + pub fn new(file: &'a File<'dir>, colours: &'a Colours) -> FileName<'a, 'dir> { + FileName { + file: file, + colours: colours, + } + } + + pub fn paint(&self, links: bool, classify: bool) -> TextCellContents { + let mut bits = Vec::new(); + + if self.file.dir.is_none() { + if let Some(parent) = self.file.path.parent() { + self.add_parent_bits(&mut bits, parent); + } + } + + if !self.file.name.is_empty() { + for bit in self.coloured_file_name() { + bits.push(bit); + } + } + + if links && self.file.is_link() { + match self.file.link_target() { + FileTarget::Ok(target) => { + bits.push(Style::default().paint(" ")); + bits.push(self.colours.punctuation.paint("->")); + bits.push(Style::default().paint(" ")); + + if let Some(parent) = target.path.parent() { + self.add_parent_bits(&mut bits, parent); + } + + if !target.name.is_empty() { + let target = FileName::new(&target, self.colours); + for bit in target.coloured_file_name() { + bits.push(bit); + } + } + }, + + FileTarget::Broken(broken_path) => { + bits.push(Style::default().paint(" ")); + bits.push(self.colours.broken_arrow.paint("->")); + bits.push(Style::default().paint(" ")); + escape(broken_path.display().to_string(), &mut bits, self.colours.broken_filename, self.colours.control_char.underline()); + }, + + FileTarget::Err(_) => { + // Do nothing -- the error gets displayed on the next line + } + } + } + else if classify { + if let Some(class) = self.classify_char() { + bits.push(Style::default().paint(class)); + } + } + + bits.into() + } + + /// Adds the bits of the parent path to the given bits vector. + /// The path gets its characters escaped based on the colours. + fn add_parent_bits(&self, bits: &mut Vec, parent: &Path) { + let coconut = parent.components().count(); + + if coconut == 1 && parent.has_root() { + bits.push(self.colours.symlink_path.paint("/")); + } + else if coconut >= 1 { + escape(parent.to_string_lossy().to_string(), bits, self.colours.symlink_path, self.colours.control_char); + bits.push(self.colours.symlink_path.paint("/")); + } + } + + /// The character to be displayed after a file when classifying is on, if + /// the file’s type has one associated with it. + fn classify_char(&self) -> Option<&'static str> { + if self.file.is_executable_file() { + Some("*") + } else if self.file.is_directory() { + Some("/") + } else if self.file.is_pipe() { + Some("|") + } else if self.file.is_link() { + Some("@") + } else if self.file.is_socket() { + Some("=") + } else { + None + } + } + + /// Returns at least one ANSI-highlighted string representing this file’s + /// name using the given set of colours. + /// + /// Ordinarily, this will be just one string: the file’s complete name, + /// coloured according to its file type. If the name contains control + /// characters such as newlines or escapes, though, we can’t just print them + /// to the screen directly, because then there’ll be newlines in weird places. + /// + /// So in that situation, those characters will be escaped and highlighted in + /// a different colour. + fn coloured_file_name<'unused>(&self) -> Vec> { + let file_style = self.style(); + let mut bits = Vec::new(); + escape(self.file.name.clone(), &mut bits, file_style, self.colours.control_char); + bits + } + + pub fn style(&self) -> Style { + match self.file { + f if f.is_directory() => self.colours.filetypes.directory, + f if f.is_executable_file() => self.colours.filetypes.executable, + f if f.is_link() => self.colours.filetypes.symlink, + f if f.is_pipe() => self.colours.filetypes.pipe, + f if f.is_char_device() + | f.is_block_device() => self.colours.filetypes.device, + f if f.is_socket() => self.colours.filetypes.socket, + f if !f.is_file() => self.colours.filetypes.special, + f if f.is_immediate() => self.colours.filetypes.immediate, + f if f.is_image() => self.colours.filetypes.image, + f if f.is_video() => self.colours.filetypes.video, + f if f.is_music() => self.colours.filetypes.music, + f if f.is_lossless() => self.colours.filetypes.lossless, + f if f.is_crypto() => self.colours.filetypes.crypto, + f if f.is_document() => self.colours.filetypes.document, + f if f.is_compressed() => self.colours.filetypes.compressed, + f if f.is_temp() => self.colours.filetypes.temp, + f if f.is_compiled() => self.colours.filetypes.compiled, + _ => self.colours.filetypes.normal, + } + } +} diff --git a/src/output/grid.rs b/src/output/grid.rs index 5456d58..bddebae 100644 --- a/src/output/grid.rs +++ b/src/output/grid.rs @@ -3,9 +3,8 @@ use std::io::{Write, Result as IOResult}; use term_grid as grid; use fs::File; -use output::DisplayWidth; use output::colours::Colours; -use super::filename; +use output::file_name::FileName; #[derive(PartialEq, Debug, Copy, Clone)] @@ -29,14 +28,8 @@ impl Grid { grid.reserve(files.len()); for file in files.iter() { - let filename = filename(file, &self.colours, false, self.classify); - - let mut width = filename.width(); - if file.dir.is_none() { - if let Some(parent) = file.path.parent() { - width = width + 1 + DisplayWidth::from(parent.to_string_lossy().as_ref()); - } - } + let filename = FileName::new(file, &self.colours).paint(false, self.classify); + let width = filename.width(); grid.add(grid::Cell { contents: filename.strings().to_string(), @@ -50,7 +43,8 @@ impl Grid { else { // File names too long for a grid - drop down to just listing them! for file in files.iter() { - writeln!(w, "{}", filename(file, &self.colours, false, self.classify).strings())?; + let name_cell = FileName::new(file, &self.colours).paint(false, self.classify); + writeln!(w, "{}", name_cell.strings())?; } Ok(()) } diff --git a/src/output/grid_details.rs b/src/output/grid_details.rs index 1ad38bc..48cb9a5 100644 --- a/src/output/grid_details.rs +++ b/src/output/grid_details.rs @@ -45,7 +45,7 @@ impl GridDetails { .collect::>(); let file_names = files.into_iter() - .map(|file| first_table.filename_cell(file, false)) + .map(|file| first_table.filename(file, false).promote()) .collect::>(); (cells, file_names) diff --git a/src/output/lines.rs b/src/output/lines.rs index 6582dec..dfe8d47 100644 --- a/src/output/lines.rs +++ b/src/output/lines.rs @@ -4,7 +4,7 @@ use ansi_term::ANSIStrings; use fs::File; -use super::filename; +use output::file_name::FileName; use super::colours::Colours; @@ -18,7 +18,8 @@ pub struct Lines { impl Lines { pub fn view(&self, files: Vec, w: &mut W) -> IOResult<()> { for file in files { - writeln!(w, "{}", ANSIStrings(&filename(&file, &self.colours, true, self.classify)))?; + let name_cell = FileName::new(&file, &self.colours).paint(true, self.classify); + writeln!(w, "{}", ANSIStrings(&name_cell))?; } Ok(()) } diff --git a/src/output/mod.rs b/src/output/mod.rs index 5b87d2f..48b886e 100644 --- a/src/output/mod.rs +++ b/src/output/mod.rs @@ -1,13 +1,10 @@ -use ansi_term::{ANSIString, Style}; - -use fs::{File, FileTarget}; - pub use self::cell::{TextCell, TextCellContents, DisplayWidth}; pub use self::colours::Colours; pub use self::details::Details; pub use self::grid_details::GridDetails; pub use self::grid::Grid; pub use self::lines::Lines; +pub use self::escape::escape; mod grid; pub mod details; @@ -17,143 +14,5 @@ pub mod column; mod cell; mod colours; mod tree; - - -pub fn filename(file: &File, colours: &Colours, links: bool, classify: bool) -> TextCellContents { - let mut bits = Vec::new(); - - // TODO: This long function could do with some splitting up. - - if file.dir.is_none() { - if let Some(parent) = file.path.parent() { - let coconut = parent.components().count(); - - if coconut == 1 && parent.has_root() { - bits.push(colours.symlink_path.paint("/")); - } - else if coconut >= 1 { - bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string())); - bits.push(colours.symlink_path.paint("/")); - } - } - } - - if !file.name.is_empty() { - for bit in coloured_file_name(file, colours) { - bits.push(bit); - } - } - - if links && file.is_link() { - match file.link_target() { - FileTarget::Ok(target) => { - bits.push(Style::default().paint(" ")); - bits.push(colours.punctuation.paint("->")); - bits.push(Style::default().paint(" ")); - - if let Some(parent) = target.path.parent() { - let coconut = parent.components().count(); - - if coconut == 1 && parent.has_root() { - bits.push(colours.symlink_path.paint("/")); - } - else if coconut >= 1 { - bits.push(colours.symlink_path.paint(parent.to_string_lossy().to_string())); - bits.push(colours.symlink_path.paint("/")); - } - } - - if !target.name.is_empty() { - bits.push(file_colour(colours, &target).paint(target.name)); - } - }, - - FileTarget::Broken(broken_path) => { - bits.push(Style::default().paint(" ")); - bits.push(colours.broken_arrow.paint("->")); - bits.push(Style::default().paint(" ")); - bits.push(colours.broken_filename.paint(broken_path.display().to_string())); - }, - - FileTarget::Err(_) => { - // Do nothing -- the error gets displayed on the next line - } - } - } else if classify { - if file.is_executable_file() { - bits.push(Style::default().paint("*")); - } else if file.is_directory() { - bits.push(Style::default().paint("/")); - } else if file.is_pipe() { - bits.push(Style::default().paint("|")); - } else if file.is_link() { - bits.push(Style::default().paint("@")); - } else if file.is_socket() { - bits.push(Style::default().paint("=")); - } - } - - bits.into() -} - -/// Returns at least one ANSI-highlighted string representing this file’s -/// name using the given set of colours. -/// -/// Ordinarily, this will be just one string: the file’s complete name, -/// coloured according to its file type. If the name contains control -/// characters such as newlines or escapes, though, we can’t just print them -/// to the screen directly, because then there’ll be newlines in weird places. -/// -/// So in that situation, those characters will be escaped and highlighted in -/// a different colour. -fn coloured_file_name<'a>(file: &File, colours: &Colours) -> Vec> { - let colour = file_colour(colours, file); - let mut bits = Vec::new(); - - if file.name.chars().all(|c| c >= 0x20 as char) { - bits.push(colour.paint(file.name.clone())); - } - else { - for c in file.name.chars() { - // The `escape_default` method on `char` is *almost* what we want here, but - // it still escapes non-ASCII UTF-8 characters, which are still printable. - - if c >= 0x20 as char { - // TODO: This allocates way too much, - // hence the `all` check above. - let mut s = String::new(); - s.push(c); - bits.push(colour.paint(s)); - } else { - let s = c.escape_default().collect::(); - bits.push(colours.control_char.paint(s)); - } - } - } - - bits -} - -pub fn file_colour(colours: &Colours, file: &File) -> Style { - match file { - f if f.is_directory() => colours.filetypes.directory, - f if f.is_executable_file() => colours.filetypes.executable, - f if f.is_link() => colours.filetypes.symlink, - f if f.is_pipe() => colours.filetypes.pipe, - f if f.is_char_device() - | f.is_block_device() => colours.filetypes.device, - f if f.is_socket() => colours.filetypes.socket, - f if !f.is_file() => colours.filetypes.special, - f if f.is_immediate() => colours.filetypes.immediate, - f if f.is_image() => colours.filetypes.image, - f if f.is_video() => colours.filetypes.video, - f if f.is_music() => colours.filetypes.music, - f if f.is_lossless() => colours.filetypes.lossless, - f if f.is_crypto() => colours.filetypes.crypto, - f if f.is_document() => colours.filetypes.document, - f if f.is_compressed() => colours.filetypes.compressed, - f if f.is_temp() => colours.filetypes.temp, - f if f.is_compiled() => colours.filetypes.compiled, - _ => colours.filetypes.normal, - } -} +pub mod file_name; +mod escape; diff --git a/xtests/file_names b/xtests/file_names index 5120bb4..fcb50ce 100644 --- a/xtests/file_names +++ b/xtests/file_names @@ -1,6 +1,6 @@ -ansi: [\u{1b}[34mblue\u{1b}[0m] form-feed: [\u{c}] return: [\r] -ascii: hello invalid-utf8-1: [�] tab: [\t] -backspace: [\u{8}] invalid-utf8-2: [�(] utf-8: pâté -bell: [\u{7}] invalid-utf8-3: [�(] vertical-tab: [\u{b}] -emoji: [🆒] invalid-utf8-4: [�(�(] -escape: [\u{1b}] new-line: [\n] +ansi: [\u{1b}[34mblue\u{1b}[0m] form-feed: [\u{c}] new-line-dir: [\n] +ascii: hello invalid-utf8-1: [�] new-line: [\n] +backspace: [\u{8}] invalid-utf8-2: [�(] return: [\r] +bell: [\u{7}] invalid-utf8-3: [�(] tab: [\t] +emoji: [🆒] invalid-utf8-4: [�(�(] utf-8: pâté +escape: [\u{1b}] links vertical-tab: [\u{b}] diff --git a/xtests/file_names_1 b/xtests/file_names_1 index b0c2508..eb2b5d2 100644 --- a/xtests/file_names_1 +++ b/xtests/file_names_1 @@ -9,6 +9,8 @@ invalid-utf8-1: [�] invalid-utf8-2: [�(] invalid-utf8-3: [�(] invalid-utf8-4: [�(�(] +links +new-line-dir: [\n] new-line: [\n] return: [\r] tab: [\t] diff --git a/xtests/file_names_R b/xtests/file_names_R new file mode 100644 index 0000000..7b7c9fe --- /dev/null +++ b/xtests/file_names_R @@ -0,0 +1,12 @@ +ansi: [\u{1b}[34mblue\u{1b}[0m] form-feed: [\u{c}] new-line-dir: [\n] +ascii: hello invalid-utf8-1: [�] new-line: [\n] +backspace: [\u{8}] invalid-utf8-2: [�(] return: [\r] +bell: [\u{7}] invalid-utf8-3: [�(] tab: [\t] +emoji: [🆒] invalid-utf8-4: [�(�(] utf-8: pâté +escape: [\u{1b}] links vertical-tab: [\u{b}] + +/testcases/file-names/links: +another: [\n] broken subfile + +/testcases/file-names/new-line-dir: [\n]: +another: [\n] subfile diff --git a/xtests/file_names_T b/xtests/file_names_T new file mode 100644 index 0000000..528e4b3 --- /dev/null +++ b/xtests/file_names_T @@ -0,0 +1,29 @@ +/testcases/file-names +├── ansi: [\u{1b}[34mblue\u{1b}[0m] +├── ascii: hello +├── backspace: [\u{8}] +├── bell: [\u{7}] +├── emoji: [🆒] +├── escape: [\u{1b}] +├── form-feed: [\u{c}] +├── invalid-utf8-1: [�] +│ └──  +├── invalid-utf8-2: [�(] +│ └──  +├── invalid-utf8-3: [�(] +│ └──  +├── invalid-utf8-4: [�(�(] +│ └──  +├── links +│ ├── another: [\n] -> /testcases/file-names/new-line-dir: [\n]/another: [\n] +│ ├── broken -> /testcases/file-names/new-line-dir: [\n]/broken +│ │ └──  +│ └── subfile -> /testcases/file-names/new-line-dir: [\n]/subfile +├── new-line-dir: [\n] +│ ├── another: [\n] +│ └── subfile +├── new-line: [\n] +├── return: [\r] +├── tab: [\t] +├── utf-8: pâté +└── vertical-tab: [\u{b}] diff --git a/xtests/file_names_x b/xtests/file_names_x index f5f22bc..0264fb3 100644 --- a/xtests/file_names_x +++ b/xtests/file_names_x @@ -1,6 +1,6 @@ ansi: [\u{1b}[34mblue\u{1b}[0m] ascii: hello backspace: [\u{8}] bell: [\u{7}] emoji: [🆒] escape: [\u{1b}] form-feed: [\u{c}] invalid-utf8-1: [�] invalid-utf8-2: [�(] -invalid-utf8-3: [�(] invalid-utf8-4: [�(�(] new-line: [\n] -return: [\r] tab: [\t] utf-8: pâté -vertical-tab: [\u{b}] +invalid-utf8-3: [�(] invalid-utf8-4: [�(�(] links +new-line-dir: [\n] new-line: [\n] return: [\r] +tab: [\t] utf-8: pâté vertical-tab: [\u{b}] diff --git a/xtests/files_star_100 b/xtests/files_star_100 new file mode 100644 index 0000000..5523127 --- /dev/null +++ b/xtests/files_star_100 @@ -0,0 +1,13 @@ +/testcases/files/10_bytes /testcases/files/1_KiB /testcases/files/5_MiB +/testcases/files/10_KiB /testcases/files/1_MiB /testcases/files/6_bytes +/testcases/files/10_MiB /testcases/files/2_bytes /testcases/files/6_KiB +/testcases/files/11_bytes /testcases/files/2_KiB /testcases/files/6_MiB +/testcases/files/11_KiB /testcases/files/2_MiB /testcases/files/7_bytes +/testcases/files/11_MiB /testcases/files/3_bytes /testcases/files/7_KiB +/testcases/files/12_bytes /testcases/files/3_KiB /testcases/files/7_MiB +/testcases/files/12_KiB /testcases/files/3_MiB /testcases/files/8_bytes +/testcases/files/12_MiB /testcases/files/4_bytes /testcases/files/8_KiB +/testcases/files/13_bytes /testcases/files/4_KiB /testcases/files/8_MiB +/testcases/files/13_KiB /testcases/files/4_MiB /testcases/files/9_bytes +/testcases/files/13_MiB /testcases/files/5_bytes /testcases/files/9_KiB +/testcases/files/1_bytes /testcases/files/5_KiB /testcases/files/9_MiB diff --git a/xtests/files_star_150 b/xtests/files_star_150 new file mode 100644 index 0000000..eac4bde --- /dev/null +++ b/xtests/files_star_150 @@ -0,0 +1,8 @@ +/testcases/files/10_bytes /testcases/files/12_MiB /testcases/files/2_KiB /testcases/files/5_bytes /testcases/files/7_MiB +/testcases/files/10_KiB /testcases/files/13_bytes /testcases/files/2_MiB /testcases/files/5_KiB /testcases/files/8_bytes +/testcases/files/10_MiB /testcases/files/13_KiB /testcases/files/3_bytes /testcases/files/5_MiB /testcases/files/8_KiB +/testcases/files/11_bytes /testcases/files/13_MiB /testcases/files/3_KiB /testcases/files/6_bytes /testcases/files/8_MiB +/testcases/files/11_KiB /testcases/files/1_bytes /testcases/files/3_MiB /testcases/files/6_KiB /testcases/files/9_bytes +/testcases/files/11_MiB /testcases/files/1_KiB /testcases/files/4_bytes /testcases/files/6_MiB /testcases/files/9_KiB +/testcases/files/12_bytes /testcases/files/1_MiB /testcases/files/4_KiB /testcases/files/7_bytes /testcases/files/9_MiB +/testcases/files/12_KiB /testcases/files/2_bytes /testcases/files/4_MiB /testcases/files/7_KiB diff --git a/xtests/files_star_200 b/xtests/files_star_200 new file mode 100644 index 0000000..7e9264f --- /dev/null +++ b/xtests/files_star_200 @@ -0,0 +1,6 @@ +/testcases/files/10_bytes /testcases/files/12_bytes /testcases/files/1_bytes /testcases/files/3_bytes /testcases/files/5_bytes /testcases/files/7_bytes /testcases/files/9_bytes +/testcases/files/10_KiB /testcases/files/12_KiB /testcases/files/1_KiB /testcases/files/3_KiB /testcases/files/5_KiB /testcases/files/7_KiB /testcases/files/9_KiB +/testcases/files/10_MiB /testcases/files/12_MiB /testcases/files/1_MiB /testcases/files/3_MiB /testcases/files/5_MiB /testcases/files/7_MiB /testcases/files/9_MiB +/testcases/files/11_bytes /testcases/files/13_bytes /testcases/files/2_bytes /testcases/files/4_bytes /testcases/files/6_bytes /testcases/files/8_bytes +/testcases/files/11_KiB /testcases/files/13_KiB /testcases/files/2_KiB /testcases/files/4_KiB /testcases/files/6_KiB /testcases/files/8_KiB +/testcases/files/11_MiB /testcases/files/13_MiB /testcases/files/2_MiB /testcases/files/4_MiB /testcases/files/6_MiB /testcases/files/8_MiB diff --git a/xtests/files_star_lG_100 b/xtests/files_star_lG_100 new file mode 100644 index 0000000..d35b5b0 --- /dev/null +++ b/xtests/files_star_lG_100 @@ -0,0 +1,39 @@ +.rw-r--r-- 10 cassowary  1 Jan 12:34 /testcases/files/10_bytes +.rw-r--r-- 10k cassowary  1 Jan 12:34 /testcases/files/10_KiB +.rw-r--r-- 10M cassowary  1 Jan 12:34 /testcases/files/10_MiB +.rw-r--r-- 11 cassowary  1 Jan 12:34 /testcases/files/11_bytes +.rw-r--r-- 11k cassowary  1 Jan 12:34 /testcases/files/11_KiB +.rw-r--r-- 11M cassowary  1 Jan 12:34 /testcases/files/11_MiB +.rw-r--r-- 12 cassowary  1 Jan 12:34 /testcases/files/12_bytes +.rw-r--r-- 12k cassowary  1 Jan 12:34 /testcases/files/12_KiB +.rw-r--r-- 12M cassowary  1 Jan 12:34 /testcases/files/12_MiB +.rw-r--r-- 13 cassowary  1 Jan 12:34 /testcases/files/13_bytes +.rw-r--r-- 13k cassowary  1 Jan 12:34 /testcases/files/13_KiB +.rw-r--r-- 13M cassowary  1 Jan 12:34 /testcases/files/13_MiB +.rw-r--r-- 1 cassowary  1 Jan 12:34 /testcases/files/1_bytes +.rw-r--r-- 1.0k cassowary  1 Jan 12:34 /testcases/files/1_KiB +.rw-r--r-- 1.0M cassowary  1 Jan 12:34 /testcases/files/1_MiB +.rw-r--r-- 2 cassowary  1 Jan 12:34 /testcases/files/2_bytes +.rw-r--r-- 2.0k cassowary  1 Jan 12:34 /testcases/files/2_KiB +.rw-r--r-- 2.1M cassowary  1 Jan 12:34 /testcases/files/2_MiB +.rw-r--r-- 3 cassowary  1 Jan 12:34 /testcases/files/3_bytes +.rw-r--r-- 3.1k cassowary  1 Jan 12:34 /testcases/files/3_KiB +.rw-r--r-- 3.1M cassowary  1 Jan 12:34 /testcases/files/3_MiB +.rw-r--r-- 4 cassowary  1 Jan 12:34 /testcases/files/4_bytes +.rw-r--r-- 4.1k cassowary  1 Jan 12:34 /testcases/files/4_KiB +.rw-r--r-- 4.2M cassowary  1 Jan 12:34 /testcases/files/4_MiB +.rw-r--r-- 5 cassowary  1 Jan 12:34 /testcases/files/5_bytes +.rw-r--r-- 5.1k cassowary  1 Jan 12:34 /testcases/files/5_KiB +.rw-r--r-- 5.2M cassowary  1 Jan 12:34 /testcases/files/5_MiB +.rw-r--r-- 6 cassowary  1 Jan 12:34 /testcases/files/6_bytes +.rw-r--r-- 6.1k cassowary  1 Jan 12:34 /testcases/files/6_KiB +.rw-r--r-- 6.3M cassowary  1 Jan 12:34 /testcases/files/6_MiB +.rw-r--r-- 7 cassowary  1 Jan 12:34 /testcases/files/7_bytes +.rw-r--r-- 7.2k cassowary  1 Jan 12:34 /testcases/files/7_KiB +.rw-r--r-- 7.3M cassowary  1 Jan 12:34 /testcases/files/7_MiB +.rw-r--r-- 8 cassowary  1 Jan 12:34 /testcases/files/8_bytes +.rw-r--r-- 8.2k cassowary  1 Jan 12:34 /testcases/files/8_KiB +.rw-r--r-- 8.4M cassowary  1 Jan 12:34 /testcases/files/8_MiB +.rw-r--r-- 9 cassowary  1 Jan 12:34 /testcases/files/9_bytes +.rw-r--r-- 9.2k cassowary  1 Jan 12:34 /testcases/files/9_KiB +.rw-r--r-- 9.4M cassowary  1 Jan 12:34 /testcases/files/9_MiB diff --git a/xtests/files_star_lG_150 b/xtests/files_star_lG_150 new file mode 100644 index 0000000..4f076a8 --- /dev/null +++ b/xtests/files_star_lG_150 @@ -0,0 +1,20 @@ +.rw-r--r-- 10 cassowary  1 Jan 12:34 /testcases/files/10_bytes .rw-r--r-- 3.1M cassowary  1 Jan 12:34 /testcases/files/3_MiB +.rw-r--r-- 10k cassowary  1 Jan 12:34 /testcases/files/10_KiB .rw-r--r-- 4 cassowary  1 Jan 12:34 /testcases/files/4_bytes +.rw-r--r-- 10M cassowary  1 Jan 12:34 /testcases/files/10_MiB .rw-r--r-- 4.1k cassowary  1 Jan 12:34 /testcases/files/4_KiB +.rw-r--r-- 11 cassowary  1 Jan 12:34 /testcases/files/11_bytes .rw-r--r-- 4.2M cassowary  1 Jan 12:34 /testcases/files/4_MiB +.rw-r--r-- 11k cassowary  1 Jan 12:34 /testcases/files/11_KiB .rw-r--r-- 5 cassowary  1 Jan 12:34 /testcases/files/5_bytes +.rw-r--r-- 11M cassowary  1 Jan 12:34 /testcases/files/11_MiB .rw-r--r-- 5.1k cassowary  1 Jan 12:34 /testcases/files/5_KiB +.rw-r--r-- 12 cassowary  1 Jan 12:34 /testcases/files/12_bytes .rw-r--r-- 5.2M cassowary  1 Jan 12:34 /testcases/files/5_MiB +.rw-r--r-- 12k cassowary  1 Jan 12:34 /testcases/files/12_KiB .rw-r--r-- 6 cassowary  1 Jan 12:34 /testcases/files/6_bytes +.rw-r--r-- 12M cassowary  1 Jan 12:34 /testcases/files/12_MiB .rw-r--r-- 6.1k cassowary  1 Jan 12:34 /testcases/files/6_KiB +.rw-r--r-- 13 cassowary  1 Jan 12:34 /testcases/files/13_bytes .rw-r--r-- 6.3M cassowary  1 Jan 12:34 /testcases/files/6_MiB +.rw-r--r-- 13k cassowary  1 Jan 12:34 /testcases/files/13_KiB .rw-r--r-- 7 cassowary  1 Jan 12:34 /testcases/files/7_bytes +.rw-r--r-- 13M cassowary  1 Jan 12:34 /testcases/files/13_MiB .rw-r--r-- 7.2k cassowary  1 Jan 12:34 /testcases/files/7_KiB +.rw-r--r-- 1 cassowary  1 Jan 12:34 /testcases/files/1_bytes .rw-r--r-- 7.3M cassowary  1 Jan 12:34 /testcases/files/7_MiB +.rw-r--r-- 1.0k cassowary  1 Jan 12:34 /testcases/files/1_KiB .rw-r--r-- 8 cassowary  1 Jan 12:34 /testcases/files/8_bytes +.rw-r--r-- 1.0M cassowary  1 Jan 12:34 /testcases/files/1_MiB .rw-r--r-- 8.2k cassowary  1 Jan 12:34 /testcases/files/8_KiB +.rw-r--r-- 2 cassowary  1 Jan 12:34 /testcases/files/2_bytes .rw-r--r-- 8.4M cassowary  1 Jan 12:34 /testcases/files/8_MiB +.rw-r--r-- 2.0k cassowary  1 Jan 12:34 /testcases/files/2_KiB .rw-r--r-- 9 cassowary  1 Jan 12:34 /testcases/files/9_bytes +.rw-r--r-- 2.1M cassowary  1 Jan 12:34 /testcases/files/2_MiB .rw-r--r-- 9.2k cassowary  1 Jan 12:34 /testcases/files/9_KiB +.rw-r--r-- 3 cassowary  1 Jan 12:34 /testcases/files/3_bytes .rw-r--r-- 9.4M cassowary  1 Jan 12:34 /testcases/files/9_MiB +.rw-r--r-- 3.1k cassowary  1 Jan 12:34 /testcases/files/3_KiB diff --git a/xtests/files_star_lG_200 b/xtests/files_star_lG_200 new file mode 100644 index 0000000..ce99ce2 --- /dev/null +++ b/xtests/files_star_lG_200 @@ -0,0 +1,13 @@ +.rw-r--r-- 10 cassowary  1 Jan 12:34 /testcases/files/10_bytes .rw-r--r-- 1.0k cassowary  1 Jan 12:34 /testcases/files/1_KiB .rw-r--r-- 5.2M cassowary  1 Jan 12:34 /testcases/files/5_MiB +.rw-r--r-- 10k cassowary  1 Jan 12:34 /testcases/files/10_KiB .rw-r--r-- 1.0M cassowary  1 Jan 12:34 /testcases/files/1_MiB .rw-r--r-- 6 cassowary  1 Jan 12:34 /testcases/files/6_bytes +.rw-r--r-- 10M cassowary  1 Jan 12:34 /testcases/files/10_MiB .rw-r--r-- 2 cassowary  1 Jan 12:34 /testcases/files/2_bytes .rw-r--r-- 6.1k cassowary  1 Jan 12:34 /testcases/files/6_KiB +.rw-r--r-- 11 cassowary  1 Jan 12:34 /testcases/files/11_bytes .rw-r--r-- 2.0k cassowary  1 Jan 12:34 /testcases/files/2_KiB .rw-r--r-- 6.3M cassowary  1 Jan 12:34 /testcases/files/6_MiB +.rw-r--r-- 11k cassowary  1 Jan 12:34 /testcases/files/11_KiB .rw-r--r-- 2.1M cassowary  1 Jan 12:34 /testcases/files/2_MiB .rw-r--r-- 7 cassowary  1 Jan 12:34 /testcases/files/7_bytes +.rw-r--r-- 11M cassowary  1 Jan 12:34 /testcases/files/11_MiB .rw-r--r-- 3 cassowary  1 Jan 12:34 /testcases/files/3_bytes .rw-r--r-- 7.2k cassowary  1 Jan 12:34 /testcases/files/7_KiB +.rw-r--r-- 12 cassowary  1 Jan 12:34 /testcases/files/12_bytes .rw-r--r-- 3.1k cassowary  1 Jan 12:34 /testcases/files/3_KiB .rw-r--r-- 7.3M cassowary  1 Jan 12:34 /testcases/files/7_MiB +.rw-r--r-- 12k cassowary  1 Jan 12:34 /testcases/files/12_KiB .rw-r--r-- 3.1M cassowary  1 Jan 12:34 /testcases/files/3_MiB .rw-r--r-- 8 cassowary  1 Jan 12:34 /testcases/files/8_bytes +.rw-r--r-- 12M cassowary  1 Jan 12:34 /testcases/files/12_MiB .rw-r--r-- 4 cassowary  1 Jan 12:34 /testcases/files/4_bytes .rw-r--r-- 8.2k cassowary  1 Jan 12:34 /testcases/files/8_KiB +.rw-r--r-- 13 cassowary  1 Jan 12:34 /testcases/files/13_bytes .rw-r--r-- 4.1k cassowary  1 Jan 12:34 /testcases/files/4_KiB .rw-r--r-- 8.4M cassowary  1 Jan 12:34 /testcases/files/8_MiB +.rw-r--r-- 13k cassowary  1 Jan 12:34 /testcases/files/13_KiB .rw-r--r-- 4.2M cassowary  1 Jan 12:34 /testcases/files/4_MiB .rw-r--r-- 9 cassowary  1 Jan 12:34 /testcases/files/9_bytes +.rw-r--r-- 13M cassowary  1 Jan 12:34 /testcases/files/13_MiB .rw-r--r-- 5 cassowary  1 Jan 12:34 /testcases/files/5_bytes .rw-r--r-- 9.2k cassowary  1 Jan 12:34 /testcases/files/9_KiB +.rw-r--r-- 1 cassowary  1 Jan 12:34 /testcases/files/1_bytes .rw-r--r-- 5.1k cassowary  1 Jan 12:34 /testcases/files/5_KiB .rw-r--r-- 9.4M cassowary  1 Jan 12:34 /testcases/files/9_MiB diff --git a/xtests/run.sh b/xtests/run.sh index b234f16..12a5c2e 100755 --- a/xtests/run.sh +++ b/xtests/run.sh @@ -33,6 +33,10 @@ COLUMNS=120 $exa $testcases/files | diff -q - $results/files_120 || exit 1 COLUMNS=160 $exa $testcases/files | diff -q - $results/files_160 || exit 1 COLUMNS=200 $exa $testcases/files | diff -q - $results/files_200 || exit 1 +COLUMNS=100 $exa $testcases/files/* | diff -q - $results/files_star_100 || exit 1 +COLUMNS=150 $exa $testcases/files/* | diff -q - $results/files_star_150 || exit 1 +COLUMNS=200 $exa $testcases/files/* | diff -q - $results/files_star_200 || exit 1 + # Long grid view tests COLUMNS=40 $exa $testcases/files -lG | diff -q - $results/files_lG_40 || exit 1 @@ -41,6 +45,10 @@ COLUMNS=120 $exa $testcases/files -lG | diff -q - $results/files_lG_120 || exit COLUMNS=160 $exa $testcases/files -lG | diff -q - $results/files_lG_160 || exit 1 COLUMNS=200 $exa $testcases/files -lG | diff -q - $results/files_lG_200 || exit 1 +COLUMNS=100 $exa $testcases/files/* -lG | diff -q - $results/files_star_lG_100 || exit 1 +COLUMNS=150 $exa $testcases/files/* -lG | diff -q - $results/files_star_lG_150 || exit 1 +COLUMNS=200 $exa $testcases/files/* -lG | diff -q - $results/files_star_lG_200 || exit 1 + # Attributes $exa $testcases/attributes -l@T | diff -q - $results/attributes || exit 1 @@ -55,9 +63,12 @@ sudo -u cassowary $exa $testcases/permissions -lghR 2>&1 | diff -q - $results/pe $exa $testcases/permissions -lghR 2>&1 | diff -q - $results/permissions || exit 1 # File names +# (Mostly escaping control characters in file names) COLUMNS=80 $exa $testcases/file-names 2>&1 | diff -q - $results/file_names || exit 1 COLUMNS=80 $exa $testcases/file-names -x 2>&1 | diff -q - $results/file_names_x || exit 1 +COLUMNS=80 $exa $testcases/file-names -R 2>&1 | diff -q - $results/file_names_R || exit 1 $exa $testcases/file-names -1 2>&1 | diff -q - $results/file_names_1 || exit 1 + $exa $testcases/file-names -T 2>&1 | diff -q - $results/file_names_T || exit 1 # File types $exa $testcases/file-names-exts -1 2>&1 | diff -q - $results/file-names-exts || exit 1