Reputation: 3248
Following AdaCore Gem #138 : Master the Commandline I am trying to create a commandline program that only accepts two options.
I pass a string to getopt which says which options are valid:
Getopt ("-project= -help")
Which means '--project foo' can be given, and also '--help' without an argument. I want to be very strict - project requires a parameter, and help should not take an argument. However, it seems the above will happily accept (and ignore) futher parameters given on the commandline, as long as they don't begin with a minus symbol:
$ ./getopt_invalid_option.exe --project foo these options are all invalid
Project := foo
$ ./getopt_invalid_option.exe things
Project :=
$ ./getopt_invalid_option.exe --switch
raised GNAT.COMMAND_LINE.INVALID_SWITCH : Unrecognized option '--switch'
$ ./getopt_invalid_option.exe -sss
raised GNAT.COMMAND_LINE.INVALID_SWITCH : Unrecognized option '-s'
How do I disallow the first two examples above?
My situation is slightly complicated by the fact that I have a section, after which everything should be allowed. From reading the specification of GNAT.Command_Line
I thought this would be achieved by calling Getopt("*")
after going to the corresponding section, which works, but with and without a section, I am unable to catch invalid switches (unless they begin with '-').
In the code below, I've commented the parts pertaining to the section; but the point is that a complete solution would need to work with the section as well.
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Command_Line; use GNAT.Command_Line;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Main is
Project : Unbounded_String;
begin
-- Initialize_Option_Scan (Section_Delimiters => "anything");
-- Goto_Section ("");
loop
case Getopt ("-project= -help") is
when '-' =>
if Full_Switch = "-project" then
Project := To_Unbounded_String (Parameter);
elsif Full_Switch = "-help" then
Put_Line ("Usage: etc... ");
return;
end if;
when ASCII.Nul =>
exit;
when others =>
Put_Line ("Error: unrecognized switch " & Full_Switch);
return;
end case;
end loop;
-- Goto_Section ("anything");
-- loop
-- exit when Getopt ("*") = ASCII.Nul;
-- Put_Line ("Accepted: " & Full_Switch);
-- end loop;
Put_Line ("Project := " & To_String (Project));
end Main;
I have tried changing the first Getopt to:
case Getopt ("-project= -help *") is
Which does not change the behaviour at all! Also tried:
case Getopt ("*") is
Which disallows everything, oddly enough.
Upvotes: 3
Views: 332
Reputation: 5941
I altered the example program somewhat. The following seems to work:
main.adb
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
with GNAT.Command_Line; use GNAT.Command_Line;
procedure Main is
Usage_Error : exception;
-----------------------
-- Raise_Usage_Error --
-----------------------
procedure Raise_Usage_Error (Switch : String) is
begin
raise Usage_Error with "error: unrecognized switch " & Switch;
end Raise_Usage_Error;
--------------------
-- Display_Usage --
--------------------
procedure Display_Usage is
begin
Put_Line ("usage: etc... ");
end Display_Usage;
Project : Unbounded_String;
begin
Initialize_Option_Scan (Section_Delimiters => "anything");
-- Scan the first section.
loop
case Getopt ("* project= help") is
when ASCII.Nul =>
exit;
when 'p' =>
if Full_Switch = "project" then
Project := To_Unbounded_String (Parameter);
else
Raise_Usage_Error (Full_Switch);
end if;
when 'h' =>
if Full_Switch = "help" then
Display_Usage;
return;
else
Raise_Usage_Error (Full_Switch);
end if;
when others =>
Raise_Usage_Error (Full_Switch);
end case;
end loop;
Put ("OK: project = " & To_String (Project));
-- Scan the "anything" section.
Goto_Section ("anything");
while Getopt ("*") /= ASCII.Nul loop
Put (", arg = " & Full_Switch);
end loop;
exception
when UE : Usage_Error =>
Put_Line (Exception_Message (UE));
-- Optional: Display_Usage
end Main;
test.sh
#!/bin/bash
function run_test ()
{
echo "------------------------------"
echo " input : $1"
echo " output : $($1)"
}
run_test "./obj/main -project=foo"
run_test "./obj/main -project foo"
run_test "./obj/main -help"
run_test "./obj/main -project=foo -anything ddd eee fff"
run_test "./obj/main -help -anything ddd eee fff"
run_test "./obj/main -project=foo aaa bbb ccc"
run_test "./obj/main -help aaa bbb ccc"
run_test "./obj/main -project=foo aaa bbb ccc -anything ddd eee fff"
run_test "./obj/main -help aaa bbb ccc -anything ddd eee fff"
run_test "./obj/main --sss"
run_test "./obj/main -sss"
output
$ ./test.sh
------------------------------
input : ./obj/main -project=foo
output : OK: project = foo
------------------------------
input : ./obj/main -project foo
output : OK: project = foo
------------------------------
input : ./obj/main -help
output : usage: etc...
------------------------------
input : ./obj/main -project=foo -anything ddd eee fff
output : OK: project = foo, arg = ddd, arg = eee, arg = fff
------------------------------
input : ./obj/main -help -anything ddd eee fff
output : usage: etc...
------------------------------
input : ./obj/main -project=foo aaa bbb ccc
output : error: unrecognized switch aaa
------------------------------
input : ./obj/main -help aaa bbb ccc
output : usage: etc...
------------------------------
input : ./obj/main -project=foo aaa bbb ccc -anything ddd eee fff
output : error: unrecognized switch aaa
------------------------------
input : ./obj/main -help aaa bbb ccc -anything ddd eee fff
output : usage: etc...
------------------------------
input : ./obj/main --sss
output : error: unrecognized switch --sss
------------------------------
input : ./obj/main -sss
output : error: unrecognized switch -sss
Upvotes: 1