Improve working with file permissions (#591)

* Improve working with file permissions

Implements:

- xplr.util.permissions_rwx
- xplr.util.permissions_octal

* Edit permissions
pull/592/head
Arijit Basu 1 year ago committed by GitHub
parent 0b03fda363
commit 670f6596f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -49,109 +49,65 @@ of [modes][4] and the key mappings for each mode.
| ~ | | go home |
| [0-9] | | input |
### debug_error
| key | remaps | action |
| ----- | ------ | ------------------- |
| enter | | open logs in editor |
| q | | quit |
### sort
| key | remaps | action |
| --------- | ------ | --------------------------------- |
| ! | | reverse sorters |
| C | | by created reverse |
| E | | by canonical extension reverse |
| L | | by last modified reverse |
| M | | by canonical mime essence reverse |
| N | | by node type reverse |
| R | | by relative path reverse |
| S | | by size reverse |
| backspace | | remove last sorter |
| c | | by created |
| ctrl-r | | reset sorters |
| ctrl-u | | clear sorters |
| e | | by canonical extension |
| enter | | submit |
| l | | by last modified |
| m | | by canonical mime essence |
| n | | by node type |
| r | | by relative path |
| s | | by size |
### relative_path_does_not_match_regex
| key | remaps | action |
| ----- | ------ | ------ |
| enter | | submit |
### switch_layout
| key | remaps | action |
| --- | ------ | -------------------- |
| 1 | | default |
| 2 | | no help menu |
| 3 | | no selection panel |
| 4 | | no help or selection |
### recover
### filter
| key | remaps | action |
| --- | ------ | ------ |
| key | remaps | action |
| --------- | ------ | ---------------------------------- |
| R | | relative path does not match regex |
| backspace | | remove last filter |
| ctrl-r | | reset filters |
| ctrl-u | | clear filters |
| r | | relative path does match regex |
### vroot
### debug_error
| key | remaps | action |
| ------ | ------ | ------------ |
| . | | vroot $PWD |
| / | | vroot / |
| ctrl-r | | reset vroot |
| ctrl-u | | unset vroot |
| v | | toggle vroot |
| ~ | | vroot $HOME |
| key | remaps | action |
| ----- | ------ | ------------------- |
| enter | | open logs in editor |
| q | | quit |
### go_to_path
### number
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
| key | remaps | action |
| ----- | ------ | -------- |
| down | j | to down |
| enter | | to index |
| k | up | to up |
| [0-9] | | input |
### rename
### create_directory
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### selection_ops
| key | remaps | action |
| --- | ------ | --------------- |
| c | | copy here |
| e | | edit selection |
| h | | hardlink here |
| l | | list selection |
| m | | move here |
| s | | softlink here |
| u | | clear selection |
### create_file
### go_to
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
| key | remaps | action |
| --- | ------ | -------------- |
| f | | follow symlink |
| g | | top |
| i | | initial $PWD |
| p | | path |
| x | | open in gui |
### delete
### switch_layout
| key | remaps | action |
| --- | ------ | ------------ |
| D | | force delete |
| d | | delete |
| key | remaps | action |
| --- | ------ | -------------------- |
| 1 | | default |
| 2 | | no help menu |
| 3 | | no selection panel |
| 4 | | no help or selection |
### create_directory
### rename
| key | remaps | action |
| ----- | ------ | ------------ |
@ -165,29 +121,12 @@ of [modes][4] and the key mappings for each mode.
| d | | create directory |
| f | | create file |
### action
| key | remaps | action |
| ----- | ------ | -------------------- |
| ! | | shell |
| c | | create |
| e | | open in editor |
| l | | logs |
| m | | toggle mouse |
| q | | quit options |
| s | | selection operations |
| v | | vroot |
| [0-9] | | go to index |
### filter
### duplicate_as
| key | remaps | action |
| --------- | ------ | ---------------------------------- |
| R | | relative path does not match regex |
| backspace | | remove last filter |
| ctrl-r | | reset filters |
| ctrl-u | | clear filters |
| r | | relative path does match regex |
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### search
@ -206,28 +145,94 @@ of [modes][4] and the key mappings for each mode.
| right | | enter |
| tab | | toggle selection |
### duplicate_as
### action
| key | remaps | action |
| ----- | ------ | -------------------- |
| ! | | shell |
| c | | create |
| e | | open in editor |
| l | | logs |
| m | | toggle mouse |
| p | | edit permissions |
| q | | quit options |
| s | | selection operations |
| v | | vroot |
| [0-9] | | go to index |
### create_file
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### go_to
| key | remaps | action |
| --- | ------ | -------------- |
| f | | follow symlink |
| g | | top |
| i | | initial $PWD |
| p | | path |
| x | | open in gui |
### relative_path_does_match_regex
### edit_permissions
| key | remaps | action |
| ----- | ------ | ------ |
| G | | -group |
| O | | -other |
| U | | -user |
| enter | | submit |
| g | | +group |
| o | | +other |
| u | | +user |
### sort
| key | remaps | action |
| --------- | ------ | --------------------------------- |
| ! | | reverse sorters |
| C | | by created reverse |
| E | | by canonical extension reverse |
| L | | by last modified reverse |
| M | | by canonical mime essence reverse |
| N | | by node type reverse |
| R | | by relative path reverse |
| S | | by size reverse |
| backspace | | remove last sorter |
| c | | by created |
| ctrl-r | | reset sorters |
| ctrl-u | | clear sorters |
| e | | by canonical extension |
| enter | | submit |
| l | | by last modified |
| m | | by canonical mime essence |
| n | | by node type |
| r | | by relative path |
| s | | by size |
### delete
| key | remaps | action |
| --- | ------ | ------------ |
| D | | force delete |
| d | | delete |
### selection_ops
| key | remaps | action |
| --- | ------ | --------------- |
| c | | copy here |
| e | | edit selection |
| h | | hardlink here |
| l | | list selection |
| m | | move here |
| s | | softlink here |
| u | | clear selection |
### go_to_path
| key | remaps | action |
| ----- | ------ | ------------ |
| enter | | submit |
| tab | | try complete |
### recover
| key | remaps | action |
| --- | ------ | ------ |
### quit
@ -239,11 +244,19 @@ of [modes][4] and the key mappings for each mode.
| r | | quit printing result |
| s | | quit printing selection |
### number
### relative_path_does_match_regex
| key | remaps | action |
| ----- | ------ | -------- |
| down | j | to down |
| enter | | to index |
| k | up | to up |
| [0-9] | | input |
| key | remaps | action |
| ----- | ------ | ------ |
| enter | | submit |
### vroot
| key | remaps | action |
| ------ | ------ | ------------ |
| . | | vroot $PWD |
| / | | vroot / |
| ctrl-r | | reset vroot |
| ctrl-u | | unset vroot |
| v | | toggle vroot |
| ~ | | vroot $HOME |

@ -143,6 +143,12 @@ The builtin vroot mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.builtin.edit_permissions
The builtin edit permissions mode.
Type: [Mode](https://xplr.dev/en/mode)
#### xplr.config.modes.custom
This is where you define custom modes.

@ -469,6 +469,38 @@ xplr.util.layout_replace(layout, "Table", "Selection")
-- }
```
### xplr.util.permissions_rwx
Convert [Permission][8] to rwxrwxrwx representation with special bits.
Type: function( [Permission][8] ) -> string
Example:
```lua
xplr.util.permissions_rwx({ user_read = true }))
-- "r--------"
xplr.util.permissions_rwx(app.focused_node.permission))
-- "rwxrwsrwT"
```
### xplr.util.permissions_octal
Convert [Permission][8] to octal representation.
Type: function( [Permission][8] ) -> { number, number, number, number }
Example:
```lua
xplr.util.permissions_octal({ user_read = true }))
-- { 0, 4, 0, 0 }
xplr.util.permissions_octal(app.focused_node.permission))
-- { 0, 7, 5, 4 }
```
[1]: https://xplr.dev/en/lua-function-calls#explorer-config
[2]: https://xplr.dev/en/lua-function-calls#node
[3]: https://xplr.dev/en/style
@ -476,3 +508,4 @@ xplr.util.layout_replace(layout, "Table", "Selection")
[5]: https://xplr.dev/en/lua-function-calls#node
[6]: https://xplr.dev/en/node-type
[7]: https://xplr.dev/en/node_types
[8]: https://xplr.dev/en/column-renderer#permission

@ -159,7 +159,7 @@ xplr.config.general.logs.error.style = { fg = "Red" }
xplr.config.general.table.header.cols = {
{ format = " index", style = {} },
{ format = "╭─── path", style = {} },
{ format = "permissions", style = {} },
{ format = "perm", style = {} },
{ format = "size", style = {} },
{ format = "modified", style = {} },
}
@ -1406,7 +1406,7 @@ xplr.config.modes.builtin.go_to_path = {
messages = {
{
BashExecSilently0 = [===[
PTH=${XPLR_INPUT_BUFFER}
PTH="$XPLR_INPUT_BUFFER"
PTH_ESC=$(printf %q "$PTH")
if [ -d "$PTH" ]; then
"$XPLR" -m 'ChangeDirectory: %q' "$PTH"
@ -2045,6 +2045,19 @@ xplr.config.modes.builtin.action = {
"ToggleMouse",
},
},
["p"] = {
help = "edit permissions",
messages = {
"PopMode",
{ SwitchModeBuiltin = "edit_permissions" },
{
BashExecSilently0 = [===[
PERM=$(stat -c '%a' -- "${XPLR_FOCUS_PATH:?}")
"$XPLR" -m 'SetInputBuffer: %q' "${PERM:?}"
]===],
},
},
},
["v"] = {
help = "vroot",
messages = {
@ -2594,6 +2607,100 @@ xplr.config.modes.builtin.vroot = {
},
}
-- The builtin edit permissions mode.
--
-- Type: [Mode](https://xplr.dev/en/mode)
xplr.config.modes.builtin.edit_permissions = {
name = "edit permissions",
key_bindings = {
on_key = {
["u"] = {
help = "+user",
messages = {
{
BashExecSilently0 = [===[
PERM=$(("${XPLR_INPUT_BUFFER:?}"+100))
"$XPLR" -m 'SetInputBuffer: %q' "${PERM:?}"
]===],
},
},
},
["U"] = {
help = "-user",
messages = {
{
BashExecSilently0 = [===[
PERM=$(("${XPLR_INPUT_BUFFER:?}"-100))
"$XPLR" -m 'SetInputBuffer: %q' "${PERM:?}"
]===],
},
},
},
["g"] = {
help = "+group",
messages = {
{
BashExecSilently0 = [===[
PERM=$(("${XPLR_INPUT_BUFFER:?}"+10))
"$XPLR" -m 'SetInputBuffer: %q' "${PERM:?}"
]===],
},
},
},
["G"] = {
help = "-group",
messages = {
{
BashExecSilently0 = [===[
PERM=$(("${XPLR_INPUT_BUFFER:?}"-10))
"$XPLR" -m 'SetInputBuffer: %q' "${PERM:?}"
]===],
},
},
},
["o"] = {
help = "+other",
messages = {
{
BashExecSilently0 = [===[
PERM=$(("${XPLR_INPUT_BUFFER:?}"+1))
"$XPLR" -m 'SetInputBuffer: %q' "${PERM:?}"
]===],
},
},
},
["O"] = {
help = "-other",
messages = {
{
BashExecSilently0 = [===[
PERM=$(("${XPLR_INPUT_BUFFER:?}"-1))
"$XPLR" -m 'SetInputBuffer: %q' "${PERM:?}"
]===],
},
},
},
["enter"] = {
help = "submit",
messages = {
{
BashExecSilently0 = [===[
chmod "${XPLR_INPUT_BUFFER:?}" -- "${XPLR_FOCUS_PATH:?}"
]===],
},
"PopMode",
"ExplorePwdAsync",
},
},
},
default = {
messages = {
"UpdateInputBufferFromKey",
},
},
},
}
-- This is where you define custom modes.
--
-- Type: mapping of the following key-value pairs:
@ -2759,62 +2866,23 @@ end
-- Renders the third column in the table
xplr.fn.builtin.fmt_general_table_row_cols_2 = function(m)
local green = { fg = "Green" }
local yellow = { fg = "Yellow" }
local red = { fg = "Red" }
local function bit(x, color, cond)
if cond then
return xplr.util.paint(x, color)
else
return xplr.util.paint("-", color)
end
end
local p = m.permissions
local r = ""
r = r .. bit("r", green, p.user_read)
r = r .. bit("w", yellow, p.user_write)
if p.user_execute == false and p.setuid == false then
r = r .. bit("-", red, p.user_execute)
elseif p.user_execute == true and p.setuid == false then
r = r .. bit("x", red, p.user_execute)
elseif p.user_execute == false and p.setuid == true then
r = r .. bit("S", red, p.user_execute)
else
r = r .. bit("s", red, p.user_execute)
end
r = r .. bit("r", green, p.group_read)
r = r .. bit("w", yellow, p.group_write)
if p.group_execute == false and p.setuid == false then
r = r .. bit("-", red, p.group_execute)
elseif p.group_execute == true and p.setuid == false then
r = r .. bit("x", red, p.group_execute)
elseif p.group_execute == false and p.setuid == true then
r = r .. bit("S", red, p.group_execute)
else
r = r .. bit("s", red, p.group_execute)
end
r = r .. bit("r", green, p.other_read)
r = r .. bit("w", yellow, p.other_write)
if p.other_execute == false and p.setuid == false then
r = r .. bit("-", red, p.other_execute)
elseif p.other_execute == true and p.setuid == false then
r = r .. bit("x", red, p.other_execute)
elseif p.other_execute == false and p.setuid == true then
r = r .. bit("T", red, p.other_execute)
else
r = r .. bit("t", red, p.other_execute)
end
return r
local r = xplr.util.paint("r", { fg = "Green" })
local w = xplr.util.paint("w", { fg = "Yellow" })
local x = xplr.util.paint("x", { fg = "Red" })
local s = xplr.util.paint("s", { fg = "Red" })
local S = xplr.util.paint("S", { fg = "Red" })
local t = xplr.util.paint("t", { fg = "Red" })
local T = xplr.util.paint("T", { fg = "Red" })
return xplr.util
.permissions_rwx(m.permissions)
:gsub("r", r)
:gsub("w", w)
:gsub("x", x)
:gsub("s", s)
:gsub("S", S)
:gsub("t", t)
:gsub("T", T)
end
-- Renders the fourth column in the table

@ -6,6 +6,8 @@ use crate::msg::in_::external::ExplorerConfig;
use crate::node::Node;
use crate::path;
use crate::path::RelativityConfig;
use crate::permissions::Octal;
use crate::permissions::Permissions;
use crate::ui;
use crate::ui::Layout;
use crate::ui::Style;
@ -776,6 +778,53 @@ pub fn layout_replace<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
Ok(util)
}
/// Convert [Permission][8] to rwxrwxrwx representation with special bits.
///
/// Type: function( [Permission][8] ) -> string
///
/// Example:
///
/// ```lua
/// xplr.util.permissions_rwx({ user_read = true }))
/// -- "r--------"
///
/// xplr.util.permissions_rwx(app.focused_node.permission))
/// -- "rwxrwsrwT"
/// ```
pub fn permissions_rwx<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
let func = lua.create_function(|lua, permission: Table| {
let permissions: Permissions = lua.from_value(Value::Table(permission))?;
let permissions = permissions.to_string();
Ok(permissions)
})?;
util.set("permissions_rwx", func)?;
Ok(util)
}
/// Convert [Permission][8] to octal representation.
///
/// Type: function( [Permission][8] ) -> { number, number, number, number }
///
/// Example:
///
/// ```lua
/// xplr.util.permissions_octal({ user_read = true }))
/// -- { 0, 4, 0, 0 }
///
/// xplr.util.permissions_octal(app.focused_node.permission))
/// -- { 0, 7, 5, 4 }
/// ```
pub fn permissions_octal<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
let func = lua.create_function(|lua, permission: Table| {
let permissions: Permissions = lua.from_value(Value::Table(permission))?;
let permissions: Octal = permissions.into();
let permissions = lua::serialize(lua, &permissions).map_err(LuaError::custom)?;
Ok(permissions)
})?;
util.set("permissions_octal", func)?;
Ok(util)
}
///
/// [1]: https://xplr.dev/en/lua-function-calls#explorer-config
/// [2]: https://xplr.dev/en/lua-function-calls#node
@ -784,6 +833,7 @@ pub fn layout_replace<'a>(util: Table<'a>, lua: &Lua) -> Result<Table<'a>> {
/// [5]: https://xplr.dev/en/lua-function-calls#node
/// [6]: https://xplr.dev/en/node-type
/// [7]: https://xplr.dev/en/node_types
/// [8]: https://xplr.dev/en/column-renderer#permission
pub(crate) fn create_table(lua: &Lua) -> Result<Table> {
let mut util = lua.create_table()?;
@ -816,6 +866,8 @@ pub(crate) fn create_table(lua: &Lua) -> Result<Table> {
util = style_mix(util, lua)?;
util = textwrap(util, lua)?;
util = layout_replace(util, lua)?;
util = permissions_rwx(util, lua)?;
util = permissions_octal(util, lua)?;
Ok(util)
}

@ -1,27 +1,52 @@
// Stolen from https://github.com/Peltoche/lsd/blob/master/src/meta/permissions.rs
use serde::{Deserialize, Serialize};
use std::fs::Metadata;
use std::{fmt::Display, fs::Metadata};
#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, Hash, Default)]
pub type RWX = (char, char, char, char, char, char, char, char, char);
pub type Octal = (u8, u8, u8, u8);
#[derive(Debug, Default, PartialEq, Eq, Copy, Clone, Serialize, Deserialize, Hash)]
pub struct Permissions {
#[serde(default)]
pub user_read: bool,
#[serde(default)]
pub user_write: bool,
#[serde(default)]
pub user_execute: bool,
#[serde(default)]
pub group_read: bool,
#[serde(default)]
pub group_write: bool,
#[serde(default)]
pub group_execute: bool,
#[serde(default)]
pub other_read: bool,
#[serde(default)]
pub other_write: bool,
#[serde(default)]
pub other_execute: bool,
#[serde(default)]
pub sticky: bool,
#[serde(default)]
pub setgid: bool,
#[serde(default)]
pub setuid: bool,
}
impl Permissions {}
impl<'a> From<&'a Metadata> for Permissions {
#[cfg(unix)]
fn from(meta: &Metadata) -> Self {
@ -55,6 +80,68 @@ impl<'a> From<&'a Metadata> for Permissions {
}
}
impl Display for Permissions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (ur, uw, ux, gr, gw, gx, or, ow, ox) = (*self).into();
write!(f, "{ur}{uw}{ux}{gr}{gw}{gx}{or}{ow}{ox}")
}
}
impl Into<RWX> for Permissions {
fn into(self) -> RWX {
let bit = |bit: bool, chr: char| {
if bit {
chr
} else {
'-'
}
};
let ur = bit(self.user_read, 'r');
let uw = bit(self.user_write, 'w');
let ux = match (self.user_execute, self.setuid) {
(true, true) => 's',
(true, false) => 'x',
(false, true) => 'S',
(false, false) => '-',
};
let gr = bit(self.group_read, 'r');
let gw = bit(self.group_write, 'w');
let gx = match (self.group_execute, self.setgid) {
(true, true) => 's',
(true, false) => 'x',
(false, true) => 'S',
(false, false) => '-',
};
let or = bit(self.other_read, 'r');
let ow = bit(self.other_write, 'w');
let ox = match (self.other_execute, self.sticky) {
(true, true) => 't',
(true, false) => 'x',
(false, true) => 'T',
(false, false) => '-',
};
(ur, uw, ux, gr, gw, gx, or, ow, ox)
}
}
impl Into<Octal> for Permissions {
fn into(self) -> Octal {
let bits_to_octal =
|r: bool, w: bool, x: bool| (r as u8) * 4 + (w as u8) * 2 + (x as u8);
(
bits_to_octal(self.setuid, self.setgid, self.sticky),
bits_to_octal(self.user_read, self.user_write, self.user_execute),
bits_to_octal(self.group_read, self.group_write, self.group_execute),
bits_to_octal(self.other_read, self.other_write, self.other_execute),
)
}
}
// More readable aliases for the permission bits exposed by libc.
#[allow(trivial_numeric_casts)]
#[cfg(unix)]

Loading…
Cancel
Save