aboutsummaryrefslogtreecommitdiffstats
path: root/shell
diff options
context:
space:
mode:
authorLibravatar Rutger Broekhoff2024-01-24 18:58:58 +0100
committerLibravatar Rutger Broekhoff2024-01-24 18:58:58 +0100
commit6095ead99248963ae70091c5bb3399eed49c0826 (patch)
treee6a1f57f4b9434d77364f2d15bdae1a44a14e00d /shell
parentf41e9cc4fc2c91ef710c6976ca4554840dde22e5 (diff)
downloadgitolfs3-6095ead99248963ae70091c5bb3399eed49c0826.tar.gz
gitolfs3-6095ead99248963ae70091c5bb3399eed49c0826.zip
Remove Go and C source
The Rust implementation now implements all features I need
Diffstat (limited to 'shell')
-rw-r--r--shell/Cargo.toml6
-rw-r--r--shell/src/main.rs143
2 files changed, 149 insertions, 0 deletions
diff --git a/shell/Cargo.toml b/shell/Cargo.toml
new file mode 100644
index 0000000..0dcb6d6
--- /dev/null
+++ b/shell/Cargo.toml
@@ -0,0 +1,6 @@
1[package]
2name = "shell"
3version = "0.1.0"
4edition = "2021"
5
6[dependencies]
diff --git a/shell/src/main.rs b/shell/src/main.rs
new file mode 100644
index 0000000..ef0ef48
--- /dev/null
+++ b/shell/src/main.rs
@@ -0,0 +1,143 @@
1use std::{os::unix::process::CommandExt, process::ExitCode};
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 != "" {
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 {
83 let bad_usage = ExitCode::from(2);
84
85 let mut args = std::env::args().skip(1);
86 if args.next() != Some("-c".to_string()) {
87 eprintln!("Expected usage: shell -c <argument>");
88 return bad_usage;
89 }
90 let Some(cmd) = args.next() else {
91 eprintln!("Missing argument for argument '-c'");
92 return bad_usage;
93 };
94 if args.next() != None {
95 eprintln!("Too many arguments passed");
96 return bad_usage;
97 }
98
99 let Some(mut cmd) = parse_cmd(&cmd) else {
100 eprintln!("Bad command");
101 return bad_usage;
102 };
103
104 let Some(mut program) = cmd.drain(0..1).next() else {
105 eprintln!("Bad command");
106 return bad_usage;
107 };
108 if program == "git" {
109 let Some(subcommand) = cmd.drain(0..1).next() else {
110 eprintln!("Bad command");
111 return bad_usage;
112 };
113 program.push('-');
114 program.push_str(&subcommand);
115 }
116
117 let mut args = Vec::new();
118
119 let git_cmds = ["git-receive-pack", "git-upload-archive", "git-upload-pack"];
120 if git_cmds.contains(&program.as_str()) {
121 if cmd.len() != 1 {
122 eprintln!("Bad command");
123 return bad_usage;
124 }
125 let repository = cmd[0].trim_start_matches('/');
126 args.push(repository);
127 } else if program == "git-lfs-authenticate" {
128 if cmd.len() != 2 {
129 eprintln!("Bad command");
130 return bad_usage;
131 }
132 let repository = cmd[0].trim_start_matches('/');
133 args.push(repository);
134 args.push(&cmd[1]);
135 } else {
136 eprintln!("Unknown command");
137 return bad_usage;
138 }
139
140 let e = std::process::Command::new(program).args(args).exec();
141 eprintln!("Error: {e}");
142 ExitCode::FAILURE
143}