1#[derive(Clone, Debug, PartialEq, Eq)]
5pub struct Shebang {
6 pub interp: Vec<u8>,
8 pub arg: Option<Vec<u8>>,
10}
11
12pub fn parse(line: &[u8]) -> Option<Shebang> {
24 let is_blank = |b: &u8| *b == b' ' || *b == b'\t';
25 let line = &line[..line.len().min(255)];
26 let end = line
28 .iter()
29 .position(|&b| b == 0 || b == b'\n')
30 .unwrap_or(line.len());
31 let line = &line[..end];
32 let start = line.iter().position(|b| !is_blank(b))?;
33 let rest = &line[start..];
34 let ilen = rest.iter().position(is_blank).unwrap_or(rest.len());
35 let interp = rest[..ilen].to_vec();
36 if interp.is_empty() {
37 return None;
38 }
39 let tail = &rest[ilen..];
40 let tstart = tail.iter().position(|b| !is_blank(b)).unwrap_or(tail.len());
41 let tend = tail
42 .iter()
43 .rposition(|b| !is_blank(b))
44 .map(|p| p + 1)
45 .unwrap_or(tstart);
46 let arg = if tend > tstart {
47 Some(tail[tstart..tend].to_vec())
48 } else {
49 None
50 };
51 Some(Shebang { interp, arg })
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57
58 #[test]
59 fn parses_interpreter_and_single_argument() {
60 assert_eq!(
61 parse(b"/bin/sh"),
62 Some(Shebang {
63 interp: b"/bin/sh".to_vec(),
64 arg: None
65 })
66 );
67 assert_eq!(
68 parse(b" /usr/bin/env python3 -u "),
69 Some(Shebang {
70 interp: b"/usr/bin/env".to_vec(),
71 arg: Some(b"python3 -u".to_vec())
72 })
73 );
74 assert_eq!(
75 parse(b"/bin/bash\t-e"),
76 Some(Shebang {
77 interp: b"/bin/bash".to_vec(),
78 arg: Some(b"-e".to_vec())
79 })
80 );
81 assert_eq!(parse(b" "), None);
82 assert_eq!(parse(b""), None);
83 }
84}