binocular-cli 0.2.3

Not exactly a telescope, but it's useful sometimes. TUI to search/navigate through files and workspaces.
Documentation
//! Binary type detection and format-specific metadata.

use std::fs;
use std::io::{Read, Seek, SeekFrom};
use std::path::Path;

const MAX_ZIP_ENTRIES_TO_SCAN: usize = 10_000;

pub fn append_file_type_info(
    path: &Path,
    header: &[u8],
    doc: &mut crate::preview::doc::PreviewDoc,
) {
    use ratatui::style::Color;

    doc.push_section(super::SECTION_FILE_TYPE);

    let (file_type, description) = detect_file_type(header);
    doc.push_field("Type", file_type, Color::Green);
    if !description.is_empty() {
        doc.push_field("Description", description, Color::White);
    }

    if let Some(extra_info) = get_format_specific_info(header, path) {
        for (label, value) in extra_info {
            doc.push_field(label, value, Color::White);
        }
    }

    doc.push_blank_line();
}

fn detect_file_type(header: &[u8]) -> (&'static str, &'static str) {
    if header.len() < 4 {
        return ("Unknown", "");
    }

    match &header[..4.min(header.len())] {
        [0x7f, b'E', b'L', b'F'] => ("ELF", "Executable and Linkable Format (Linux/Unix)"),
        [b'M', b'Z', ..] => ("PE/DOS", "Windows Executable"),
        [0xfe, 0xed, 0xfa, 0xce] | [0xce, 0xfa, 0xed, 0xfe] => {
            ("Mach-O", "macOS Executable (32-bit)")
        }
        [0xfe, 0xed, 0xfa, 0xcf] | [0xcf, 0xfa, 0xed, 0xfe] => {
            ("Mach-O", "macOS Executable (64-bit)")
        }
        [0xca, 0xfe, 0xba, 0xbe] => ("Mach-O", "macOS Universal Binary"),
        [b'P', b'K', 0x03, 0x04] => ("ZIP", "ZIP Archive"),
        [b'P', b'K', 0x05, 0x06] => ("ZIP", "ZIP Archive (empty)"),
        [0x1f, 0x8b, ..] => ("GZIP", "GZIP Compressed"),
        [b'B', b'Z', b'h', ..] => ("BZIP2", "BZIP2 Compressed"),
        [0xfd, b'7', b'z', b'X'] => ("XZ", "XZ Compressed"),
        [b'R', b'a', b'r', b'!'] => ("RAR", "RAR Archive"),
        [b'7', b'z', 0xbc, 0xaf] => ("7Z", "7-Zip Archive"),
        [0x89, b'P', b'N', b'G'] => ("PNG", "Portable Network Graphics"),
        [0xff, 0xd8, 0xff, ..] => ("JPEG", "JPEG Image"),
        [b'G', b'I', b'F', b'8'] => ("GIF", "Graphics Interchange Format"),
        [b'B', b'M', ..] => ("BMP", "Bitmap Image"),
        [b'R', b'I', b'F', b'F'] if header.len() >= 12 && &header[8..12] == b"WEBP" => {
            ("WEBP", "WebP Image")
        }
        [b'I', b'I', 0x2a, 0x00] | [b'M', b'M', 0x00, 0x2a] => ("TIFF", "Tagged Image File Format"),
        [0x25, b'P', b'D', b'F'] => ("PDF", "Portable Document Format"),
        [0xd0, 0xcf, 0x11, 0xe0] => ("OLE", "Microsoft Office Document (legacy)"),
        [b'I', b'D', b'3', ..] => ("MP3", "MP3 Audio (ID3 tagged)"),
        [0xff, 0xfb, ..] | [0xff, 0xfa, ..] => ("MP3", "MP3 Audio"),
        [b'O', b'g', b'g', b'S'] => ("OGG", "Ogg Container"),
        [b'f', b'L', b'a', b'C'] => ("FLAC", "Free Lossless Audio Codec"),
        [b'R', b'I', b'F', b'F'] if header.len() >= 12 && &header[8..12] == b"WAVE" => {
            ("WAV", "Waveform Audio")
        }
        [b'R', b'I', b'F', b'F'] if header.len() >= 12 && &header[8..12] == b"AVI " => {
            ("AVI", "Audio Video Interleave")
        }
        [b'S', b'Q', b'L', b'i'] => ("SQLite", "SQLite Database"),
        [0x00, 0x00, 0x00, ..] if header.len() >= 8 && &header[4..8] == b"ftyp" => {
            ("MP4/MOV", "MPEG-4 / QuickTime")
        }
        [0x1a, 0x45, 0xdf, 0xa3] => ("MKV/WebM", "Matroska/WebM Container"),
        _ => ("Binary", "Unknown binary format"),
    }
}

