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