Repository: rust-lang/rust
File: library/std/src/sys/pal/uefi/os.rs (Rust)

Code before:
                .ok_or(io::const_error!(io::ErrorKind::InvalidData, "invalid path"))
        }
        None => {
            let mut t = current_exe()?;
            // SAFETY: This should never fail since the disk prefix will be present even for root
            // executables
            assert!(t.pop());
            Ok(t)
        }
    }
}

pub fn chdir(p: &path::Path) -> io::Result<()> {
    let shell = helpers::open_shell().ok_or(unsupported_err())?;

    let mut p = helpers::os_string_to_raw(p.as_os_str())
        .ok_or(io::const_error!(io::ErrorKind::InvalidData, "invalid path"))?;

    let r = unsafe { ((*shell.as_ptr()).set_cur_dir)(crate::ptr::null_mut(), p.as_mut_ptr()) };
    if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}

pub struct SplitPaths<'a>(crate::os::uefi::ffi::EncodeWide<'a>);

pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
    SplitPaths(unparsed.encode_wide())
}

impl<'a> Iterator for SplitPaths<'a> {
    type Item = PathBuf;
    fn next(&mut self) -> Option<PathBuf> {
        let mut buf = Vec::new();

        loop {
            match self.0.next() {
                // Ignore consecutive separators
                Some(ch) if ch == PATHS_SEP && buf.is_empty() => {}
                Some(ch) if ch == PATHS_SEP => break,
                Some(ch) => buf.push(ch),
                None if buf.is_empty() => return None,
                None => break,
            }
        }

        Some(PathBuf::from(OsString::from_wide(&buf)))
    }
}

#[derive(Debug)]
pub struct JoinPathsError;


Review comment (suggestion):
Using the `split_paths_unix` and `split_paths_windows` tests for comparison, I noticed an issue with the way the UEFI implementation handles empty paths. The Unix and Windows implementations map `""` to `[""]` and `";;"` to `["", "", ""]`, but the UEFI implementation returns an empty iterator for both. We should probably make the UEFI behavior match Unix/Windows here.

Code after:
            let mut t = current_exe()?;
            // SAFETY: This should never fail since the disk prefix will be present even for root
            // executables
            assert!(t.pop());
            Ok(t)
        }
    }
}

pub fn chdir(p: &path::Path) -> io::Result<()> {
    let shell = helpers::open_shell().ok_or(unsupported_err())?;

    let mut p = helpers::os_string_to_raw(p.as_os_str())
        .ok_or(io::const_error!(io::ErrorKind::InvalidData, "invalid path"))?;

    let r = unsafe { ((*shell.as_ptr()).set_cur_dir)(crate::ptr::null_mut(), p.as_mut_ptr()) };
    if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
}

pub struct SplitPaths<'a> {
    data: crate::os::uefi::ffi::EncodeWide<'a>,
    must_yield: bool,
}

pub fn split_paths(unparsed: &OsStr) -> SplitPaths<'_> {
    SplitPaths { data: unparsed.encode_wide(), must_yield: true }
}

impl<'a> Iterator for SplitPaths<'a> {
    type Item = PathBuf;

    fn next(&mut self) -> Option<PathBuf> {
        let must_yield = self.must_yield;
        self.must_yield = false;

        let mut in_progress = Vec::new();
        for b in self.data.by_ref() {
            if b == PATHS_SEP {
                self.must_yield = true;
                break;
            } else {
                in_progress.push(b)
            }
        }

        if !must_yield && in_progress.is_empty() {
            None
        } else {
            Some(PathBuf::from(OsString::from_wide(&in_progress)))
        }
    }