Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for '~otheruser/directory' expansion on *nix systems #9

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ Cargo.lock
.*.sw?
.idea
*.iml
.mypy_cache
19 changes: 18 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,24 @@ repository = "https://github.com/netvl/shellexpand"
documentation = "http://docs.rs/shellexpand/"
readme = "Readme.md"
keywords = ["strings", "shell", "variables"]
edition = "2018"

[dependencies]
# nix
[target.'cfg(all(unix, not(target_os = "redox")))'.dependencies]
libc = { version = "0.2", optional = true }

# redox
[target.'cfg(target_os = "redox")'.dependencies]
redox_users = "0.3"

# windows
[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3", features = ["errhandlingapi", "handleapi", "processthreadsapi", "psapi", "securitybaseapi", "userenv", "winbase", "winerror"] }

[dev-dependencies]
dirs = "2.0"

[features]
default = ["libc"]


97 changes: 97 additions & 0 deletions scripts/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
#!/usr/bin/env python3

import subprocess
import sys
from typing import Dict, Set

# { os: (channel, [triple...]) }
OSS = {
"android": (
"stable",
["aarch64-linux-android", "armv7-linux-androideabi", "i686-linux-android",],
),
"freebsd": ("stable", ["x86_64-unknown-freebsd"]),
"linux": ("stable", ["x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl"]),
"macos": ("stable", ["x86_64-apple-darwin"]),
"redox": ("nightly", ["x86_64-unknown-redox"]),
"netbsd": ("stable", ["x86_64-unknown-netbsd"]),
"windows": ("stable", ["x86_64-pc-windows-gnu", "x86_64-pc-windows-msvc"]),
}

if sys.platform == "darwin":
OSS["ios"] = ("stable", ["aarch64-apple-ios", "x86_64-apple-ios",])


def blue(s: str) -> str:
return "\033[94m" + s + "\033[0m"


def get_installed_targets() -> Dict[str, Set[str]]:
installed: Dict[str, Set[str]] = {}
for channel in ["stable", "nightly"]:
installed[channel] = set()
command = f"rustup +{channel} target list"
completed = subprocess.run(command.split(), capture_output=True)
if completed.returncode != 0:
print(completed.stderr)
sys.exit(1)
for line in completed.stdout.splitlines():
split = line.split()
if len(split) == 2 and split[1].decode("utf-8") == "(installed)":
triple = split[0].decode("utf-8")
installed[channel].add(triple)
return installed


def run_command(command: str):
print(blue(command))
completed = subprocess.run(command.split())
if completed.returncode != 0:
sys.exit(1)


def run_cargo_check(channel: str, triple: str):
command = f"cargo +{channel} check --target={triple}"
run_command(command)


def run_rustup_target_install(channel: str, triple: str):
command = f"rustup +{channel} target install {triple}"
run_command(command)


def main():
installed = get_installed_targets()
if len(sys.argv) == 1:
for (channel, ts) in OSS.values():
for t in ts:
if t not in installed[channel]:
run_rustup_target_install(channel, t)
run_cargo_check(channel, t)
# check linux without libc
linux_triples = OSS["linux"][1]
for triple in linux_triples:
command = f"cargo check --target={triple} --no-default-features"
run_command(command)
else:
for os in sys.argv[1:]:
value = OSS.get(os)
if value is None:
available = str(list(OSS.keys()))
print(f"Unrecognized OS '{os}'. Must be one of: {available}.")
sys.exit(1)
(channel, ts) = value
for t in ts:
if t not in installed[channel]:
run_rustup_target_install(channel, t)
run_cargo_check(channel, t)
if "linux" in sys.argv[1]:
# check linux without libc
linux_triples = OSS["linux"][1]
for triple in linux_triples:
command = f"cargo check --target={triple} --no-default-features"
run_command(command)


if __name__ == "__main__":
main()
99 changes: 99 additions & 0 deletions src/home_dir/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* *******************
* OS-specific modules
* *******************
*/

// nix (with libc feature)
#[cfg(all(unix, not(target_os = "redox"), feature = "libc"))]
mod nix;

// redox
#[cfg(target_os = "redox")]
mod redox;

// windows
#[cfg(windows)]
mod windows;

// all others
#[cfg(not(any(
all(unix, not(target_os = "redox"), feature = "libc"),
target_os = "redox",
windows,
)))]
mod other;

/*
* *******************
* OS-specific exports
* *******************
*/

// nix (with libc feature)
#[cfg(all(unix, not(target_os = "redox"), feature = "libc"))]
pub(crate) use self::nix::home_dir;

// redox
#[cfg(target_os = "redox")]
pub(crate) use self::redox::home_dir;

// windows
#[cfg(windows)]
pub(crate) use self::windows::home_dir;

// all others
#[cfg(not(any(
all(unix, not(target_os = "redox"), feature = "libc"),
target_os = "redox",
windows,
)))]
pub(crate) use self::other::home_dir;

/*
* *******************
* Common
* *******************
*/

use std::error::Error;
use std::fmt;

/// Internal error type used for debugging. Not exposed publicly.
#[derive(Debug)]
pub(crate) struct HomeDirError(HomeDirErrorKind);

impl fmt::Display for HomeDirError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::HomeDirErrorKind::*;
match &self.0 {
NotFound(Some(user)) => write!(f, "Unable to find home directory for user {}", user),
NotFound(None) => write!(f, "Unable to find home directory for current user"),
OS(Some(msg)) => write!(f, "OS error while looking up home directory: {}", msg),
OS(None) => write!(f, "OS error while looking up home directory"),
PermissionDenied(user) => write!(f, "Permission denied. Reading home directory of {} requires elevated priviliges.", user),
Unimplemented => write!(f, "Identifying the home directory of a user other than the current user is not yet implemented for this platform"),
}
}
}

impl HomeDirError {
fn not_found(user: Option<&str>) -> Self {
let kind = HomeDirErrorKind::NotFound(user.map(|s| s.to_string()));
Self(kind)
}
}

impl Error for HomeDirError {}

#[derive(Debug)]
pub(crate) enum HomeDirErrorKind {
#[allow(unused)]
NotFound(Option<String>),
#[allow(unused)]
OS(Option<String>),
#[allow(unused)]
PermissionDenied(String),
#[allow(unused)]
Unimplemented,
}
Loading