fn get_format_specific_info(header: &[u8], path: &Path) -> Option<Vec<(&'static str, String)>> {
    if header.len() < 4 {
        return None;
    }

    let mut info = Vec::new();

    if let Some(entries) = elf_info(header) {
        info.extend(entries);
    }

    if let Some((width, height)) = png_dimensions(header) {
        info.push(("Dimensions", format!("{}x{}", width, height)));
    }

    if header.starts_with(&[0xff, 0xd8, 0xff]) {
        if let Some((w, h)) = find_jpeg_dimensions(path) {
            info.push(("Dimensions", format!("{}x{}", w, h)));
        }
    }

    if header.starts_with(&[b'P', b'K', 0x03, 0x04]) {
        if let Some(count) = count_zip_entries(path) {
            info.push(("Entries", count.to_string()));
        }
    }

    if info.is_empty() {
        None
    } else {
        Some(info)
    }
}

fn elf_info(header: &[u8]) -> Option<Vec<(&'static str, String)>> {
    if !header.starts_with(&[0x7f, b'E', b'L', b'F']) || header.len() < 20 {
        return None;
    }

    let class = match header.get(4) {
        Some(1) => "32-bit",
        Some(2) => "64-bit",
        _ => "Unknown",
    };
    let endian = match header.get(5) {
        Some(1) => "Little-endian",
        Some(2) => "Big-endian",
        _ => "Unknown",
    };
    let os_abi = match header.get(7) {
        Some(0) => "System V",
        Some(3) => "Linux",
        Some(9) => "FreeBSD",
        Some(12) => "OpenBSD",
        _ => "Other",
    };

    Some(vec![
        ("Class", class.to_string()),
        ("Endianness", endian.to_string()),
        ("OS/ABI", os_abi.to_string()),
    ])
}

fn png_dimensions(header: &[u8]) -> Option<(u32, u32)> {
    if !header.starts_with(&[0x89, b'P', b'N', b'G']) || header.len() < 24 {
        return None;
    }

    let width = u32::from_be_bytes([header[16], header[17], header[18], header[19]]);
    let height = u32::from_be_bytes([header[20], header[21], header[22], header[23]]);
    Some((width, height))
}

fn find_jpeg_dimensions(path: &Path) -> Option<(u16, u16)> {
    let mut file = fs::File::open(path).ok()?;
    let mut buffer = [0u8; 12];

    file.seek(SeekFrom::Start(2)).ok()?;

    loop {
        if file.read_exact(&mut buffer[..2]).is_err() {
            break;
        }

        if buffer[0] != 0xff {
            break;
        }

        let marker = buffer[1];

        if matches!(
            marker,
            0xc0 | 0xc1
                | 0xc2
                | 0xc3
                | 0xc5
                | 0xc6
                | 0xc7
                | 0xc9
                | 0xca
                | 0xcb
                | 0xcd
                | 0xce
                | 0xcf
        ) {
            if file.read_exact(&mut buffer[..7]).is_err() {
                break;
            }
            let height = u16::from_be_bytes([buffer[3], buffer[4]]);
            let width = u16::from_be_bytes([buffer[5], buffer[6]]);
            return Some((width, height));
        }

        if file.read_exact(&mut buffer[..2]).is_err() {
            break;
        }
        let length = u16::from_be_bytes([buffer[0], buffer[1]]) as i64 - 2;
        if length > 0 {
            file.seek(SeekFrom::Current(length)).ok()?;
        }
    }

    None
}

fn count_zip_entries(path: &Path) -> Option<usize> {
    let mut file = fs::File::open(path).ok()?;
    let mut count = 0;
    let mut buffer = [0u8; 30];

    loop {
        if file.read_exact(&mut buffer[..4]).is_err() {
            break;
        }

        if &buffer[..4] != &[b'P', b'K', 0x03, 0x04] {
            break;
        }

        count += 1;
        if count > MAX_ZIP_ENTRIES_TO_SCAN {
            break;
        }

        if file.read_exact(&mut buffer[..26]).is_err() {
            break;
        }

        let compressed_size = u32::from_le_bytes([buffer[14], buffer[15], buffer[16], buffer[17]]);
        let filename_len = u16::from_le_bytes([buffer[22], buffer[23]]) as i64;
        let extra_len = u16::from_le_bytes([buffer[24], buffer[25]]) as i64;

        let skip = filename_len + extra_len + compressed_size as i64;
        if file.seek(SeekFrom::Current(skip)).is_err() {
            break;
        }
    }

    Some(count)
}