ktrs_cli/
java_launcher.rs1use crate::ktlint::java_glob::PathMatcher;
7
8pub fn application_args() -> Vec<String> {
10 let args: Vec<String> = std::env::args().skip(1).collect();
11 #[cfg(windows)]
12 {
13 expand_application_args(args, &windows::command_line())
14 }
15 #[cfg(not(windows))]
16 {
17 args
18 }
19}
20
21pub fn expand_application_args(args: Vec<String>, command_line: &str) -> Vec<String> {
24 let std_args = cmd_to_args(command_line);
25 if std_args.len() != args.len() + 1 {
26 return args;
27 }
28 args.into_iter()
29 .zip(std_args.into_iter().skip(1))
30 .flat_map(|(arg, (std_arg, has_wildcard))| if has_wildcard && std_arg == arg { expand_arg(&arg) } else { vec![arg] })
31 .collect()
32}
33
34fn cmd_to_args(command_line: &str) -> Vec<(String, bool)> {
36 let chars: Vec<char> = command_line.chars().collect();
37 let mut pos = 0;
38 let mut out = Vec::new();
39 loop {
40 let (arg, wildcard, more) = next_arg(&chars, &mut pos);
41 out.push((arg, wildcard));
42 if !more {
43 return out;
44 }
45 }
46}
47
48fn next_arg(chars: &[char], pos: &mut usize) -> (String, bool, bool) {
50 let mut dest = String::new();
51 let (mut separator, mut quotes, mut slashes, mut prev) = (false, 0i32, 0usize, '\0');
52 let mut wildcard = false;
53 let mut done = false;
54 let push_slashes = |dest: &mut String, n: usize| dest.extend(std::iter::repeat_n('\\', n));
55 while !done {
56 let Some(&ch) = chars.get(*pos) else { break };
57 match ch {
58 '"' => {
59 if separator {
60 done = true;
61 } else {
62 if prev == '\\' {
63 push_slashes(&mut dest, slashes / 2);
64 if slashes % 2 == 1 {
65 dest.push(ch);
66 } else {
67 quotes += 1;
68 }
69 } else if prev == '"' && quotes % 2 == 0 {
70 quotes += 1;
71 dest.push(ch);
72 } else if quotes == 0 {
73 quotes += 1;
74 } else {
75 quotes -= 1;
76 }
77 slashes = 0;
78 }
79 }
80 '\\' => {
81 slashes += 1;
82 if separator {
83 done = true;
84 separator = false;
85 }
86 }
87 ' ' | '\t' => {
88 if prev == '\\' {
89 push_slashes(&mut dest, slashes);
90 }
91 if quotes % 2 == 1 {
92 dest.push(ch);
93 } else {
94 separator = true;
95 }
96 slashes = 0;
97 }
98 '*' | '?' => {
99 if separator {
100 done = true;
101 separator = false;
102 } else {
103 if quotes % 2 == 0 {
104 wildcard = true;
105 }
106 if prev == '\\' {
107 push_slashes(&mut dest, slashes);
108 }
109 dest.push(ch);
110 slashes = 0;
111 }
112 }
113 _ => {
114 if prev == '\\' {
115 push_slashes(&mut dest, slashes);
116 dest.push(ch);
117 } else if separator {
118 done = true;
119 } else {
120 dest.push(ch);
121 }
122 slashes = 0;
123 }
124 }
125 if !done {
126 prev = ch;
127 *pos += 1;
128 }
129 }
130 if prev == '\\' {
131 push_slashes(&mut dest, slashes);
132 }
133 (dest, wildcard, done)
134}
135
136fn expand_arg(arg: &str) -> Vec<String> {
139 let file = windows_path::File::new(arg);
140 let parent = file.parent().unwrap_or_else(|| ".".to_owned());
141 let Some(dir) = windows_path::Path::parse(&parent) else { return vec![arg.to_owned()] };
142 let Ok(matcher) = PathMatcher::new(&file.name(), true) else { return vec![arg.to_owned()] };
143 let Ok(entries) = std::fs::read_dir(dir.to_string()) else { return vec![arg.to_owned()] };
144 let expanded: Vec<String> = entries
145 .flatten()
146 .map(|e| e.file_name().to_string_lossy().into_owned())
147 .filter(|name| matcher.matches(name))
148 .map(|name| dir.resolve(&name).normalize().to_string())
149 .collect();
150 if expanded.is_empty() { vec![arg.to_owned()] } else { expanded }
151}
152
153mod windows_path;
154
155#[cfg(windows)]
156mod windows {
157 #[link(name = "kernel32")]
158 unsafe extern "system" {
159 fn GetCommandLineW() -> *const u16;
160 }
161
162 pub fn command_line() -> String {
163 unsafe {
165 let p = GetCommandLineW();
166 let len = (0..).take_while(|&i| *p.add(i) != 0).count();
167 String::from_utf16_lossy(std::slice::from_raw_parts(p, len))
168 }
169 }
170}
171
172#[cfg(test)]
173mod tests;