From 16046d57de460f6b5f22acc0e4148e5b7ea4d24a Mon Sep 17 00:00:00 2001 From: Lars Haalck Date: Fri, 24 Jul 2020 13:47:34 +0200 Subject: [PATCH] Add --octal-permissions argument Using --octal_permissions will insert another column before the existing permissions where permissions are encoded using octal values as requested in #316 --- src/fs/fields.rs | 5 ++ src/options/flags.rs | 7 ++- src/options/help.rs | 7 ++- src/options/view.rs | 3 +- src/output/render/mod.rs | 3 + src/output/render/octal.rs | 116 +++++++++++++++++++++++++++++++++++++ src/output/table.rs | 14 +++++ src/style/colours.rs | 2 + 8 files changed, 151 insertions(+), 6 deletions(-) create mode 100644 src/output/render/octal.rs diff --git a/src/fs/fields.rs b/src/fs/fields.rs index 87cebfc..3cde29d 100644 --- a/src/fs/fields.rs +++ b/src/fs/fields.rs @@ -87,6 +87,11 @@ pub struct PermissionsPlus { } +/// The permissions encoded as octal values +pub struct OctalPermissions { + pub permissions: Permissions, +} + /// A file’s number of hard links on the filesystem. /// /// Under Unix, a file can exist on the filesystem only once but appear in diff --git a/src/options/flags.rs b/src/options/flags.rs index 6979580..b4e24d2 100644 --- a/src/options/flags.rs +++ b/src/options/flags.rs @@ -60,8 +60,9 @@ pub static NO_USER: Arg = Arg { short: None, long: "no-user", takes_value: Takes pub static NO_TIME: Arg = Arg { short: None, long: "no-time", takes_value: TakesValue::Forbidden }; // optional feature options -pub static GIT: Arg = Arg { short: None, long: "git", takes_value: TakesValue::Forbidden }; -pub static EXTENDED: Arg = Arg { short: Some(b'@'), long: "extended", takes_value: TakesValue::Forbidden }; +pub static GIT: Arg = Arg { short: None, long: "git", takes_value: TakesValue::Forbidden }; +pub static EXTENDED: Arg = Arg { short: Some(b'@'), long: "extended", takes_value: TakesValue::Forbidden }; +pub static OCTAL: Arg = Arg { short: None, long: "octal-permissions", takes_value: TakesValue::Forbidden }; pub static ALL_ARGS: Args = Args(&[ @@ -77,5 +78,5 @@ pub static ALL_ARGS: Args = Args(&[ &BLOCKS, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, - &GIT, &EXTENDED, + &GIT, &EXTENDED, &OCTAL ]); diff --git a/src/options/help.rs b/src/options/help.rs index eb3927e..c934fea 100644 --- a/src/options/help.rs +++ b/src/options/help.rs @@ -56,8 +56,9 @@ LONG VIEW OPTIONS --no-user suppress the user field --no-time suppress the time field"##; -static GIT_HELP: &str = r##" --git list each file's Git status, if tracked or ignored"##; -static EXTENDED_HELP: &str = r##" -@, --extended list each file's extended attributes and sizes"##; +static GIT_HELP: &str = r##" --git list each file's Git status, if tracked or ignored"##; +static EXTENDED_HELP: &str = r##" -@, --extended list each file's extended attributes and sizes"##; +static OCTAL_HELP: &str = r##" --octal-permissions list each file's permission in octal format"##; /// All the information needed to display the help text, which depends @@ -119,6 +120,8 @@ impl fmt::Display for HelpString { write!(f, "\n{}", EXTENDED_HELP)?; } + write!(f, "\n{}", OCTAL_HELP)?; + Ok(()) } } diff --git a/src/options/view.rs b/src/options/view.rs index eaebecf..c79e151 100644 --- a/src/options/view.rs +++ b/src/options/view.rs @@ -228,12 +228,13 @@ impl Columns { let group = matches.has(&flags::GROUP)?; let inode = matches.has(&flags::INODE)?; let links = matches.has(&flags::LINKS)?; + let octal = matches.has(&flags::OCTAL)?; let permissions = !matches.has(&flags::NO_PERMISSIONS)?; let filesize = !matches.has(&flags::NO_FILESIZE)?; let user = !matches.has(&flags::NO_USER)?; - Ok(Columns { time_types, git, blocks, group, inode, links, permissions, filesize, user }) + Ok(Columns { time_types, git, octal, blocks, group, inode, links, permissions, filesize, user }) } } diff --git a/src/output/render/mod.rs b/src/output/render/mod.rs index f6248af..7bb1189 100644 --- a/src/output/render/mod.rs +++ b/src/output/render/mod.rs @@ -28,3 +28,6 @@ pub use self::times::Render as TimeRender; mod users; pub use self::users::Colours as UserColours; + +mod octal; +// octal uses just one colour diff --git a/src/output/render/octal.rs b/src/output/render/octal.rs new file mode 100644 index 0000000..7f8cf11 --- /dev/null +++ b/src/output/render/octal.rs @@ -0,0 +1,116 @@ +use ansi_term::Style; + +use crate::output::cell::TextCell; +use crate::fs::fields as f; + +impl f::OctalPermissions { + fn bits_to_octal(r: bool, w: bool, x: bool) -> u8 { + (r as u8) * 4 + (w as u8) * 2 + (x as u8) * 1 + } + + pub fn render(&self, style: Style) -> TextCell { + + let perm = &self.permissions; + let octal_sticky = Self::bits_to_octal(perm.setuid, perm.setgid, perm.sticky); + let octal_owner = Self::bits_to_octal(perm.user_read, perm.user_write, perm.user_execute); + let octal_group = Self::bits_to_octal(perm.group_read, perm.group_write, perm.group_execute); + let octal_other = Self::bits_to_octal(perm.other_read, perm.other_write, perm.other_execute); + + TextCell::paint(style, format!("{}{}{}{}", octal_sticky, octal_owner, octal_group, octal_other)) + } + +} + +#[cfg(test)] +pub mod test { + use crate::output::cell::TextCell; + use crate::fs::fields as f; + + use ansi_term::Colour::*; + + + #[test] + fn normal_folder() { + let bits = f::Permissions { + user_read: true, user_write: true, user_execute: true, setuid: false, + group_read: true, group_write: false, group_execute: true, setgid: false, + other_read: true, other_write: false, other_execute: true, sticky: false, + }; + + let octal = f::OctalPermissions{ permissions: bits }; + + let expected = TextCell::paint_str(Purple.bold(), "0755"); + assert_eq!(expected, octal.render(Purple.bold()).into()); + } + + #[test] + fn normal_file() { + let bits = f::Permissions { + user_read: true, user_write: true, user_execute: false, setuid: false, + group_read: true, group_write: false, group_execute: false, setgid: false, + other_read: true, other_write: false, other_execute: false, sticky: false, + }; + + let octal = f::OctalPermissions{ permissions: bits }; + + let expected = TextCell::paint_str(Purple.bold(), "0644"); + assert_eq!(expected, octal.render(Purple.bold()).into()); + } + + #[test] + fn secret_file() { + let bits = f::Permissions { + user_read: true, user_write: true, user_execute: false, setuid: false, + group_read: false, group_write: false, group_execute: false, setgid: false, + other_read: false, other_write: false, other_execute: false, sticky: false, + }; + + let octal = f::OctalPermissions{ permissions: bits }; + + let expected = TextCell::paint_str(Purple.bold(), "0600"); + assert_eq!(expected, octal.render(Purple.bold()).into()); + } + + #[test] + fn sticky1() { + let bits = f::Permissions { + user_read: true, user_write: true, user_execute: true, setuid: true, + group_read: true, group_write: true, group_execute: true, setgid: false, + other_read: true, other_write: true, other_execute: true, sticky: false, + }; + + let octal = f::OctalPermissions{ permissions: bits }; + + let expected = TextCell::paint_str(Purple.bold(), "4777"); + assert_eq!(expected, octal.render(Purple.bold()).into()); + + } + + #[test] + fn sticky2() { + let bits = f::Permissions { + user_read: true, user_write: true, user_execute: true, setuid: false, + group_read: true, group_write: true, group_execute: true, setgid: true, + other_read: true, other_write: true, other_execute: true, sticky: false, + }; + + let octal = f::OctalPermissions{ permissions: bits }; + + let expected = TextCell::paint_str(Purple.bold(), "2777"); + assert_eq!(expected, octal.render(Purple.bold()).into()); + } + + #[test] + fn sticky3() { + let bits = f::Permissions { + user_read: true, user_write: true, user_execute: true, setuid: false, + group_read: true, group_write: true, group_execute: true, setgid: false, + other_read: true, other_write: true, other_execute: true, sticky: true, + }; + + let octal = f::OctalPermissions{ permissions: bits }; + + let expected = TextCell::paint_str(Purple.bold(), "1777"); + assert_eq!(expected, octal.render(Purple.bold()).into()); + } +} diff --git a/src/output/table.rs b/src/output/table.rs index 90fb236..989bf9f 100644 --- a/src/output/table.rs +++ b/src/output/table.rs @@ -49,6 +49,7 @@ pub struct Columns { pub blocks: bool, pub group: bool, pub git: bool, + pub octal: bool, // Defaults to true: pub permissions: bool, @@ -64,6 +65,10 @@ impl Columns { columns.push(Column::Inode); } + if self.octal { + columns.push(Column::Octal); + } + if self.permissions { columns.push(Column::Permissions); } @@ -125,6 +130,7 @@ pub enum Column { HardLinks, Inode, GitStatus, + Octal, } /// Each column can pick its own **Alignment**. Usually, numbers are @@ -161,6 +167,7 @@ impl Column { Column::HardLinks => "Links", Column::Inode => "inode", Column::GitStatus => "Git", + Column::Octal => "Octal", } } } @@ -350,6 +357,12 @@ impl<'a, 'f> Table<'a> { } } + fn octal_permissions(&self, file: &File) -> f::OctalPermissions { + f::OctalPermissions { + permissions: file.permissions(), + } + } + fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell { use crate::output::table::TimeType::*; @@ -362,6 +375,7 @@ impl<'a, 'f> Table<'a> { Column::User => file.user().render(self.colours, &*self.env.lock_users()), Column::Group => file.group().render(self.colours, &*self.env.lock_users()), Column::GitStatus => self.git_status(file).render(self.colours), + Column::Octal => self.octal_permissions(file).render(self.colours.octal), Column::Timestamp(Modified) => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format), Column::Timestamp(Changed) => file.changed_time() .render(self.colours.date, &self.env.tz, &self.time_format), diff --git a/src/style/colours.rs b/src/style/colours.rs index 69e42e8..1addafe 100644 --- a/src/style/colours.rs +++ b/src/style/colours.rs @@ -23,6 +23,7 @@ pub struct Colours { pub inode: Style, pub blocks: Style, pub header: Style, + pub octal: Style, pub symlink_path: Style, pub control_char: Style, @@ -174,6 +175,7 @@ impl Colours { date: Blue.normal(), inode: Purple.normal(), blocks: Cyan.normal(), + octal: Purple.normal(), header: Style::default().underline(), symlink_path: Cyan.normal(),