16f50094aSMemoryShore use std::{
2c6454d32SMemoryShore cell::RefCell,
36f50094aSMemoryShore fs::{self, File, OpenOptions},
4cac674d3SMemoryShore io::{BufRead, BufReader, Read, Write},
5c6454d32SMemoryShore ops::Deref,
66f50094aSMemoryShore print,
75b859941SMemoryShore process::Child,
8c6454d32SMemoryShore rc::Rc,
96f50094aSMemoryShore };
106f50094aSMemoryShore
117bb802adSMemoryShore use crate::{
12*0b5cf83bSMemoryShore env::EnvManager,
137bb802adSMemoryShore keycode::{FunctionKeySuffix, SpecialKeycode},
147bb802adSMemoryShore parser::{Parser, Pipeline},
157bb802adSMemoryShore };
166f50094aSMemoryShore
17c6454d32SMemoryShore use colored::Colorize;
187bb802adSMemoryShore use command::BuildInCmd;
19cac674d3SMemoryShore use printer::Printer;
207bb802adSMemoryShore use thread_manager::ThreadManager;
21cac674d3SMemoryShore
22cac674d3SMemoryShore mod printer;
236f50094aSMemoryShore
247bb802adSMemoryShore mod thread_manager;
257bb802adSMemoryShore
266f50094aSMemoryShore pub mod command;
276f50094aSMemoryShore
28dcf45035SMemoryShore const DEFAULT_HISTORY_COMMANDS_PATH: &str = "/history_commands.txt";
29dcf45035SMemoryShore
30cac674d3SMemoryShore #[allow(dead_code)]
316f50094aSMemoryShore pub struct Shell {
32c6454d32SMemoryShore history_commands: Vec<Rc<RefCell<Vec<u8>>>>,
33c6454d32SMemoryShore history_path: String,
34c6454d32SMemoryShore printer: Printer,
35*0b5cf83bSMemoryShore backend_thread: ThreadManager<(String, Vec<Pipeline>), Child>,
366f50094aSMemoryShore }
376f50094aSMemoryShore
386f50094aSMemoryShore impl Shell {
new() -> Shell396f50094aSMemoryShore pub fn new() -> Shell {
407bb802adSMemoryShore if BuildInCmd::map().is_none() {
417bb802adSMemoryShore unsafe { BuildInCmd::init() };
427bb802adSMemoryShore }
437bb802adSMemoryShore
446f50094aSMemoryShore let mut shell = Shell {
456f50094aSMemoryShore history_commands: Vec::new(),
46dcf45035SMemoryShore history_path: DEFAULT_HISTORY_COMMANDS_PATH.to_string(),
47c6454d32SMemoryShore printer: Printer::new(&Rc::new(RefCell::new(Vec::new()))),
487bb802adSMemoryShore backend_thread: Self::create_backend_thread(),
496f50094aSMemoryShore };
506f50094aSMemoryShore shell.read_commands();
516f50094aSMemoryShore shell
526f50094aSMemoryShore }
536f50094aSMemoryShore
create_backend_thread() -> ThreadManager<(String, Vec<Pipeline>), Child>54*0b5cf83bSMemoryShore fn create_backend_thread() -> ThreadManager<(String, Vec<Pipeline>), Child> {
557bb802adSMemoryShore ThreadManager::new(|| {
56*0b5cf83bSMemoryShore let (p_s, c_r) = std::sync::mpsc::channel::<(String, Vec<Pipeline>)>();
577bb802adSMemoryShore let (c_s, p_r) = std::sync::mpsc::channel::<Child>();
587bb802adSMemoryShore let map = BuildInCmd::map();
597bb802adSMemoryShore let func = move || loop {
60*0b5cf83bSMemoryShore if let Ok((dir, pipelines)) = c_r.recv() {
61*0b5cf83bSMemoryShore std::env::set_current_dir(dir).expect("set current dir failed");
62*0b5cf83bSMemoryShore for pipeline in pipelines {
637bb802adSMemoryShore for child in pipeline.execute(map.clone()) {
647bb802adSMemoryShore let _ = c_s.send(child);
654d691f96S裕依2439 }
66*0b5cf83bSMemoryShore }
677bb802adSMemoryShore };
687bb802adSMemoryShore };
697bb802adSMemoryShore (p_s, p_r, func)
707bb802adSMemoryShore })
716f50094aSMemoryShore }
726f50094aSMemoryShore
exec(&mut self)736f50094aSMemoryShore pub fn exec(&mut self) {
74b0dea7c1SMemoryShore // 设置前台进程组
75b0dea7c1SMemoryShore unsafe {
76b0dea7c1SMemoryShore libc::tcsetpgrp(libc::STDIN_FILENO, std::process::id() as i32);
77b0dea7c1SMemoryShore };
78b0dea7c1SMemoryShore
79cac674d3SMemoryShore // 开启终端raw模式
80c6454d32SMemoryShore crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode");
815b859941SMemoryShore
825b859941SMemoryShore // 循环读取一行
836f50094aSMemoryShore loop {
84c6454d32SMemoryShore self.printer.init_before_readline();
855b859941SMemoryShore // 读取一行
86cf3f377bS裕依 if self.readline() == 0 {
8781c61261SMemoryShore println!();
886f50094aSMemoryShore break;
896f50094aSMemoryShore }
905b859941SMemoryShore
91c6454d32SMemoryShore let command_bytes = self.printer.buf.borrow().clone();
92cac674d3SMemoryShore // 如果命令不以空格开头且不跟上一条命令相同,这条命令会被记录
93dcf45035SMemoryShore if !command_bytes.is_empty()
94dcf45035SMemoryShore && !command_bytes.starts_with(&[b' '])
95c6454d32SMemoryShore && command_bytes
96c6454d32SMemoryShore != self
97c6454d32SMemoryShore .history_commands
98c6454d32SMemoryShore .last()
99c6454d32SMemoryShore .unwrap_or(&Rc::new(RefCell::new(Vec::new())))
100c6454d32SMemoryShore .borrow()
101c6454d32SMemoryShore .clone()
102c6454d32SMemoryShore {
103c6454d32SMemoryShore self.history_commands
104c6454d32SMemoryShore .push(Rc::new(RefCell::new(command_bytes.clone())));
105dcf45035SMemoryShore self.write_commands(&command_bytes);
106c6454d32SMemoryShore };
107cac674d3SMemoryShore // 命令不为空,执行命令
108732e8725SMemoryShore if !command_bytes.iter().all(|&byte| byte == b' ') {
1096f1d506bSMemoryShore self.exec_commands_in_line(&command_bytes);
1106f50094aSMemoryShore }
111ee35d965SMemoryShore }
1126f50094aSMemoryShore }
1136f50094aSMemoryShore
exec_commands_in_line(&mut self, command_bytes: &Vec<u8>)1146f1d506bSMemoryShore fn exec_commands_in_line(&mut self, command_bytes: &Vec<u8>) {
1157bb802adSMemoryShore // 解析命令
1167bb802adSMemoryShore let input_command = String::from_utf8(command_bytes.clone()).unwrap();
1177bb802adSMemoryShore let pipelines = Parser::parse(&input_command).unwrap();
1187bb802adSMemoryShore
1197bb802adSMemoryShore let mut foreground_pipelines = Vec::new();
120*0b5cf83bSMemoryShore let mut backend_pipelines = Vec::new();
1217bb802adSMemoryShore
1227bb802adSMemoryShore for pipeline in pipelines {
1237bb802adSMemoryShore if pipeline.backend() {
124*0b5cf83bSMemoryShore backend_pipelines.push(pipeline);
1257bb802adSMemoryShore } else {
1267bb802adSMemoryShore foreground_pipelines.push(pipeline);
1275b859941SMemoryShore }
1286f50094aSMemoryShore }
1296f50094aSMemoryShore
130*0b5cf83bSMemoryShore // 后台pipeline发送给子线程执行
131*0b5cf83bSMemoryShore let _ = self
132*0b5cf83bSMemoryShore .backend_thread
133*0b5cf83bSMemoryShore .send((EnvManager::current_dir(), backend_pipelines));
134*0b5cf83bSMemoryShore
1357bb802adSMemoryShore crossterm::terminal::disable_raw_mode().expect("failed to disable raw mode");
1367bb802adSMemoryShore
1377bb802adSMemoryShore // 顺序执行所有前台pipeline
1387bb802adSMemoryShore for pipeline in &foreground_pipelines {
1397bb802adSMemoryShore for mut child in pipeline.execute(BuildInCmd::map().clone()) {
1407bb802adSMemoryShore let _ = child.wait();
1417bb802adSMemoryShore }
1427bb802adSMemoryShore }
1437bb802adSMemoryShore
1447bb802adSMemoryShore crossterm::terminal::enable_raw_mode().expect("failed to enable raw mode");
1457bb802adSMemoryShore
1467bb802adSMemoryShore foreground_pipelines.clear();
1477bb802adSMemoryShore }
1487bb802adSMemoryShore
read_commands(&mut self)149c6454d32SMemoryShore pub fn read_commands(&mut self) {
150c6454d32SMemoryShore let mut history = Vec::new();
151c6454d32SMemoryShore for line in BufReader::new(match File::open(&self.history_path) {
1526f50094aSMemoryShore Ok(file) => file,
153c6454d32SMemoryShore Err(_) => File::create(&self.history_path).unwrap(),
1546f50094aSMemoryShore })
1556f50094aSMemoryShore .lines()
1566f50094aSMemoryShore {
1576f50094aSMemoryShore match line {
158c6454d32SMemoryShore Ok(s) => history.push(Rc::new(RefCell::new(s.into_bytes()))),
1596f50094aSMemoryShore Err(_) => {
1606f50094aSMemoryShore break;
1616f50094aSMemoryShore }
1626f50094aSMemoryShore }
1636f50094aSMemoryShore }
164c6454d32SMemoryShore self.history_commands = history;
1656f50094aSMemoryShore }
1666f50094aSMemoryShore
write_commands(&self, command_bytes: &Vec<u8>)167dcf45035SMemoryShore fn write_commands(&self, command_bytes: &Vec<u8>) {
1686f50094aSMemoryShore let mut file = OpenOptions::new()
169dcf45035SMemoryShore .append(true)
170dcf45035SMemoryShore .open(self.history_path.as_str())
1716f50094aSMemoryShore .unwrap();
172dcf45035SMemoryShore file.write_all(&command_bytes)
173dcf45035SMemoryShore .expect("failed to write history command");
17481c61261SMemoryShore file.write_all(&[SpecialKeycode::LF.into()]).unwrap();
1756f50094aSMemoryShore }
1766f50094aSMemoryShore
read_char() -> u8177c6454d32SMemoryShore fn read_char() -> u8 {
178c6454d32SMemoryShore let mut buf: [u8; 1] = [0];
1795b859941SMemoryShore loop {
1805b859941SMemoryShore if std::io::stdin().read(&mut buf).is_ok() {
1815b859941SMemoryShore return buf[0];
1825b859941SMemoryShore }
1835b859941SMemoryShore }
184ee35d965SMemoryShore }
185ee35d965SMemoryShore
handle_funckey(&mut self, command_index: &mut usize)186730e0306SLoGin fn handle_funckey(&mut self, command_index: &mut usize) {
1876c1ca14dSLoGin let mut keys = Vec::new();
1886c1ca14dSLoGin
1896c1ca14dSLoGin while FunctionKeySuffix::should_read_more(&keys) {
1906c1ca14dSLoGin keys.push(Self::read_char());
191730e0306SLoGin }
1926c1ca14dSLoGin let function_key = FunctionKeySuffix::try_from(&keys);
193730e0306SLoGin if function_key.is_none() {
194730e0306SLoGin return;
195730e0306SLoGin }
1966c1ca14dSLoGin
197730e0306SLoGin let function_key = function_key.unwrap();
198730e0306SLoGin
199001f2a75SMemoryShore match function_key {
200001f2a75SMemoryShore FunctionKeySuffix::Up => {
201730e0306SLoGin if *command_index > 0 {
202730e0306SLoGin *command_index -= 1;
203730e0306SLoGin self.printer
204730e0306SLoGin .change_line(self.history_commands.get(*command_index).unwrap());
2056f50094aSMemoryShore }
20681c61261SMemoryShore }
20781c61261SMemoryShore
208001f2a75SMemoryShore FunctionKeySuffix::Down => {
209730e0306SLoGin if *command_index < self.history_commands.len() - 1 {
210730e0306SLoGin *command_index += 1;
211730e0306SLoGin self.printer
212730e0306SLoGin .change_line(self.history_commands.get(*command_index).unwrap());
2136f50094aSMemoryShore }
2146f50094aSMemoryShore }
2156f50094aSMemoryShore
216001f2a75SMemoryShore FunctionKeySuffix::Left => {
217cac674d3SMemoryShore if self.printer.cursor > 0 {
218cac674d3SMemoryShore self.printer.cursor_left(1);
219cac674d3SMemoryShore }
2206f50094aSMemoryShore }
2216f50094aSMemoryShore
222001f2a75SMemoryShore FunctionKeySuffix::Right => {
223cac674d3SMemoryShore if self.printer.cursor < self.printer.buf.borrow().len() {
224cac674d3SMemoryShore self.printer.cursor_right(1);
225cac674d3SMemoryShore }
2266f50094aSMemoryShore }
2276f50094aSMemoryShore
228001f2a75SMemoryShore FunctionKeySuffix::Home => {
229c6454d32SMemoryShore self.printer.home();
23081c61261SMemoryShore }
23181c61261SMemoryShore
232001f2a75SMemoryShore FunctionKeySuffix::End => {
233c6454d32SMemoryShore self.printer.end();
23481c61261SMemoryShore }
2356c1ca14dSLoGin FunctionKeySuffix::Delete => self.printer.delete(1),
2366f50094aSMemoryShore }
2376f50094aSMemoryShore }
23881c61261SMemoryShore
readline(&mut self) -> usize239730e0306SLoGin fn readline(&mut self) -> usize {
240730e0306SLoGin let mut stdout = std::io::stdout();
241730e0306SLoGin self.history_commands.push(Rc::clone(&self.printer.buf));
242730e0306SLoGin let mut command_index = self.history_commands.len() - 1;
243730e0306SLoGin loop {
244730e0306SLoGin let key = Self::read_char();
245730e0306SLoGin if let Ok(special_key) = SpecialKeycode::try_from(key) {
246730e0306SLoGin match special_key {
247730e0306SLoGin SpecialKeycode::ESC => {
248730e0306SLoGin self.handle_funckey(&mut command_index);
249730e0306SLoGin }
250730e0306SLoGin
25181c61261SMemoryShore SpecialKeycode::LF | SpecialKeycode::CR => {
252c6454d32SMemoryShore println!();
253c6454d32SMemoryShore self.history_commands.pop();
25481c61261SMemoryShore return 1;
25581c61261SMemoryShore }
25681c61261SMemoryShore
2576c1ca14dSLoGin SpecialKeycode::BackSpace => {
258c6454d32SMemoryShore self.printer.backspace();
25981c61261SMemoryShore }
26081c61261SMemoryShore
26181c61261SMemoryShore SpecialKeycode::Tab => {
262c6454d32SMemoryShore let mut buf = self.printer.buf.deref().borrow().clone();
263c6454d32SMemoryShore buf.truncate(self.printer.cursor);
264c6454d32SMemoryShore let str = String::from_utf8(buf.clone()).unwrap();
265c6454d32SMemoryShore if buf.len() == 0 || buf.iter().all(|byte| *byte == b' ') {
2665b859941SMemoryShore continue;
267c6454d32SMemoryShore }
268c6454d32SMemoryShore
269c6454d32SMemoryShore let iter = str.chars();
270c6454d32SMemoryShore let mut fragments: Vec<String> = Vec::new();
271c6454d32SMemoryShore let mut stack: String = String::with_capacity(str.len());
272c6454d32SMemoryShore let mut left_quote: char = ' ';
273c6454d32SMemoryShore for ch in iter {
27446fb3114SMemoryShore //存在未闭合的左引号,此时包括空格的任何字符都加入栈中,直到匹配到右引号
275c6454d32SMemoryShore if left_quote != ' ' {
276c6454d32SMemoryShore if ch == left_quote {
277c6454d32SMemoryShore left_quote = ' ';
278c6454d32SMemoryShore }
279c6454d32SMemoryShore stack.push(ch);
2806f50094aSMemoryShore } else {
281c6454d32SMemoryShore //不存在未闭合的左引号
282c6454d32SMemoryShore if ch == '\'' || ch == '\"' {
283c6454d32SMemoryShore //字符为引号,记录下来
284c6454d32SMemoryShore left_quote = ch;
285c6454d32SMemoryShore stack.push(ch);
286c6454d32SMemoryShore } else if ch == ' ' {
287c6454d32SMemoryShore if !stack.is_empty() {
288c6454d32SMemoryShore //字符为空格且栈中不为空,该空格视作命令段之间的分割线
289c6454d32SMemoryShore //将栈中字符作为一个命令段加入集合,之后重置栈
290c6454d32SMemoryShore fragments.push(stack.to_string());
291c6454d32SMemoryShore stack.clear();
2926f50094aSMemoryShore }
293c6454d32SMemoryShore } else {
294c6454d32SMemoryShore //其他字符都作为普通字符加入栈中
295c6454d32SMemoryShore stack.push(ch);
2966f50094aSMemoryShore }
297c6454d32SMemoryShore }
298c6454d32SMemoryShore }
299c6454d32SMemoryShore //结束时如果栈不为空
300c6454d32SMemoryShore if !stack.is_empty() {
301c6454d32SMemoryShore fragments.push(stack.to_string());
302c6454d32SMemoryShore } else {
303c6454d32SMemoryShore //结束时如果栈为空,说明光标左边的字符不属于任何命令片段,无法进行补全
304c6454d32SMemoryShore return 1;
305c6454d32SMemoryShore }
306c6454d32SMemoryShore
307c6454d32SMemoryShore let mut target_fragment = fragments.last().unwrap().clone();
308c6454d32SMemoryShore target_fragment = target_fragment.replace("\'", "").replace("\"", "");
309c6454d32SMemoryShore
31046fb3114SMemoryShore let (prefix, candidates) = if fragments.len() < 2 {
311c6454d32SMemoryShore //补全命令
312c6454d32SMemoryShore complete_command(&target_fragment)
313c6454d32SMemoryShore } else {
314c6454d32SMemoryShore //补全参数
315c6454d32SMemoryShore complete_path(&target_fragment)
3166f50094aSMemoryShore };
317c6454d32SMemoryShore
3186f50094aSMemoryShore match candidates.len() {
3196f50094aSMemoryShore 1 => {
320c6454d32SMemoryShore let old_fragment = fragments.last().unwrap();
321c6454d32SMemoryShore let candidate = candidates.last().unwrap();
322cac674d3SMemoryShore self.printer.cursor_left(old_fragment.len());
323c6454d32SMemoryShore self.printer.delete(old_fragment.len());
32446fb3114SMemoryShore self.printer
32546fb3114SMemoryShore .insert(format!("{}{}", prefix, candidate).as_bytes());
3266f50094aSMemoryShore }
3276f50094aSMemoryShore 2.. => {
328c6454d32SMemoryShore self.printer.end();
32981c61261SMemoryShore println!();
3306f50094aSMemoryShore for candidate in candidates {
331c6454d32SMemoryShore print!(
332cf3f377bS裕依 "{}\t",
3336537b369S裕依2439 if candidate.ends_with('/') {
334cf3f377bS裕依 candidate.truecolor(0x00, 0x88, 0xff)
3356537b369S裕依2439 } else {
336c6454d32SMemoryShore candidate.white()
3376f50094aSMemoryShore }
338c6454d32SMemoryShore );
3396537b369S裕依2439 }
34081c61261SMemoryShore println!();
341c6454d32SMemoryShore self.printer.print_prompt();
342cac674d3SMemoryShore print!(
343cac674d3SMemoryShore "{}",
344cac674d3SMemoryShore String::from_utf8(self.printer.buf.borrow().to_vec()).unwrap()
345cac674d3SMemoryShore );
3466f50094aSMemoryShore }
3476f50094aSMemoryShore _ => {}
3486f50094aSMemoryShore }
3496f50094aSMemoryShore }
3506f50094aSMemoryShore
351001f2a75SMemoryShore _ => {}
35281c61261SMemoryShore }
35381c61261SMemoryShore } else {
354c6454d32SMemoryShore match key {
3556f50094aSMemoryShore 1..=31 => {}
3566f50094aSMemoryShore c => {
357c6454d32SMemoryShore self.printer.insert(&[c]);
358cac674d3SMemoryShore // String::from_utf8("abdsdf".as_bytes().to_vec()).unwrap();
3596f50094aSMemoryShore }
3606f50094aSMemoryShore }
3616f50094aSMemoryShore }
3626f50094aSMemoryShore stdout.flush().unwrap();
3636f50094aSMemoryShore }
3646f50094aSMemoryShore }
3656f50094aSMemoryShore }
3666f50094aSMemoryShore
36746fb3114SMemoryShore #[allow(dead_code)]
368cac674d3SMemoryShore struct WindowSize {
369cac674d3SMemoryShore row: usize,
370cac674d3SMemoryShore col: usize,
371cac674d3SMemoryShore }
372cac674d3SMemoryShore
3737bb802adSMemoryShore #[allow(dead_code)]
374cac674d3SMemoryShore impl WindowSize {
new() -> Option<Self>375cac674d3SMemoryShore pub fn new() -> Option<Self> {
376cac674d3SMemoryShore let mut ws: libc::winsize = unsafe { std::mem::zeroed() };
377cac674d3SMemoryShore if unsafe {
378cac674d3SMemoryShore libc::ioctl(
379cac674d3SMemoryShore libc::STDOUT_FILENO,
380cac674d3SMemoryShore libc::TIOCGWINSZ,
381cac674d3SMemoryShore &mut ws as *mut libc::winsize,
382cac674d3SMemoryShore )
383cac674d3SMemoryShore } == -1
384cac674d3SMemoryShore {
385cac674d3SMemoryShore None
386cac674d3SMemoryShore } else {
387cac674d3SMemoryShore Some(Self {
388cac674d3SMemoryShore row: ws.ws_row.into(),
389cac674d3SMemoryShore col: ws.ws_col.into(),
390cac674d3SMemoryShore })
391cac674d3SMemoryShore }
392cac674d3SMemoryShore }
393c6454d32SMemoryShore }
394c6454d32SMemoryShore
complete_command(command: &str) -> (&str, Vec<String>)39546fb3114SMemoryShore pub fn complete_command(command: &str) -> (&str, Vec<String>) {
3966f50094aSMemoryShore let mut candidates: Vec<String> = Vec::new();
3977bb802adSMemoryShore for (cmd, _) in BuildInCmd::map().as_ref().unwrap().lock().unwrap().iter() {
3986f50094aSMemoryShore if cmd.starts_with(command) {
3997bb802adSMemoryShore candidates.push(String::from(cmd));
4006f50094aSMemoryShore }
4016f50094aSMemoryShore }
40246fb3114SMemoryShore ("", candidates)
4036f50094aSMemoryShore }
4046f50094aSMemoryShore
complete_path(incomplete_path: &str) -> (&str, Vec<String>)40546fb3114SMemoryShore pub fn complete_path(incomplete_path: &str) -> (&str, Vec<String>) {
4066f50094aSMemoryShore let mut candidates: Vec<String> = Vec::new();
40746fb3114SMemoryShore let mut dir = "";
4086f50094aSMemoryShore let incomplete_name: &str;
40946fb3114SMemoryShore if let Some(index) = incomplete_path.rfind('/') {
41046fb3114SMemoryShore dir = &incomplete_path[..=index];
41146fb3114SMemoryShore incomplete_name = &incomplete_path[index + 1..];
4126f50094aSMemoryShore } else {
41346fb3114SMemoryShore incomplete_name = incomplete_path;
4146f50094aSMemoryShore }
41546fb3114SMemoryShore if let Ok(read_dir) = fs::read_dir(if dir.is_empty() { "." } else { dir }) {
4166f50094aSMemoryShore for entry in read_dir {
4176f50094aSMemoryShore let entry = entry.unwrap();
4186f50094aSMemoryShore let mut file_name = entry.file_name().into_string().unwrap();
4196f50094aSMemoryShore if file_name.starts_with(incomplete_name) {
42046fb3114SMemoryShore if file_name.contains(' ') {
42146fb3114SMemoryShore file_name = format!("\'{}\'", file_name);
42246fb3114SMemoryShore }
4236f50094aSMemoryShore if entry.file_type().unwrap().is_dir() {
4246f50094aSMemoryShore file_name.push('/');
4256f50094aSMemoryShore }
4266f50094aSMemoryShore candidates.push(file_name);
4276f50094aSMemoryShore }
4286f50094aSMemoryShore }
4296f50094aSMemoryShore }
4306f50094aSMemoryShore
43146fb3114SMemoryShore return (dir, candidates);
4326f50094aSMemoryShore }
433