use std::{ env, ffi::CString, fs, os::raw::{c_char, c_int}, }; const BOARDS: &str = include_str!("../../../alt-rootfs-installer/SUPPORTED-BOARDS"); unsafe extern "C" { fn run_qt_app( argc: c_int, argv: *mut *mut c_char, boards: *const c_char, drives: *const c_char, ) -> c_int; } fn main() { let arguments: Vec = env::args() .map(|arg| CString::new(arg).expect("application argument contains a NUL byte")) .collect(); let mut argument_pointers: Vec<*mut c_char> = arguments .iter() .map(|arg| arg.as_ptr().cast_mut()) .collect(); let boards = CString::new(BOARDS).expect("board list contains a NUL byte"); let drives = CString::new(available_drives().join("\n")) .expect("drive description contains a NUL byte"); // QApplication only borrows argv and the two strings for the duration of // this call; all backing CStrings remain alive until the event loop exits. unsafe { run_qt_app( argument_pointers.len() as c_int, argument_pointers.as_mut_ptr(), boards.as_ptr(), drives.as_ptr(), ); } } fn available_drives() -> Vec { let mut drives = Vec::new(); let Ok(entries) = fs::read_dir("/sys/block") else { return drives; }; for entry in entries.flatten() { let name = entry.file_name().to_string_lossy().into_owned(); if name.starts_with("loop") || name.starts_with("ram") || name.starts_with("zram") { continue; } let removable = fs::read_to_string(entry.path().join("removable")) .map(|value| value.trim() == "1") .unwrap_or(false); if !removable && !name.starts_with("mmcblk") { continue; } let sectors = fs::read_to_string(entry.path().join("size")) .ok() .and_then(|value| value.trim().parse::().ok()) .unwrap_or(0); let model = fs::read_to_string(entry.path().join("device/model")) .unwrap_or_default() .trim() .to_owned(); let description = if model.is_empty() { format!("/dev/{name}") } else { model }; drives.push(format!( "{description} · {} · /dev/{name}", format_size(sectors.saturating_mul(512)) )); } drives.sort(); drives } fn format_size(bytes: u64) -> String { const GIB: f64 = 1024.0 * 1024.0 * 1024.0; const MIB: f64 = 1024.0 * 1024.0; if bytes as f64 >= GIB { format!("{:.1} GB", bytes as f64 / GIB) } else { format!("{:.0} MB", bytes as f64 / MIB) } }