use anyhow::{Context, Result};
use clap::Parser;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
pub fn load_config(args: &mut crate::cli::Args) -> Result<()> {
let mut config_args = vec![std::env::args()
.next()
.unwrap_or_else(|| "rusdu".to_string())];
#[cfg(unix)]
{
let system_config = Path::new("/etc/ncdu.conf");
if system_config.exists() {
if let Err(e) = append_config_args(system_config, &mut config_args) {
log::warn!("Failed to read system config /etc/ncdu.conf: {}", e);
}
}
}
if let Some(mut user_config) = dirs::config_dir() {
user_config.push("ncdu");
user_config.push("config");
if user_config.exists() {
append_config_args(&user_config, &mut config_args)?;
}
}
if config_args.len() > 1 {
let actual_args = std::env::args().skip(1);
config_args.extend(actual_args);
match crate::cli::Args::try_parse_from(&config_args) {
Ok(parsed) => {
*args = parsed;
}
Err(e) => {
eprintln!("Configuration parsing error:\n{}", e);
}
}
}
Ok(())
}
fn append_config_args(path: &Path, args: &mut Vec<String>) -> Result<()> {
let file =
File::open(path).with_context(|| format!("Failed to open config file {:?}", path))?;
let reader = BufReader::new(file);
for line in reader.lines() {
let line = line?;
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
let clean_line = if trimmed.starts_with('@') {
&trimmed[1..]
} else {
trimmed
};
let parts = shell_words::split(clean_line).unwrap_or_else(|_| {
clean_line
.split_whitespace()
.map(|s| s.to_string())
.collect()
});
for part in parts {
if !part.is_empty() {
let expanded_part = if part.starts_with("~/") || part == "~" {
if let Some(home) = dirs::home_dir() {
part.replacen('~', &home.to_string_lossy(), 1)
} else {
part
}
} else {
part
};
args.push(expanded_part);
}
}
}
Ok(())
}
mod shell_words {
pub fn split(input: &str) -> Result<Vec<String>, ()> {
let mut words = Vec::new();
let mut word = String::new();
let mut in_double_quote = false;
let mut in_single_quote = false;
let mut escaped = false;
for c in input.chars() {
if escaped {
word.push(c);
escaped = false;
} else if c == '\\' && !in_single_quote {
escaped = true;
} else if c == '"' && !in_single_quote {
in_double_quote = !in_double_quote;
} else if c == '\'' && !in_double_quote {
in_single_quote = !in_single_quote;
} else if c.is_whitespace() && !in_double_quote && !in_single_quote {
if !word.is_empty() {
words.push(word.clone());
word.clear();
}
} else {
word.push(c);
}
}
if !word.is_empty() {
words.push(word);
}
if in_double_quote || in_single_quote || escaped {
Err(())
} else {
Ok(words)
}
}
}