Anne
Anne

Reputation: 1270

Make OCaml Arg.parse function accept anonymous arguments starting with

I am trying to use the Arg.parse OCaml function to read command line options and arguments, including not specified arguments starting with -. According to the manual:

For the user to be able to specify anonymous arguments starting with a -, include for example ("-", String anon_fun, doc) in speclist.

So I would expect this example to work:

let other_option x = Format.printf "Other option: %s@." x
let other_argument x = Format.printf "Other argument: %s@." x
let arg_speclist =[
 ("-a", Arg.Unit (fun () -> Format.printf "Known option: -a@."), "option a");
 ("-", Arg.String other_option, "any option")
]
let () = Arg.parse arg_speclist other_argument "Usage:"

With specified -a option and other arguments, it is working:

$ ocaml test.ml arg1 -a arg2
Other argument: arg1
Known option: -a
Other argument: arg2

But it is not when trying to use something starting with -:

$ ocaml test.ml -x
test.ml: unknown option '-x'.
Usage:
  -a option a
  - any option
  -help  Display this list of options
  --help  Display this list of options

I would expect the other_option function to be called. What am I doing wrong?

Upvotes: 3

Views: 750

Answers (1)

PatJ
PatJ

Reputation: 6144

Using the ("-", String anon_fun, doc) doesn't allow you to do what you think, it allows you to type in options this way:

ocaml test.ml - -x

Basically, it makes "-" an "escaping" option that will pass the next one to anon_fun.

The exact behavior you want can't be done easily through Arg (which was never meant as an exhaustive complex argument parser AFAIK).

As a possible workaround, you may first manually go through your argv (don't forget not to read 0) and to add options to your speclist dynamically:

let speclist = [ ("-option", Arg.String print_endline, "handled option") ]
let speclist =
 let r = ref speclist in
 for i = 1 to pred (Array.length Sys.argv) do
  let s = Sys.argv.(i) in
  if s.[0] = '-' && not List.exists (fun (s',_,_) -> s = s') !r
  then r := (s, Arg.Unit (fun () -> anon_fun s), "any_option") :: !r
 done;
 !r

Another option is to get the code from Arg and modify it according to your model, or to use another, more powerful tool. Maybe Cmdliner is what you're looking for (I've never used it, so I don't know if it does the trick).

Upvotes: 4

Related Questions