diff options
Diffstat (limited to 'src/load_program.rs')
-rw-r--r-- | src/load_program.rs | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/src/load_program.rs b/src/load_program.rs new file mode 100644 index 0000000..0473207 --- /dev/null +++ b/src/load_program.rs @@ -0,0 +1,93 @@ +use crate::*; + +use std::io::Read; + + +pub struct Program { + pub bytecode: Vec<u8>, + pub path: Option<PathBuf>, +} + +impl Program { + fn path(&self) -> String { + match &self.path { + Some(path) => path.as_os_str().to_string_lossy().to_string(), + None => String::from("<unknown>"), + } + } +} + + +/// Load program from path or standard input. +pub fn load_program(path: Option<&PathBuf>) -> Program { + if let Some(path) = path { + if let Ok(program) = load_program_from_file(path) { + let length = program.bytecode.len(); + let path = program.path(); + info!("Loaded program from {path:?} ({length} bytes)"); + return program; + } else if let Some(program) = load_program_from_bedrock_path(path) { + let length = program.bytecode.len(); + let path = program.path(); + info!("Loaded program from {path:?} ({length} bytes)"); + return program; + } else { + fatal!("Could not read program from {path:?}"); + } + } else { + info!("Reading program from standard input..."); + if let Ok(program) = load_program_from_stdin() { + let length = program.bytecode.len(); + info!("Loaded program from standard input ({length} bytes)"); + return program; + } else { + fatal!("Could not read program from standard input"); + } + } +} + +/// Attempt to load program from a directory in the BEDROCK_PATH environment variable. +fn load_program_from_bedrock_path(path: &Path) -> Option<Program> { + // Only if the path can be treated as a file name. + if path.is_relative() && path.components().count() == 1 { + for base_path in std::env::var("BEDROCK_PATH").ok()?.split(':') { + let mut base_path = PathBuf::from(base_path); + if !base_path.is_absolute() { continue; } + base_path.push(path); + info!("Attempting to load program from {base_path:?}"); + if let Ok(program) = load_program_from_file(&base_path) { + return Some(program); + } + if path.extension().is_some() { continue; } + base_path.set_extension("br"); + info!("Attempting to load program from {base_path:?}"); + if let Ok(program) = load_program_from_file(&base_path) { + return Some(program); + } + } + } + return None; +} + +/// Attempt to load program from a specific file path. +fn load_program_from_file(path: &Path) -> Result<Program, std::io::Error> { + // Canonicalize paths so that symbolic links to program files resolve to + // the real program directory, which could contain a symbols file. + let path = match path.canonicalize() { + Ok(canonical) => canonical, + Err(_) => path.to_path_buf(), + }; + load_program_from_readable_source(std::fs::File::open(&path)?, Some(&path)) +} + +/// Attempt to load program from standard input. +fn load_program_from_stdin() -> Result<Program, std::io::Error> { + load_program_from_readable_source(std::io::stdin(), None) +} + +/// Attempt to load program from a source that implements std::io::Read. +fn load_program_from_readable_source(source: impl Read, path: Option<&Path>) -> Result<Program, std::io::Error> { + let mut bytecode = Vec::<u8>::new(); + source.take(65536).read_to_end(&mut bytecode)?; + return Ok(Program { bytecode, path: path.map(|p| p.to_path_buf()) }); +} |