-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathopen_in_explorer.cpp
More file actions
195 lines (179 loc) · 5.87 KB
/
Copy pathopen_in_explorer.cpp
File metadata and controls
195 lines (179 loc) · 5.87 KB
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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#include "open_in_explorer.hpp"
#include "../utils/utf8_path.hpp"
#include <system_error>
#ifdef _WIN32
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# ifndef NOMINMAX
# define NOMINMAX
# endif
# include <windows.h>
# include <shellapi.h>
#else
# include <cstdlib>
# include <unistd.h>
#endif
namespace fs = std::filesystem;
namespace acecode::desktop {
namespace {
bool platform_open_target(const fs::path& path,
OpenInExplorerTargetKind kind,
std::string& error) {
#ifdef _WIN32
const std::wstring wide_path = path.wstring();
HINSTANCE result = nullptr;
if (kind == OpenInExplorerTargetKind::File) {
const std::wstring parameters = L"/select,\"" + wide_path + L"\"";
const std::wstring working_directory = path.parent_path().wstring();
result = ::ShellExecuteW(
nullptr,
L"open",
L"explorer.exe",
parameters.c_str(),
working_directory.c_str(),
SW_SHOWNORMAL);
} else {
result = ::ShellExecuteW(
nullptr,
L"open",
wide_path.c_str(),
nullptr,
nullptr,
SW_SHOWNORMAL);
}
auto code = reinterpret_cast<intptr_t>(result);
if (code > 32) return true;
error = "ShellExecute failed: " + std::to_string(code);
return false;
#else
# ifdef __APPLE__
const char* opener = "open";
# else
const char* opener = "xdg-open";
# endif
const fs::path launch_path =
# ifdef __APPLE__
path;
# else
kind == OpenInExplorerTargetKind::File ? path.parent_path() : path;
# endif
const std::string native_path = acecode::path_to_utf8(launch_path);
pid_t pid = ::fork();
if (pid < 0) {
error = "fork failed";
return false;
}
if (pid == 0) {
# ifdef __APPLE__
if (kind == OpenInExplorerTargetKind::File) {
::execlp(opener, opener, "-R", native_path.c_str(), static_cast<char*>(nullptr));
::_exit(127);
}
# endif
::execlp(opener, opener, native_path.c_str(), static_cast<char*>(nullptr));
::_exit(127);
}
return true;
#endif
}
} // namespace
ValidatedOpenTarget validate_open_in_explorer_request(
const std::string& path_utf8) {
if (path_utf8.empty()) {
return {false, {}, OpenInExplorerTargetKind::Directory, "path required"};
}
std::error_code ec;
auto requested = acecode::path_from_utf8(path_utf8);
if (requested.empty()) {
return {false, {}, OpenInExplorerTargetKind::Directory, "path required"};
}
if (!requested.is_absolute()) {
return {false, {}, OpenInExplorerTargetKind::Directory, "path must be absolute"};
}
const bool requested_is_directory = fs::is_directory(requested, ec);
if (ec) {
return {false, {}, OpenInExplorerTargetKind::Directory, "failed to inspect path"};
}
const bool requested_is_file =
!requested_is_directory && fs::is_regular_file(requested, ec);
if (ec || (!requested_is_directory && !requested_is_file)) {
return {
false,
{},
OpenInExplorerTargetKind::Directory,
"path is not an existing file or directory",
};
}
auto canonical_path = fs::weakly_canonical(requested, ec);
if (ec || canonical_path.empty()) {
return {false, {}, OpenInExplorerTargetKind::Directory, "failed to resolve path"};
}
const bool canonical_is_directory = fs::is_directory(canonical_path, ec);
if (ec) {
return {false, {}, OpenInExplorerTargetKind::Directory, "failed to inspect path"};
}
const bool canonical_is_file =
!canonical_is_directory && fs::is_regular_file(canonical_path, ec);
if (ec || (!canonical_is_directory && !canonical_is_file)) {
return {
false,
{},
OpenInExplorerTargetKind::Directory,
"path is not an existing file or directory",
};
}
return {
true,
canonical_path,
canonical_is_file
? OpenInExplorerTargetKind::File
: OpenInExplorerTargetKind::Directory,
{},
};
}
ValidatedOpenDirectory validate_open_directory_request(
const std::string& path_utf8) {
auto validated = validate_open_in_explorer_request(path_utf8);
if (!validated.ok) return {false, {}, validated.error};
if (validated.kind != OpenInExplorerTargetKind::Directory) {
return {false, {}, "path is not an existing directory"};
}
return {true, std::move(validated.path), {}};
}
OpenInExplorerResult open_path_in_file_manager(
const std::string& path_utf8,
OpenInExplorerLauncher launcher) {
auto validated = validate_open_in_explorer_request(path_utf8);
if (!validated.ok) return {false, validated.error};
std::string error;
auto launch = launcher
? std::move(launcher)
: OpenInExplorerLauncher(platform_open_target);
if (!launch(validated.path, validated.kind, error)) {
if (error.empty()) error = "failed to open path in file manager";
return {false, error};
}
return {true, {}};
}
OpenInExplorerResult open_directory_in_file_manager(
const std::string& path_utf8,
DirectoryOpenLauncher launcher) {
auto validated = validate_open_directory_request(path_utf8);
if (!validated.ok) return {false, validated.error};
std::string error;
auto launch = launcher
? std::move(launcher)
: DirectoryOpenLauncher([](const fs::path& path, std::string& launch_error) {
return platform_open_target(
path,
OpenInExplorerTargetKind::Directory,
launch_error);
});
if (!launch(validated.path, error)) {
if (error.empty()) error = "failed to open directory";
return {false, error};
}
return {true, {}};
}
} // namespace acecode::desktop