1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#[cfg(windows)]
extern crate winapi;
#[cfg(not(target_os = "windows"))]
use std::process::{Command, Stdio};
use std::ffi::OsStr;
use std::io;
use std::process::ExitStatus;
#[cfg(not(any(target_os = "windows", target_os = "macos")))]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
let path_ref = path.as_ref();
let mut last_err: io::Error = io::Error::from_raw_os_error(0);
for program in &["xdg-open", "gnome-open", "kde-open"] {
match Command::new(program)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg(path_ref)
.spawn()
{
Ok(mut child) => return child.wait(),
Err(err) => {
last_err = err;
continue;
}
}
}
Err(last_err)
}
#[cfg(target_os = "windows")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
use std::os::windows::ffi::OsStrExt;
use std::os::windows::process::ExitStatusExt;
use std::ptr;
use winapi::ctypes::c_int;
use winapi::um::shellapi::ShellExecuteW;
const SW_SHOW: c_int = 5;
let path = windows::convert_path(path.as_ref())?;
let operation: Vec<u16> = OsStr::new("open\0").encode_wide().collect();
let result = unsafe {
ShellExecuteW(
ptr::null_mut(),
operation.as_ptr(),
path.as_ptr(),
ptr::null(),
ptr::null(),
SW_SHOW,
)
};
if result as c_int > 32 {
Ok(ExitStatus::from_raw(0))
} else {
Err(io::Error::last_os_error())
}
}
#[cfg(target_os = "macos")]
pub fn that<T: AsRef<OsStr> + Sized>(path: T) -> io::Result<ExitStatus> {
Command::new("open")
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg(path.as_ref())
.spawn()?
.wait()
}
#[cfg(windows)]
mod windows {
use std::ffi::OsStr;
use std::io;
use std::os::windows::ffi::OsStrExt;
pub fn convert_path(path: &OsStr) -> io::Result<Vec<u16>> {
let mut maybe_result: Vec<_> = path.encode_wide().collect();
if maybe_result.iter().any(|&u| u == 0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path contains NUL byte(s)",
));
}
maybe_result.push(0);
Ok(maybe_result)
}
}