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.
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) ()