summaryrefslogtreecommitdiff
path: root/src/load_program.rs
blob: 0473207e210ee3adece540d7721d14ffebd31c44 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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()) });
}