aboutsummaryrefslogtreecommitdiffstats
path: root/shell/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'shell/src/main.rs')
-rw-r--r--shell/src/main.rs158
1 files changed, 79 insertions, 79 deletions
diff --git a/shell/src/main.rs b/shell/src/main.rs
index 4901e7f..4a98828 100644
--- a/shell/src/main.rs
+++ b/shell/src/main.rs
@@ -1,84 +1,5 @@
1use std::{os::unix::process::CommandExt, process::ExitCode}; 1use std::{os::unix::process::CommandExt, process::ExitCode};
2 2
3fn parse_sq(s: &str) -> Option<(String, &str)> {
4 #[derive(PartialEq, Eq)]
5 enum SqState {
6 Quoted,
7 Unquoted { may_escape: bool },
8 UnquotedEscaped,
9 }
10
11 let mut result = String::new();
12 let mut state = SqState::Unquoted { may_escape: false };
13 let mut remaining = "";
14 for (i, c) in s.char_indices() {
15 match state {
16 SqState::Unquoted { may_escape: false } => {
17 if c != '\'' {
18 return None;
19 }
20 state = SqState::Quoted
21 }
22 SqState::Quoted => {
23 if c == '\'' {
24 state = SqState::Unquoted { may_escape: true };
25 continue;
26 }
27 result.push(c);
28 }
29 SqState::Unquoted { may_escape: true } => {
30 if is_posix_space(c) {
31 remaining = &s[i..];
32 break;
33 }
34 if c != '\\' {
35 return None;
36 }
37 state = SqState::UnquotedEscaped;
38 }
39 SqState::UnquotedEscaped => {
40 if c != '\\' && c != '!' {
41 return None;
42 }
43 result.push(c);
44 state = SqState::Unquoted { may_escape: false };
45 }
46 }
47 }
48
49 if state != (SqState::Unquoted { may_escape: true }) {
50 return None;
51 }
52 Some((result, remaining))
53}
54
55fn parse_cmd(mut cmd: &str) -> Option<Vec<String>> {
56 let mut args = Vec::<String>::new();
57
58 cmd = cmd.trim_matches(is_posix_space);
59 while !cmd.is_empty() {
60 if cmd.starts_with('\'') {
61 let (arg, remaining) = parse_sq(cmd)?;
62 args.push(arg);
63 cmd = remaining.trim_start_matches(is_posix_space);
64 } else if let Some((arg, remaining)) = cmd.split_once(is_posix_space) {
65 args.push(arg.to_owned());
66 cmd = remaining.trim_start_matches(is_posix_space);
67 } else {
68 args.push(cmd.to_owned());
69 cmd = "";
70 }
71 }
72
73 Some(args)
74}
75
76fn is_posix_space(c: char) -> bool {
77 // Form feed: 0x0c
78 // Vertical tab: 0x0b
79 c == ' ' || c == '\x0c' || c == '\n' || c == '\r' || c == '\t' || c == '\x0b'
80}
81
82fn main() -> ExitCode { 3fn main() -> ExitCode {
83 let bad_usage = ExitCode::from(2); 4 let bad_usage = ExitCode::from(2);
84 5
@@ -141,3 +62,82 @@ fn main() -> ExitCode {
141 eprintln!("Error: {e}"); 62 eprintln!("Error: {e}");
142 ExitCode::FAILURE 63 ExitCode::FAILURE
143} 64}
65
66fn parse_cmd(mut cmd: &str) -> Option<Vec<String>> {
67 let mut args = Vec::<String>::new();
68
69 cmd = cmd.trim_matches(is_posix_space);
70 while !cmd.is_empty() {
71 if cmd.starts_with('\'') {
72 let (arg, remaining) = parse_sq(cmd)?;
73 args.push(arg);
74 cmd = remaining.trim_start_matches(is_posix_space);
75 } else if let Some((arg, remaining)) = cmd.split_once(is_posix_space) {
76 args.push(arg.to_owned());
77 cmd = remaining.trim_start_matches(is_posix_space);
78 } else {
79 args.push(cmd.to_owned());
80 cmd = "";
81 }
82 }
83
84 Some(args)
85}
86
87fn is_posix_space(c: char) -> bool {
88 // Form feed: 0x0c
89 // Vertical tab: 0x0b
90 c == ' ' || c == '\x0c' || c == '\n' || c == '\r' || c == '\t' || c == '\x0b'
91}
92
93fn parse_sq(s: &str) -> Option<(String, &str)> {
94 #[derive(PartialEq, Eq)]
95 enum SqState {
96 Quoted,
97 Unquoted { may_escape: bool },
98 UnquotedEscaped,
99 }
100
101 let mut result = String::new();
102 let mut state = SqState::Unquoted { may_escape: false };
103 let mut remaining = "";
104 for (i, c) in s.char_indices() {
105 match state {
106 SqState::Unquoted { may_escape: false } => {
107 if c != '\'' {
108 return None;
109 }
110 state = SqState::Quoted
111 }
112 SqState::Quoted => {
113 if c == '\'' {
114 state = SqState::Unquoted { may_escape: true };
115 continue;
116 }
117 result.push(c);
118 }
119 SqState::Unquoted { may_escape: true } => {
120 if is_posix_space(c) {
121 remaining = &s[i..];
122 break;
123 }
124 if c != '\\' {
125 return None;
126 }
127 state = SqState::UnquotedEscaped;
128 }
129 SqState::UnquotedEscaped => {
130 if c != '\\' && c != '!' {
131 return None;
132 }
133 result.push(c);
134 state = SqState::Unquoted { may_escape: false };
135 }
136 }
137 }
138
139 if state != (SqState::Unquoted { may_escape: true }) {
140 return None;
141 }
142 Some((result, remaining))
143}