Skip to content

Commit

Permalink
PR ajeetdsouza#609: Normalize drive letters when resolving paths on W…
Browse files Browse the repository at this point in the history
…indows
  • Loading branch information
mataha committed Jun 15, 2024
1 parent 7bc5e2b commit fcbb99a
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 14 deletions.
9 changes: 5 additions & 4 deletions src/cmd/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@ impl Run for Add {
let mut db = Database::open()?;

for path in &self.paths {
let path =
if config::resolve_symlinks() { util::canonicalize } else { util::resolve_path }(
path,
)?;
let path = util::patch_path(if config::resolve_symlinks() {
util::canonicalize
} else {
util::resolve_path
}(path)?);
let path = util::path_to_str(&path)?;

// Ignore path if it contains unsupported characters, or if it's in the exclude
Expand Down
51 changes: 41 additions & 10 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::ffi::OsStr;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Read, Write};
use std::path::{Component, Path, PathBuf};
use std::path::{Component, Path, PathBuf, Prefix};
use std::process::{Child, Command, Stdio};
use std::time::SystemTime;
use std::{env, mem};
Expand Down Expand Up @@ -263,6 +263,37 @@ pub fn path_to_str(path: &impl AsRef<Path>) -> Result<&str> {
path.to_str().with_context(|| format!("invalid unicode in path: {}", path.display()))
}

pub fn patch_path(path: PathBuf) -> PathBuf {
if cfg!(windows) {
fn patch_drive(drive_letter: u8) -> char {
drive_letter.to_ascii_uppercase() as char
}

let mut components = path.components();
match components.next() {
Some(Component::Prefix(prefix)) => {
let prefix = match prefix.kind() {
Prefix::Disk(drive_letter) => {
format!(r"{}:", patch_drive(drive_letter))
}
Prefix::VerbatimDisk(drive_letter) => {
format!(r"\\?\{}:", patch_drive(drive_letter))
}
_ => return path,
};

let mut path = PathBuf::default();
path.push(prefix);
path.extend(components);
path
}
_ => path,
}
} else {
path
}
}

/// Returns the absolute version of a path. Like
/// [`std::path::Path::canonicalize`], but doesn't resolve symlinks.
pub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {
Expand All @@ -274,8 +305,6 @@ pub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {

// initialize root
if cfg!(windows) {
use std::path::Prefix;

fn get_drive_letter(path: impl AsRef<Path>) -> Option<u8> {
let path = path.as_ref();
let mut components = path.components();
Expand All @@ -292,7 +321,7 @@ pub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {
}

fn get_drive_path(drive_letter: u8) -> PathBuf {
format!(r"{}:\", drive_letter as char).into()
format!(r"{}:\", drive_letter.to_ascii_uppercase() as char).into()
}

fn get_drive_relative(drive_letter: u8) -> Result<PathBuf> {
Expand All @@ -312,23 +341,25 @@ pub fn resolve_path(path: impl AsRef<Path>) -> Result<PathBuf> {
match components.peek() {
Some(Component::Prefix(prefix)) => match prefix.kind() {
Prefix::Disk(drive_letter) => {
let disk = components.next().unwrap();
components.next();
if components.peek() == Some(&Component::RootDir) {
let root = components.next().unwrap();
stack.push(disk);
stack.push(root);
components.next();
base_path = get_drive_path(drive_letter);
} else {
base_path = get_drive_relative(drive_letter)?;
stack.extend(base_path.components());
}

stack.extend(base_path.components());
}
Prefix::VerbatimDisk(drive_letter) => {
components.next();
if components.peek() == Some(&Component::RootDir) {
components.next();
base_path = get_drive_path(drive_letter);
} else {
bail!("illegal path: {}", path.display());
}

base_path = get_drive_path(drive_letter);
stack.extend(base_path.components());
}
_ => bail!("invalid path: {}", path.display()),
Expand Down

0 comments on commit fcbb99a

Please sign in to comment.