]> git.lizzy.rs Git - rust.git/blob - src/librustc_fs_util/lib.rs
Don't default on std crate when manipulating browser history
[rust.git] / src / librustc_fs_util / lib.rs
1 #![deny(rust_2018_idioms)]
2
3 use std::path::{Path, PathBuf};
4 use std::ffi::CString;
5 use std::fs;
6 use std::io;
7
8 // Unfortunately, on windows, it looks like msvcrt.dll is silently translating
9 // verbatim paths under the hood to non-verbatim paths! This manifests itself as
10 // gcc looking like it cannot accept paths of the form `\\?\C:\...`, but the
11 // real bug seems to lie in msvcrt.dll.
12 //
13 // Verbatim paths are generally pretty rare, but the implementation of
14 // `fs::canonicalize` currently generates paths of this form, meaning that we're
15 // going to be passing quite a few of these down to gcc, so we need to deal with
16 // this case.
17 //
18 // For now we just strip the "verbatim prefix" of `\\?\` from the path. This
19 // will probably lose information in some cases, but there's not a whole lot
20 // more we can do with a buggy msvcrt...
21 //
22 // For some more information, see this comment:
23 //   https://github.com/rust-lang/rust/issues/25505#issuecomment-102876737
24 #[cfg(windows)]
25 pub fn fix_windows_verbatim_for_gcc(p: &Path) -> PathBuf {
26     use std::path;
27     use std::ffi::OsString;
28     let mut components = p.components();
29     let prefix = match components.next() {
30         Some(path::Component::Prefix(p)) => p,
31         _ => return p.to_path_buf(),
32     };
33     match prefix.kind() {
34         path::Prefix::VerbatimDisk(disk) => {
35             let mut base = OsString::from(format!("{}:", disk as char));
36             base.push(components.as_path());
37             PathBuf::from(base)
38         }
39         path::Prefix::VerbatimUNC(server, share) => {
40             let mut base = OsString::from(r"\\");
41             base.push(server);
42             base.push(r"\");
43             base.push(share);
44             base.push(components.as_path());
45             PathBuf::from(base)
46         }
47         _ => p.to_path_buf(),
48     }
49 }
50
51 #[cfg(not(windows))]
52 pub fn fix_windows_verbatim_for_gcc(p: &Path) -> PathBuf {
53     p.to_path_buf()
54 }
55
56 pub enum LinkOrCopy {
57     Link,
58     Copy,
59 }
60
61 /// Copy `p` into `q`, preferring to use hard-linking if possible. If
62 /// `q` already exists, it is removed first.
63 /// The result indicates which of the two operations has been performed.
64 pub fn link_or_copy<P: AsRef<Path>, Q: AsRef<Path>>(p: P, q: Q) -> io::Result<LinkOrCopy> {
65     let p = p.as_ref();
66     let q = q.as_ref();
67     if q.exists() {
68         fs::remove_file(&q)?;
69     }
70
71     match fs::hard_link(p, q) {
72         Ok(()) => Ok(LinkOrCopy::Link),
73         Err(_) => {
74             match fs::copy(p, q) {
75                 Ok(_) => Ok(LinkOrCopy::Copy),
76                 Err(e) => Err(e),
77             }
78         }
79     }
80 }
81
82 #[derive(Debug)]
83 pub enum RenameOrCopyRemove {
84     Rename,
85     CopyRemove,
86 }
87
88 /// Rename `p` into `q`, preferring to use `rename` if possible.
89 /// If `rename` fails (rename may fail for reasons such as crossing
90 /// filesystem), fallback to copy & remove
91 pub fn rename_or_copy_remove<P: AsRef<Path>, Q: AsRef<Path>>(p: P,
92                                                              q: Q)
93                                                              -> io::Result<RenameOrCopyRemove> {
94     let p = p.as_ref();
95     let q = q.as_ref();
96     match fs::rename(p, q) {
97         Ok(()) => Ok(RenameOrCopyRemove::Rename),
98         Err(_) => {
99             match fs::copy(p, q) {
100                 Ok(_) => {
101                     fs::remove_file(p)?;
102                     Ok(RenameOrCopyRemove::CopyRemove)
103                 }
104                 Err(e) => Err(e),
105             }
106         }
107     }
108 }
109
110 #[cfg(unix)]
111 pub fn path_to_c_string(p: &Path) -> CString {
112     use std::os::unix::ffi::OsStrExt;
113     use std::ffi::OsStr;
114     let p: &OsStr = p.as_ref();
115     CString::new(p.as_bytes()).unwrap()
116 }
117 #[cfg(windows)]
118 pub fn path_to_c_string(p: &Path) -> CString {
119     CString::new(p.to_str().unwrap()).unwrap()
120 }