// SPDX-License-Identifier: MPL-2.0 use crate::prelude::*; /// Tries to parse a buffer as a shebang line. /// /// If the buffer starts with `#!` and its header is a valid shebang sequence, /// then the function returns `Ok(Some(parts))`, where `parts` is a `Vec` that /// contains the path of and the arguments for the interpreter. /// /// If the buffer starts with `#!` but some error occurs while parsing the /// file, then `Err(_)` is returned. If the buffer does not start with `#!`, /// then `Ok(None)` is returned. pub fn parse_shebang_line(file_first_page: &[u8]) -> Result>> { if !file_first_page.starts_with(b"#!") { // The file is not a shebang. return Ok(None); } let Some(first_line_len) = file_first_page.iter().position(|&c| c == b'\n') else { return_errno_with_message!(Errno::ENOEXEC, "the shebang line is too long"); }; // Skip `#!`. let shebang_header = &file_first_page[2..first_line_len]; let mut shebang_argv = Vec::new(); for arg in shebang_header.split(|&c| c == b' ' || c == b'\t') { if arg.is_empty() { continue; } let arg = CString::new(arg).map_err(|_| { Error::with_message(Errno::ENOEXEC, "unexpected nul terminator is found") })?; shebang_argv.push(arg); } if shebang_argv.is_empty() { return_errno_with_message!(Errno::ENOEXEC, "no interpreter program is found"); } Ok(Some(shebang_argv)) } #[cfg(ktest)] mod test { use alloc::{ffi::CString, vec}; use ostd::prelude::*; use super::parse_shebang_line; #[ktest] fn parse_shebang_line_with_multiple_args() { const LINE1: &str = "#! /bin/bash -e\n"; let res = parse_shebang_line(LINE1.as_bytes()).unwrap().unwrap(); assert_eq!( res, vec![ CString::new("/bin/bash").unwrap(), CString::new("-e").unwrap() ] ); const LINE2: &str = "#! /bin/env python3 \n"; let res = parse_shebang_line(LINE2.as_bytes()).unwrap().unwrap(); assert_eq!( res, vec![ CString::new("/bin/env").unwrap(), CString::new("python3").unwrap() ] ); } }