mirror of
https://github.com/Llewellynvdm/exa.git
synced 2024-11-05 12:27:53 +00:00
Replare Unix module with users crate
Feels good turning all that code into a dependency!
This commit is contained in:
parent
6134096586
commit
6770ac5475
6
Cargo.lock
generated
6
Cargo.lock
generated
@ -3,6 +3,7 @@ name = "exa"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ansi_term 0.3.0 (git+https://github.com/ogham/rust-ansi-term.git)",
|
||||
"users 0.1.0 (git+https://github.com/ogham/rust-users)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -10,3 +11,8 @@ name = "ansi_term"
|
||||
version = "0.3.0"
|
||||
source = "git+https://github.com/ogham/rust-ansi-term.git#4b9ea6cf266053e1a771e75b935b4e54c586c139"
|
||||
|
||||
[[package]]
|
||||
name = "users"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ogham/rust-users#221a1463d3e25acac41615186a1c7fdcf0ad36d7"
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
[package]
|
||||
|
||||
name = "exa"
|
||||
version = "0.1.0"
|
||||
authors = [ "ogham@bsago.me" ]
|
||||
|
||||
[[bin]]
|
||||
|
||||
name = "exa"
|
||||
|
||||
[dependencies.ansi_term]
|
||||
git = "https://github.com/ogham/rust-ansi-term.git"
|
||||
|
||||
git = "https://github.com/ogham/rust-ansi-term.git"
|
||||
[dependencies.users]
|
||||
git = "https://github.com/ogham/rust-users.git"
|
29
src/exa.rs
29
src/exa.rs
@ -2,30 +2,31 @@
|
||||
extern crate regex;
|
||||
#[phase(plugin)] extern crate regex_macros;
|
||||
extern crate ansi_term;
|
||||
extern crate users;
|
||||
extern crate unicode;
|
||||
|
||||
use std::os;
|
||||
use std::io::fs;
|
||||
use std::io::FileType;
|
||||
use std::io::fs;
|
||||
use std::iter::AdditiveIterator;
|
||||
use std::os;
|
||||
use std::str::StrVector;
|
||||
|
||||
use file::File;
|
||||
use dir::Dir;
|
||||
use column::Column;
|
||||
use column::Alignment::Left;
|
||||
use column::Column;
|
||||
use dir::Dir;
|
||||
use file::File;
|
||||
use options::{Options, View};
|
||||
use unix::Unix;
|
||||
|
||||
use ansi_term::Style::Plain;
|
||||
use ansi_term::strip_formatting;
|
||||
|
||||
use users::OSUsers;
|
||||
|
||||
pub mod column;
|
||||
pub mod dir;
|
||||
pub mod format;
|
||||
pub mod file;
|
||||
pub mod filetype;
|
||||
pub mod unix;
|
||||
pub mod options;
|
||||
pub mod sort;
|
||||
pub mod term;
|
||||
@ -42,7 +43,7 @@ fn main() {
|
||||
fn exa(opts: &Options) {
|
||||
let mut dirs: Vec<String> = vec![];
|
||||
let mut files: Vec<File> = vec![];
|
||||
|
||||
|
||||
// Separate the user-supplied paths into directories and files.
|
||||
// Files are shown first, and then each directory is expanded
|
||||
// and listed second.
|
||||
@ -71,7 +72,7 @@ fn exa(opts: &Options) {
|
||||
if !files.is_empty() {
|
||||
view(opts, files);
|
||||
}
|
||||
|
||||
|
||||
for dir_name in dirs.into_iter() {
|
||||
if first {
|
||||
first = false;
|
||||
@ -121,7 +122,7 @@ fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
|
||||
let width = files.iter()
|
||||
.map(|f| f.name.len() + 2)
|
||||
.sum() - 2;
|
||||
|
||||
|
||||
if width <= console_width {
|
||||
let names: Vec<String> = files.iter()
|
||||
.map(|f| f.file_name().to_string())
|
||||
@ -140,7 +141,7 @@ fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
|
||||
if count % num_columns != 0 {
|
||||
num_rows += 1;
|
||||
}
|
||||
|
||||
|
||||
for y in range(0, num_rows) {
|
||||
for x in range(0, num_columns) {
|
||||
let num = if across {
|
||||
@ -149,11 +150,11 @@ fn grid_view(across: bool, console_width: uint, files: Vec<File>) {
|
||||
else {
|
||||
y + num_rows * x
|
||||
};
|
||||
|
||||
|
||||
if num >= count {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
let ref file = files[num];
|
||||
let file_name = file.name.clone();
|
||||
let styled_name = file.file_colour().paint(file_name.as_slice()).to_string();
|
||||
@ -175,7 +176,7 @@ fn details_view(options: &Options, columns: &Vec<Column>, files: Vec<File>) {
|
||||
// width of each column based on the length of the results and
|
||||
// padding the fields during output.
|
||||
|
||||
let mut cache = Unix::empty_cache();
|
||||
let mut cache = OSUsers::empty_cache();
|
||||
|
||||
let mut table: Vec<Vec<String>> = files.iter()
|
||||
.map(|f| columns.iter().map(|c| f.display(c, &mut cache)).collect())
|
||||
|
49
src/file.rs
49
src/file.rs
@ -5,10 +5,11 @@ use ansi_term::{ANSIString, Colour, Style};
|
||||
use ansi_term::Style::Plain;
|
||||
use ansi_term::Colour::{Red, Green, Yellow, Blue, Purple, Cyan, Fixed};
|
||||
|
||||
use users::{Users, OSUsers};
|
||||
|
||||
use column::Column;
|
||||
use column::Column::*;
|
||||
use format::{format_metric_bytes, format_IEC_bytes};
|
||||
use unix::Unix;
|
||||
use sort::SortPart;
|
||||
use dir::Dir;
|
||||
use filetype::HasType;
|
||||
@ -40,17 +41,17 @@ impl<'a> File<'a> {
|
||||
}
|
||||
|
||||
pub fn with_stat(stat: io::FileStat, path: Path, parent: Option<&'a Dir>) -> File<'a> {
|
||||
let v = path.filename().unwrap(); // fails if / or . or ..
|
||||
let v = path.filename().unwrap(); // fails if / or . or ..
|
||||
let filename = String::from_utf8(v.to_vec()).unwrap_or_else(|_| panic!("Name was not valid UTF-8"));
|
||||
|
||||
File {
|
||||
File {
|
||||
path: path.clone(),
|
||||
dir: parent,
|
||||
stat: stat,
|
||||
name: filename.clone(),
|
||||
ext: File::ext(filename.clone()),
|
||||
parts: SortPart::split_into_parts(filename.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn ext(name: String) -> Option<String> {
|
||||
@ -103,7 +104,7 @@ impl<'a> File<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn display(&self, column: &Column, unix: &mut Unix) -> String {
|
||||
pub fn display(&self, column: &Column, users_cache: &mut OSUsers) -> String {
|
||||
match *column {
|
||||
Permissions => {
|
||||
self.permissions_string()
|
||||
@ -141,18 +142,34 @@ impl<'a> File<'a> {
|
||||
// Display the ID if the user/group doesn't exist, which
|
||||
// usually means it was deleted but its files weren't.
|
||||
User => {
|
||||
let uid = self.stat.unstable.uid as u32;
|
||||
unix.load_user(uid);
|
||||
let user_name = unix.get_user_name(uid).unwrap_or(uid.to_string());
|
||||
let style = if unix.uid == uid { Yellow.bold() } else { Plain };
|
||||
let uid = self.stat.unstable.uid as i32;
|
||||
|
||||
let user_name = match users_cache.get_user_by_uid(uid) {
|
||||
Some(user) => user.name,
|
||||
None => uid.to_string(),
|
||||
};
|
||||
|
||||
let style = if users_cache.get_current_uid() == uid { Yellow.bold() } else { Plain };
|
||||
style.paint(user_name.as_slice()).to_string()
|
||||
},
|
||||
|
||||
Group => {
|
||||
let gid = self.stat.unstable.gid as u32;
|
||||
unix.load_group(gid);
|
||||
let group_name = unix.get_group_name(gid).unwrap_or(gid.to_string());
|
||||
let style = if unix.is_group_member(gid) { Yellow.normal() } else { Plain };
|
||||
let mut style = Plain;
|
||||
|
||||
let group_name = match users_cache.get_group_by_gid(gid) {
|
||||
Some(group) => {
|
||||
let current_uid = users_cache.get_current_uid();
|
||||
if let Some(current_user) = users_cache.get_user_by_uid(current_uid) {
|
||||
if current_user.primary_group == group.gid || group.members.contains(¤t_user.name) {
|
||||
style = Yellow.bold();
|
||||
}
|
||||
}
|
||||
group.name
|
||||
},
|
||||
None => gid.to_string(),
|
||||
};
|
||||
|
||||
style.paint(group_name.as_slice()).to_string()
|
||||
},
|
||||
}
|
||||
@ -164,10 +181,10 @@ impl<'a> File<'a> {
|
||||
if self.stat.kind == io::FileType::Symlink {
|
||||
match fs::readlink(&self.path) {
|
||||
Ok(path) => {
|
||||
let target_path = match self.dir {
|
||||
Some(dir) => dir.path.join(path),
|
||||
None => path,
|
||||
};
|
||||
let target_path = match self.dir {
|
||||
Some(dir) => dir.path.join(path),
|
||||
None => path,
|
||||
};
|
||||
format!("{} {}", displayed_name, self.target_file_name_and_arrow(target_path))
|
||||
}
|
||||
Err(_) => displayed_name.to_string(),
|
||||
|
141
src/unix.rs
141
src/unix.rs
@ -1,141 +0,0 @@
|
||||
use std::ptr::read;
|
||||
use std::ptr;
|
||||
use std::collections::HashMap;
|
||||
|
||||
mod c {
|
||||
#![allow(non_camel_case_types)]
|
||||
extern crate libc;
|
||||
pub use self::libc::{
|
||||
c_char,
|
||||
c_int,
|
||||
uid_t,
|
||||
gid_t,
|
||||
time_t
|
||||
};
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_passwd {
|
||||
pub pw_name: *const c_char, // login name
|
||||
pub pw_passwd: *const c_char,
|
||||
pub pw_uid: c_int, // user ID
|
||||
pub pw_gid: c_int, // group ID
|
||||
pub pw_change: time_t,
|
||||
pub pw_class: *const c_char,
|
||||
pub pw_gecos: *const c_char, // full name
|
||||
pub pw_dir: *const c_char, // login dir
|
||||
pub pw_shell: *const c_char, // login shell
|
||||
pub pw_expire: time_t, // password expiry time
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct c_group {
|
||||
pub gr_name: *const c_char, // group name
|
||||
pub gr_passwd: *const c_char, // password
|
||||
pub gr_gid: gid_t, // group id
|
||||
pub gr_mem: *const *const c_char, // names of users in the group
|
||||
}
|
||||
|
||||
extern {
|
||||
pub fn getpwuid(uid: c_int) -> *const c_passwd;
|
||||
pub fn getgrgid(gid: uid_t) -> *const c_group;
|
||||
pub fn getuid() -> libc::c_int;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Unix {
|
||||
user_names: HashMap<u32, Option<String>>, // mapping of user IDs to user names
|
||||
group_names: HashMap<u32, Option<String>>, // mapping of groups IDs to group names
|
||||
groups: HashMap<u32, bool>, // mapping of group IDs to whether the current user is a member
|
||||
pub uid: u32, // current user's ID
|
||||
pub username: String, // current user's name
|
||||
}
|
||||
|
||||
impl Unix {
|
||||
pub fn empty_cache() -> Unix {
|
||||
let uid = unsafe { c::getuid() };
|
||||
let infoptr = unsafe { c::getpwuid(uid as i32) };
|
||||
let info = unsafe { infoptr.as_ref().unwrap() }; // the user has to have a name
|
||||
|
||||
let username = unsafe { String::from_raw_buf(info.pw_name as *const u8) };
|
||||
|
||||
let mut user_names = HashMap::new();
|
||||
user_names.insert(uid as u32, Some(username.clone()));
|
||||
|
||||
// Unix groups work like this: every group has a list of
|
||||
// users, referred to by their names. But, every user also has
|
||||
// a primary group, which isn't in this list. So handle this
|
||||
// case immediately after we look up the user's details.
|
||||
let mut groups = HashMap::new();
|
||||
groups.insert(info.pw_gid as u32, true);
|
||||
|
||||
Unix {
|
||||
user_names: user_names,
|
||||
group_names: HashMap::new(),
|
||||
uid: uid as u32,
|
||||
username: username,
|
||||
groups: groups,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_user_name(&self, uid: u32) -> Option<String> {
|
||||
self.user_names[uid].clone()
|
||||
}
|
||||
|
||||
pub fn get_group_name(&self, gid: u32) -> Option<String> {
|
||||
self.group_names[gid].clone()
|
||||
}
|
||||
|
||||
pub fn is_group_member(&self, gid: u32) -> bool {
|
||||
self.groups[gid]
|
||||
}
|
||||
|
||||
pub fn load_user(&mut self, uid: u32) {
|
||||
let pw = unsafe { c::getpwuid(uid as i32) };
|
||||
if pw.is_not_null() {
|
||||
let username = unsafe { Some(String::from_raw_buf(read(pw).pw_name as *const u8)) };
|
||||
self.user_names.insert(uid, username);
|
||||
}
|
||||
else {
|
||||
self.user_names.insert(uid, None);
|
||||
}
|
||||
}
|
||||
|
||||
fn group_membership(group: *const *const c::c_char, uname: &String) -> bool {
|
||||
let mut i = 0;
|
||||
|
||||
// The list of members is a pointer to a pointer of
|
||||
// characters, terminated by a null pointer.
|
||||
loop {
|
||||
match unsafe { group.offset(i).as_ref() } {
|
||||
Some(&username) => {
|
||||
if username == ptr::null() {
|
||||
return false; // username was null, weird
|
||||
}
|
||||
else if unsafe { String::from_raw_buf(username as *const u8) } == *uname {
|
||||
return true; // group found!
|
||||
}
|
||||
else {
|
||||
i += 1; // try again with the next group
|
||||
}
|
||||
},
|
||||
None => return false, // no more groups to check, and none found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_group(&mut self, gid: u32) {
|
||||
match unsafe { c::getgrgid(gid).as_ref() } {
|
||||
None => {
|
||||
self.group_names.insert(gid, None);
|
||||
self.groups.insert(gid, false);
|
||||
},
|
||||
Some(r) => {
|
||||
let group_name = unsafe { Some(String::from_raw_buf(r.gr_name as *const u8)) };
|
||||
if !self.groups.contains_key(&gid) {
|
||||
self.groups.insert(gid, Unix::group_membership(r.gr_mem, &self.username));
|
||||
}
|
||||
self.group_names.insert(gid, group_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user