std/sys/path/
unix.rs

1use crate::ffi::OsStr;
2use crate::path::{Path, PathBuf, Prefix};
3use crate::{env, io};
4
5#[inline]
6pub fn is_sep_byte(b: u8) -> bool {
7    b == b'/'
8}
9
10#[inline]
11pub fn is_verbatim_sep(b: u8) -> bool {
12    b == b'/'
13}
14
15#[inline]
16pub fn parse_prefix(_: &OsStr) -> Option<Prefix<'_>> {
17    None
18}
19
20pub const MAIN_SEP_STR: &str = "/";
21pub const MAIN_SEP: char = '/';
22
23/// Make a POSIX path absolute without changing its semantics.
24pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
25    // This is mostly a wrapper around collecting `Path::components`, with
26    // exceptions made where this conflicts with the POSIX specification.
27    // See 4.13 Pathname Resolution, IEEE Std 1003.1-2017
28    // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
29
30    // Get the components, skipping the redundant leading "." component if it exists.
31    let mut components = path.strip_prefix(".").unwrap_or(path).components();
32    let path_os = path.as_os_str().as_encoded_bytes();
33
34    let mut normalized = if path.is_absolute() {
35        // "If a pathname begins with two successive <slash> characters, the
36        // first component following the leading <slash> characters may be
37        // interpreted in an implementation-defined manner, although more than
38        // two leading <slash> characters shall be treated as a single <slash>
39        // character."
40        if path_os.starts_with(b"//") && !path_os.starts_with(b"///") {
41            components.next();
42            PathBuf::from("//")
43        } else {
44            PathBuf::new()
45        }
46    } else {
47        env::current_dir()?
48    };
49    normalized.extend(components);
50
51    // "Interfaces using pathname resolution may specify additional constraints
52    // when a pathname that does not name an existing directory contains at
53    // least one non- <slash> character and contains one or more trailing
54    // <slash> characters".
55    // A trailing <slash> is also meaningful if "a symbolic link is
56    // encountered during pathname resolution".
57    if path_os.ends_with(b"/") {
58        normalized.push("");
59    }
60
61    Ok(normalized)
62}
63
64pub(crate) fn is_absolute(path: &Path) -> bool {
65    if cfg!(any(unix, target_os = "hermit", target_os = "wasi")) {
66        path.has_root()
67    } else {
68        path.has_root() && path.prefix().is_some()
69    }
70}