Skip to content

Commit a5146e6

Browse files
committed
[windows] improve path handling for windows
1 parent c2be029 commit a5146e6

7 files changed

Lines changed: 92 additions & 33 deletions

File tree

src/base/fs_util.cc

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,13 +196,18 @@ determine_path_type(const std::string& arg)
196196
}
197197
}
198198

199-
std::filesystem::path
200-
to_posix_path(std::string arg)
199+
path_transcoder
200+
path_transcoder::from(std::string arg)
201201
{
202202
if (cget(arg, 1).value_or('\0') != ':') {
203203
return {arg};
204204
}
205205

206+
bool caps = isupper(arg[0]);
207+
if (caps) {
208+
arg[0] = tolower(arg[0]);
209+
}
210+
206211
switch (cget(arg, 2).value_or('\0')) {
207212
case '\\':
208213
case '/':
@@ -216,7 +221,38 @@ to_posix_path(std::string arg)
216221
arg.insert(arg.begin(), '/');
217222
std::replace(arg.begin(), arg.end(), '\\', '/');
218223

219-
return {arg};
224+
return {arg, caps};
225+
}
226+
227+
std::string
228+
path_transcoder::to_native(std::string arg)
229+
{
230+
if (!this->pt_root_name_capitalized) {
231+
return arg;
232+
}
233+
234+
arg.erase(0, 1);
235+
if (this->pt_root_name_capitalized.value()) {
236+
arg[0] = toupper(arg[0]);
237+
}
238+
arg.insert(1, ":");
239+
std::replace(arg.begin(), arg.end(), '/', '\\');
240+
241+
return arg;
242+
}
243+
244+
std::string
245+
path_transcoder::to_shell_arg(std::string arg)
246+
{
247+
static const auto plain_path_re
248+
= lnav::pcre2pp::code::from_const(R"(^[\w/]+$)");
249+
250+
if (plain_path_re.find_in(arg).ignore_error()) {
251+
return arg;
252+
}
253+
254+
// XXX
255+
return fmt::format(FMT_STRING("'{}'"), arg);
220256
}
221257

222258
std::pair<std::string, file_location_t>

src/base/fs_util.hh

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,15 @@ std::string escape_path(const std::filesystem::path& p,
7575

7676
path_type determine_path_type(const std::string& arg);
7777

78-
std::filesystem::path to_posix_path(std::string arg);
78+
struct path_transcoder {
79+
static path_transcoder from(std::string arg);
80+
81+
std::filesystem::path pt_path;
82+
std::optional<bool> pt_root_name_capitalized;
83+
84+
std::string to_native(std::string arg);
85+
static std::string to_shell_arg(std::string arg);
86+
};
7987

8088
std::pair<std::string, file_location_t> split_file_location(
8189
const std::string& path);

src/base/fs_util.tests.cc

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,26 @@
3737

3838
TEST_CASE("fs_util::to_posix_path")
3939
{
40-
CHECK("/c/foo/bar" == lnav::filesystem::to_posix_path("c:\\foo\\bar"));
40+
auto pt = lnav::filesystem::path_transcoder::from("c:\\foo\\bar");
41+
CHECK("/c/foo/bar" == pt.pt_path);
42+
CHECK_FALSE(pt.pt_root_name_capitalized.value());
4143

42-
CHECK("/c/" == lnav::filesystem::to_posix_path("c:"));
44+
pt = lnav::filesystem::path_transcoder::from("C:\\foo\\bar");
45+
CHECK("/c/foo/bar" == pt.pt_path);
46+
CHECK(pt.pt_root_name_capitalized.value());
4347

44-
CHECK("/c/" == lnav::filesystem::to_posix_path("c:\\"));
48+
pt = lnav::filesystem::path_transcoder::from("c:");
49+
CHECK("/c/" == pt.pt_path);
50+
51+
pt = lnav::filesystem::path_transcoder::from("c:\\");
52+
CHECK("/c/" == pt.pt_path);
4553

4654
// XXX what should this be?
47-
CHECK("/c/foo/bar" == lnav::filesystem::to_posix_path("c:foo\\bar"));
55+
pt = lnav::filesystem::path_transcoder::from("c:foo\\bar");
56+
CHECK("/c/foo/bar" == pt.pt_path);
4857

49-
CHECK("" == lnav::filesystem::to_posix_path(""));
58+
pt = lnav::filesystem::path_transcoder::from("");
59+
CHECK("" == pt.pt_path);
5060
}
5161

5262
TEST_CASE("fs_util::build_path")

src/cmds.io.cc

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -835,11 +835,8 @@ com_open(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
835835
return ec.make_error("expecting file name to open");
836836
}
837837

838-
std::vector<std::string> word_exp;
839-
std::string pat;
840838
file_collection fc;
841-
842-
pat = trim(remaining_args(cmdline, args));
839+
auto pat = trim(remaining_args(cmdline, args));
843840

844841
shlex lexer(pat);
845842
auto split_args_res = lexer.split(ec.create_resolver());
@@ -1367,11 +1364,7 @@ com_xopen(exec_context& ec, std::string cmdline, std::vector<std::string>& args)
13671364
return ec.make_error("expecting file name to open");
13681365
}
13691366

1370-
std::vector<std::string> word_exp;
1371-
std::string pat;
1372-
file_collection fc;
1373-
1374-
pat = trim(remaining_args(cmdline, args));
1367+
auto pat = trim(remaining_args(cmdline, args));
13751368

13761369
shlex lexer(pat);
13771370
auto split_args_res = lexer.split(ec.create_resolver());

src/lnav.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3580,7 +3580,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
35803580
auto_mem<char> abspath;
35813581
struct stat st;
35823582

3583-
auto file_path = lnav::filesystem::to_posix_path(
3583+
auto file_path = std::filesystem::path(
35843584
stat(file_path_without_trailer.c_str(), &st) == 0
35853585
? file_path_without_trailer
35863586
: file_path_str);

src/lnav.prompt.cc

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,7 @@ prompt::get_cmd_parameter_completion(textview_curses& tc,
970970
return this->get_env_completion(str);
971971
}
972972

973+
auto str_as_path = lnav::filesystem::path_transcoder::from(str);
973974
std::set<std::string> poss_paths;
974975

975976
auto rp_opt = humanize::network::path::from_str(str);
@@ -996,8 +997,7 @@ prompt::get_cmd_parameter_completion(textview_curses& tc,
996997
poss_paths.emplace(poss_rpath);
997998
}
998999
} else {
999-
auto str_as_path = lnav::filesystem::to_posix_path(str);
1000-
auto parent = str_as_path.parent_path();
1000+
auto parent = str_as_path.pt_path.parent_path();
10011001
std::error_code ec;
10021002

10031003
log_trace("not a remote path: %s", str.c_str());
@@ -1018,33 +1018,41 @@ prompt::get_cmd_parameter_completion(textview_curses& tc,
10181018
std::filesystem::directory_iterator(parent, ec))
10191019
{
10201020
auto path_str = entry.path().string();
1021+
log_debug(" entry: %s", path_str.c_str());
10211022
if (entry.is_directory()) {
10221023
path_str.push_back('/');
10231024
} else if (ht->ht_format
10241025
== help_parameter_format_t::HPF_DIRECTORY)
10251026
{
10261027
continue;
10271028
}
1029+
path_str = str_as_path.to_native(path_str);
10281030
poss_paths.emplace(std::move(path_str));
10291031
}
10301032
if (ht->ht_format == help_parameter_format_t::HPF_DIRECTORY
10311033
&& !ec)
10321034
{
1033-
poss_paths.emplace(parent.string() + "/");
1035+
auto path_str = parent.string();
1036+
path_str.push_back('/');
1037+
path_str = str_as_path.to_native(path_str);
1038+
poss_paths.emplace(path_str);
10341039
}
10351040
}
10361041

1037-
retval = poss_paths | lnav::itertools::similar_to(str, 10)
1038-
| lnav::itertools::map([&str](const auto& path_str) {
1039-
auto escaped_path = shlex::escape(path_str);
1040-
if (!endswith(path_str, "/") || path_str == str) {
1041-
escaped_path.push_back(' ');
1042-
}
1043-
return attr_line_t()
1044-
.append(path_str)
1045-
.with_attr_for_all(
1046-
SUBST_TEXT.value(escaped_path));
1047-
});
1042+
retval
1043+
= poss_paths | lnav::itertools::similar_to(str, 10)
1044+
| lnav::itertools::map([&str](const std::string& path_str) {
1045+
auto escaped_path = shlex::escape(path_str);
1046+
if (path_str.find_last_of("/\\") == std::string::npos
1047+
|| path_str == str)
1048+
{
1049+
escaped_path.push_back(' ');
1050+
}
1051+
return attr_line_t()
1052+
.append(path_str)
1053+
.with_attr_for_all(
1054+
SUBST_TEXT.value(escaped_path));
1055+
});
10481056
break;
10491057
}
10501058
case help_parameter_format_t::HPF_LOADED_FILE: {

src/shlex.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,11 @@ shlex::tokenize()
6666
retval.tr_frag.sf_string = this->s_str;
6767
while (this->s_index < this->s_len) {
6868
switch (this->s_str[this->s_index]) {
69+
#if defined(__MSYS__)
70+
case '`':
71+
#else
6972
case '\\':
73+
#endif
7074
retval.tr_frag.sf_begin = this->s_index;
7175
if (this->s_index + 1 < this->s_len) {
7276
retval.tr_token = shlex_token_t::escape;

0 commit comments

Comments
 (0)