How to get the path of the default init file

OCaml has an option -init to use an init file different from the “default init file”.

But how do I get OCaml to tell me which file that is or would be ?

I know on Linux, it is ~/.ocamlinit, and on Windows, it will surely be some atrocious path. I am looking for a command I can give somebody so they can discover where is or would be theirs, in fashion similar to how you can say emacs -Q --batch --eval “(message (expand-file-name user-emacs-directory))” to discover the Emacs init dir.

There is no way to do so as far as I know, but the lookup order is included in the documentation of the -init option: OCaml - The toplevel system or REPL (ocaml).

Cheers,
Nicolas

Thanks for the pointer. This lead me to the function that does the selection in the toplevel, from which I derived a version that prints the paths :

external windows_xdg_defaults : unit -> string list = "caml_xdg_defaults"

let split_path_win32 path =
  (* Buffer for storing the current segment being scanned *)
  let buf = Buffer.create 256 in
  let get_contents () =
    let s = Buffer.contents buf in
    Buffer.clear buf;
    s
  in
  let add_segment segment_begin i =
    Buffer.add_substring buf path segment_begin (i - segment_begin)
  in
  let len = String.length path in
  let[@tail_mod_cons] rec parse segment_begin terminator i =
    if i >= len then
      (* Done - return the last entry *)
      [get_contents (add_segment segment_begin i)]
    else
      let ch = path.[i] in
      (* terminator is either ';' or '"' *)
      if ch = terminator then begin
        add_segment segment_begin i;
        if ch = ';' then
          (* Return this entry and begin scanning the next *)
          get_contents () :: parse (succ i) ';' (succ i)
        else
          (* Finished scanning '".."' so continue scanning this entry *)
          parse (succ i) ';' (succ i)
      end else if ch = '"' then begin
        (* Encountered the beginning of a quoted segment *)
        add_segment segment_begin i;
        parse (succ i) '"' (succ i)
      end else
        parse segment_begin terminator (succ i)
  in
  parse 0 ';' 0

let split_path =
  if Sys.win32 then
    split_path_win32
  else
    String.split_on_char ':'

let print_ocamlinit ?(verbose=false) () =
  let ocamlinit = ".ocamlinit" in
  if verbose then Printf.printf "1. .ocamlinit in the current directory\n%!";
  Printf.printf "%s\t %b\n%!" ocamlinit (Sys.file_exists ocamlinit);
  let init_ml = Filename.concat "ocaml" "init.ml" in
  let getenv var = match Sys.getenv_opt var with Some "" -> None | v -> v in
  let is_absolute = Fun.negate Filename.is_relative in
  let exists_in_dir ~file dir =
    let file = Filename.concat dir file in
    Printf.printf "%s\t %b\n%!" file (Sys.file_exists file)
  in
  let home_dir () = getenv "HOME" in
  let windows_xdg_defaults = Lazy.from_fun windows_xdg_defaults in
  let check_xdg_config_home () =
    if verbose then Printf.printf  "2. ocaml/init.ml under $XDG_CONFIG_HOME (or $HOME/.config on Unix, if\n\t $XDG_CONFIG_HOME is unset, empty or not an absolute path)\n%!";
    match getenv "XDG_CONFIG_HOME" with
    | Some dir when is_absolute dir ->
        exists_in_dir ~file:init_ml dir
    | _ -> 
      let default =
        if Sys.win32 then
          (* The first entry of the list is FOLDERID_LocalAppData (exposed by
             default in the process environment as %LOCALAPPDATA%) *)
          match Lazy.force windows_xdg_defaults with
          | dir::_ -> Some dir
          | [] -> None
        else
          Option.map (fun dir -> Filename.concat dir ".config") (home_dir ())
      in
      Option.iter (exists_in_dir ~file:init_ml) default
  in
  let check_xdg_config_dirs () =
    if verbose then Printf.printf "3. ocaml/init.ml under any of $XDG_CONFIG_DIRS (or /etc/xdg on Unix, or\n\t %%LOCALAPPDATA%%, %%APPDATA%%, %%PROGRAMDATA%% on Windows)\n%!";
    let dirs_from_env =
      match getenv "XDG_CONFIG_DIRS" with
      | Some entry -> List.filter is_absolute (split_path entry)
      | None -> []
    in
    let search =
      if dirs_from_env = [] then
        if Sys.win32 then
          (* There's a non-zero chance that a user of Cygwin, etc. sets
             XDG_CONFIG_HOME for their Cygwin installation and then starts
             native Windows `ocaml.exe` from within that installation. In this
             scenario, XDG_CONFIG_HOME is very unlikely to be a valid path (as
             Cygwin won't have translated it from Unix notation). To mitigate
             this, the default value we take for XDG_CONFIG_DIRS on Windows
             includes the default for XDG_CONFIG_HOME again. If the Cygwin user
             has set both XDG_CONFIG_HOME and XDG_CONFIG_DIRS then we can't help
             them! *)
          Lazy.force windows_xdg_defaults
        else
          ["/etc/xdg"]
      else
        dirs_from_env
    in
    List.iter (exists_in_dir ~file:init_ml) search
  in
  let check_home () =
    if verbose then Printf.printf "4. .ocamlinit in $HOME\n%!";
    Option.iter (exists_in_dir ~file:ocamlinit)  (home_dir ())
  in
  List.iter (fun f -> f ())
                [check_xdg_config_home;
                 check_xdg_config_dirs;
                 check_home]

let _ = print_ocamlinit ~verbose:(try Sys.argv.(1) = "--verbose" with _ -> false) ()
1 Like