@@ -38,19 +38,25 @@ fn config_path() -> String {
3838 format!("{}/.config/j/config.j", home)
3939}
4040
41+/// The default config, embedded so `j` always has it regardless of how it was
42+/// installed (the share/j/config.j file may not exist next to the binary).
43+const DEFAULT_CONFIG: &str = include_str!("../config.j");
44+
4145fn load_config_file() -> Result<(Config, String), ExitCode> {
4246 let path = config_path();
4347 let src = match std::fs::read_to_string(&path) {
4448 Ok(s) => s,
4549 Err(_) => {
46− return Err(err(
47− 3,
48− format!(
49− "no config at {}; the reference config.j is installed at {}",
50− path,
51− reference_config_path()
52− ),
53− ));
50+ // No user config yet: materialise the default there, editable by
51+ // the current user, so they have a starting point to customise.
52+ if let Err(e) = write_default_config(&path) {
53+ return Err(err(3, format!("cannot create config at {}: {}", path, e)));
54+ }
55+ eprintln!("j: created an editable default config at {}", path);
56+ match std::fs::read_to_string(&path) {
57+ Ok(s) => s,
58+ Err(e) => return Err(err(3, format!("cannot read config at {}: {}", path, e))),
59+ }
5460 }
5561 };
5662 match config::load_config(&src) {
@@ -59,17 +65,20 @@ fn load_config_file() -> Result<(Config, String), ExitCode> {
5965 }
6066}
6167
62−fn reference_config_path() -> String {
63− // installed next to the binary under <prefix>/share/j/config.j
64− if let Ok(exe) = std::env::current_exe() {
65− if let Some(p) = exe.parent().and_then(|p| p.parent()) {
66− let cand = p.join("share/j/config.j");
67− if cand.exists() {
68− return cand.display().to_string();
69− }
70− }
68+/// Write the embedded default config to `path`, creating parent directories
69+/// and making the file readable and writable by the current user.
70+fn write_default_config(path: &str) -> std::io::Result<()> {
71+ if let Some(parent) = std::path::Path::new(path).parent() {
72+ std::fs::create_dir_all(parent)?;
73+ }
74+ std::fs::write(path, DEFAULT_CONFIG)?;
75+ // user-read+write (0o600 on unix): the file is meant to be edited by them
76+ #[cfg(unix)]
77+ {
78+ use std::os::unix::fs::PermissionsExt;
79+ std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600))?;
7180 }
72− "/usr/local/share/j/config.j".into()
81+ Ok(())
7382}
7483
7584const RESERVED: &[&str] = &["init", "clone", "remote", "fetch", "push", "undo", "redo", "ops"];