[9/9] tools: switch to argp for command-line parsing

Message ID 87pl04nxrg.fsf@seketeli.org
State New
Headers
Series [1/9] tests/mockfedabipkgdiff.in: Fix python module loading |

Commit Message

Dodji Seketeli July 30, 2026, 6:22 p.m. UTC
  Hello,

This commit replaces the hand-written command-line parsers in all the
abigail tools with the argp facility from glibc.  This provides
automatic --help and --version handling, better error messages, and a
more consistent user interface across all tools.

	* tools/abicompat.cc: (options): Remove unknow_option,
	display_help and display_version data members.
	(display_usage): Remove.
	(enum option_key): New enum.
	(argp_options): New array of argp_option.
	(parse_opt): New function.
	(argp_args_doc, argp_doc, abicompat_argp)
	(argp_program_version_hook, argp_program_bug_address): New global
	variables.
	(parse_command_line): Overhaul to use argp_parse.
	(main): Update to use argp_help and remove handling of
	display_help, display_version and unknow_option.
	* tools/abidiff.cc (options): Remove display_usage,
	display_version, missing_operand and wrong_option data members.
	(enum option_key): New enum.
	(argp_options): New array of argp_option.
	(parse_opt): New function.
	(argp_args_doc, argp_doc, abidiff_argp, argp_program_version_hook)
	((argp_program_bug_address): New variable.
	(print_abidiff_version): New function.
	(parse_command_line): Rewrite to use argp_parse.
	(display_usage): Remove.
	(main): Update to use argp_help when no files are given, and
	remove handling of display_usage, display_version, missing_operand
	and wrong_option.
	* tools/abidw.cc (options::{wrong_option, display_version}):
	Remove data members.
	(display_usage): Remove function.
	(option_key): New enum.
	(argp_options): New array of argp_option.
	(parse_opt): New function.
	(argp_args_doc, argp_doc, abidw_argp, argp_program_version_hook)
	(argp_program_bug_address, argp_program_bug_address): New global
	variables.
	(print_abidw_version): New function.
	(parse_command_line): Overhaul to use argp_parse.
	(main): Update to use argp_help and remove handling of
	wrong_option, display_version and display_usage.
	* tools/abilint.cc (options::{wrong_option, display_version}):
	Remove data members.
	(display_usage): Remove function.
	(option_key): New enum.
	(argp_options): New array of argp_option.
	(parse_opt): New function.
	(argp_args_doc, argp_doc, abilint_argp, argp_program_version_hook)
	(argp_program_bug_address, argp_program_bug_address): New global
	variables.
	(print_abilint_version): New function.
	(parse_command_line): Overhaul to use argp_parse.
	(main): Update to use argp_help and remove handling of
	wrong_option and display_version.
	* tools/abipkgdiff.cc (options::{wrong_option, wrong_arg,
	display_usage, display_version, missing_operand}): Remove data
	members.
	(options::wrong_args): Add new data member.
	(display_usage): Remove function.
	(enum option_key): New enum.
	(argp_options): New array of argp_option.
	(parse_opt): New function.  Make --set{1,2} accept comma separated
	list of packages, in addition to the white space separated list it
	was taking previously.
	(argp_doc, argp_args_doc, abipkgdiff_argp)
	(argp_program_version_hook)
	(argp_program_bug_address, argp_program_bug_address): New global variables.
	(print_abipkgdiff_version): New function.
	(parse_command_line): Overhaul to use argp_parse.
	(main): Update to use argp_help, handle wrong_args instead of
	wrong_arg/wrong_option, and remove handling of display_usage,
	display_version and missing_operand.
	* tools/abisym.cc (options): Change elf_path and symbol_name from
	char* to string.  Remove show_help and display_version data
	members.
	(display_usage): Remove.
	(enum option_key): New enum.
	(argp_options): New array of argp_option.
	(parse_opt): New function.
	(argp_args_doc, argp_doc, abisym_argp, argp_program_version_hook)
	(argp_program_bug_address): New variable.
	(print_abisym_version): New function.
	(parse_command_line): Rewrite to use argp_parse.
	(main): Update to remove handling of show_help and
	display_version, and use base_name instead of basename for
	elf_path.
	* tools/binilint.cc (options::{display_usage,
	wrong_command_line_usage}): Remove data members.
	(options::options): Adjust.
	(enum option_key): New enum.
	(argp_options, binilint_argp): New global variables.
	(parse_opt, print_binilint_version): New function definitions.
	(parse_command_line): Overhaul to use the new parse_opt and set
	the global variables used by argp.
	* doc/manuals/abipkgdiff.rst: Adjust the documentation to reflect
	the fact that --set{1,2} can now accept comma separated values
	arguments.
	(main): Cleanup.
	* tests/data/test-diff-pkg/test-nonexistent-report-0.txt: Adjust.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>

Applied to the master branch.
---
 doc/manuals/abipkgdiff.rst                    |    6 +
 .../test-nonexistent-report-0.txt             |    2 +-
 tools/abicompat.cc                            |  410 +++---
 tools/abidiff.cc                              | 1150 +++++++++--------
 tools/abidw.cc                                |  778 ++++++-----
 tools/abilint.cc                              |  368 +++---
 tools/abipkgdiff.cc                           |  895 +++++++------
 tools/abisym.cc                               |  168 +--
 tools/binilint.cc                             |  127 +-
 9 files changed, 2130 insertions(+), 1774 deletions(-)
  

Patch

diff --git a/doc/manuals/abipkgdiff.rst b/doc/manuals/abipkgdiff.rst
index 170b87db..8efbffd4 100644
--- a/doc/manuals/abipkgdiff.rst
+++ b/doc/manuals/abipkgdiff.rst
@@ -49,10 +49,16 @@  An alternate invocation style would be:
 
   abipkgdiff [options] --set1 <pkg1-v1> <pkg2-v1> <pkg3-v1> \
                        --set2 <pkg1-v2> <pkg2-v2> <pkg3-v2>
+or
+
+  abipkgdiff [options] --set1 <pkg1-v1>,<pkg2-v1>,<pkg3-v1> \
+                       --set2 <pkg1-v2>,<pkg2-v2>,<pkg3-v2>
 
 where the ABI of binaries contained in the second set of packages are
 compared against binaries contained in the first set of packages.
 
+Packages in a set are delimited either by a white spaces or by a
+comma.
 
 Environment
 ===========
diff --git a/tests/data/test-diff-pkg/test-nonexistent-report-0.txt b/tests/data/test-diff-pkg/test-nonexistent-report-0.txt
index 8c2f0f76..cb5df908 100644
--- a/tests/data/test-diff-pkg/test-nonexistent-report-0.txt
+++ b/tests/data/test-diff-pkg/test-nonexistent-report-0.txt
@@ -1,2 +1,2 @@ 
-abipkgdiff: The input file nonexistent-0.rpm doesn't exist
+abipkgdiff: Input files nonexistent-0.rpm nonexistent-1.rpm don't exist
 try the --help option for more information
diff --git a/tools/abicompat.cc b/tools/abicompat.cc
index cb6bfffe..4546ca37 100644
--- a/tools/abicompat.cc
+++ b/tools/abicompat.cc
@@ -26,6 +26,7 @@ 
 /// library provides.
 
 #include <unistd.h>
+#include <argp.h>
 #include <cassert>
 #include <cstdio>
 #include <cstdlib>
@@ -93,7 +94,6 @@  class options
 
 public:
   string		prog_name;
-  string		unknow_option;
   string		app_path;
   string		lib1_path;
   string		lib2_path;
@@ -101,8 +101,6 @@  public:
   string		lib1_di_root_path;
   string		lib2_di_root_path;
   vector<string>	suppression_paths;
-  bool			display_help;
-  bool			display_version;
   bool			weak_mode;
   bool			list_undefined_symbols_only;
   bool			show_base_names;
@@ -121,8 +119,6 @@  public:
 
   options(const char* program_name)
     :prog_name(program_name),
-     display_help(),
-     display_version(),
      weak_mode(),
      list_undefined_symbols_only(),
      show_base_names(),
@@ -218,170 +214,6 @@  perform_compat_check_in_weak_mode(options& opts,
 				  corpus_sptr app_corpus,
 				  corpus_sptr lib_corpus);
 
-static void
-display_usage(const string& prog_name, ostream& out)
-{
-  emit_prefix(prog_name, out)
-    << "usage: " << prog_name
-    << " [options] [application-path] [lib-v1-path] [lib-v2-path]"
-    << "\n"
-    << " where options can be: \n"
-    << "  --help|-h  display this help message\n"
-    << "  --version|-v  show program version information and exit\n"
-    << "  --list-undefined-symbols|-u  display the list of "
-    "undefined symbols of the application\n"
-    << "  --show-base-names|b  in the report, only show the base names "
-    " of the files; not the full paths\n"
-    << "  --app-debug-info-dir|--appd <path-to-app-debug-info>  set the path "
-    "to the debug information directory for the application\n"
-    << "  --lib-debug-info-dir1|--libd1 <path-to-lib-debug-info1>  set the path "
-    "to the debug information directory for the first library\n"
-    << "  --lib-debug-info-dir2|--libd2 <path-to-lib-debug-info2>  set the path "
-    "to the debug information directory for the second library\n"
-    << "  --suppressions|--suppr <path> specify a suppression file\n"
-    << "  --no-redundant  do not display redundant changes\n"
-    << "  --no-show-locs  do now show location information\n"
-    << "  --ignore-soname  do not take the SONAMEs into account\n"
-    << "  --fail-no-debug-info  bail out if no debug info was found\n"
-    << "  --redundant  display redundant changes (this is the default)\n"
-    << "  --weak-mode  check compatibility between the application and "
-    "just one version of the library.\n"
-#ifdef WITH_CTF
-    << "  --ctf use CTF instead of DWARF in ELF files\n"
-#endif
-#ifdef WITH_BTF
-    << "  --btf use BTF instead of DWARF in ELF files\n"
-#endif
-    ;
-}
-
-static bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
-  if (argc < 2)
-    return false;
-
-  for (int i = 1; i < argc; ++i)
-    {
-      if (argv[i][0] != '-')
-	{
-	  if (opts.app_path.empty())
-	    opts.app_path = argv[i];
-	  else if (opts.lib1_path.empty())
-	    opts.lib1_path = argv[i];
-	  else if (opts.lib2_path.empty())
-	    opts.lib2_path = argv[i];
-	  else
-	    return false;
-	}
-      else if (!strcmp(argv[i], "--version")
-	       || !strcmp(argv[i], "-v"))
-	{
-	  opts.display_version = true;
-	  return true;
-	}
-      else if (!strcmp(argv[i], "--list-undefined-symbols")
-	       || !strcmp(argv[i], "-u"))
-	opts.list_undefined_symbols_only = true;
-      else if (!strcmp(argv[i], "--show-base-names")
-	       || !strcmp(argv[i], "-b"))
-	opts.show_base_names = true;
-      else if (!strcmp(argv[i], "--app-debug-info-dir")
-	       || !strcmp(argv[i], "--appd"))
-	{
-	  if (argc <= i + 1
-	      || argv[i + 1][0] == '-')
-	    return false;
-	  // elfutils wants the root path to the debug info to be
-	  // absolute.
-	  opts.app_di_root_path =
-	    abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--lib-debug-info-dir1")
-	       || !strcmp(argv[i], "--libd1"))
-	{
-	  if (argc <= i + 1
-	      || argv[i + 1][0] == '-')
-	    return false;
-	  // elfutils wants the root path to the debug info to be
-	  // absolute.
-	  opts.lib1_di_root_path =
-	    abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--lib-debug-info-dir2")
-	       || !strcmp(argv[i], "--libd2"))
-	{
-	  if (argc <= i + 1
-	      || argv[i + 1][0] == '-')
-	    return false;
-	  // elfutils wants the root path to the debug info to be
-	  // absolute.
-	  opts.lib2_di_root_path =
-	    abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--suppressions")
-	       || !strcmp(argv[i], "--suppr"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.suppression_paths.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--redundant"))
-        {
-	  opts.show_redundant = true;
-	  opts.redundant_opt_set = true;
-	}
-      else if (!strcmp(argv[i], "--no-redundant"))
-        {
-  	  opts.show_redundant = false;
-	  opts.no_redundant_opt_set = true;
-	}
-      else if (!strcmp(argv[i], "--no-show-locs"))
-	opts.show_locs = false;
-      else if (!strcmp(argv[i], "--ignore-soname"))
-	opts.ignore_soname=true;
-      else if (!strcmp(argv[i], "--fail-no-debug-info"))
-	opts.fail_no_debug_info = true;
-      else if (!strcmp(argv[i], "--help")
-	       || !strcmp(argv[i], "-h"))
-	{
-	  opts.display_help = true;
-	  return true;
-	}
-      else if (!strcmp(argv[i], "--weak-mode"))
-	opts.weak_mode = true;
-#ifdef WITH_CTF
-      else if (!strcmp(argv[i], "--ctf"))
-        opts.use_ctf = true;
-#endif
-#ifdef WITH_BTF
-      else if (!strcmp(argv[i], "--btf"))
-        opts.use_btf = true;
-#endif
-      else
-	{
-	  opts.unknow_option = argv[i];
-	  return false;
-	}
-    }
-
-  if (!opts.list_undefined_symbols_only)
-    {
-      if (opts.app_path.empty()
-	  || opts.lib1_path.empty())
-	return false;
-      if (!opts.weak_mode && opts.lib2_path.empty())
-	opts.weak_mode = true;
-    }
-
-  return true;
-}
-
 /// Create the context of a diff.
 ///
 /// Create the diff context, initialize it and return a smart pointer
@@ -904,45 +736,225 @@  read_corpus(options			opts,
   return retval;
 }
 
-int
-main(int argc, char* argv[])
+enum option_key
 {
-  options opts(argv[0]);
-
-  abigail::tools_utils::initialize();
+  OPT_APP_DEBUG_INFO_DIR = 256,
+  OPT_BTF,
+  OPT_CTF,
+  OPT_FAIL_NO_DEBUG_INFO,
+  OPT_IGNORE_SONAME,
+  OPT_LIB_DEBUG_INFO_DIR1,
+  OPT_LIB_DEBUG_INFO_DIR2,
+  OPT_LIST_UNDEFINED_SYMBOLS,
+  OPT_NO_REDUNDANT,
+  OPT_NO_SHOW_LOCS,
+  OPT_REDUNDANT,
+  OPT_SHOW_BASE_NAMES,
+  OPT_SUPPR,
+  OPT_WEAK_MODE,
+};
+
+static const struct argp_option argp_options[] =
+{
+  { "app-debug-info-dir", OPT_APP_DEBUG_INFO_DIR, "PATH", 0,
+    "set the path to the debug information directory for the application", 0 },
+  { "appd", OPT_APP_DEBUG_INFO_DIR, "PATH", OPTION_ALIAS, 0, 0 },
+#ifdef WITH_BTF
+  { "btf", OPT_BTF, 0, 0,
+    "use BTF instead of DWARF in ELF files", 0 },
+#endif
+#ifdef WITH_CTF
+  { "ctf", OPT_CTF, 0, 0,
+    "use CTF instead of DWARF in ELF files", 0 },
+#endif
+  { "fail-no-debug-info", OPT_FAIL_NO_DEBUG_INFO, 0, 0,
+    "bail out if no debug info was found", 0 },
+  { "ignore-soname", OPT_IGNORE_SONAME, 0, 0,
+    "do not take the SONAMEs into account", 0 },
+  { "lib-debug-info-dir1", OPT_LIB_DEBUG_INFO_DIR1, "PATH", 0,
+    "set the path to the debug information directory for the first library", 0 },
+  { "libd1", OPT_LIB_DEBUG_INFO_DIR1, "PATH", OPTION_ALIAS, 0, 0 },
+  { "lib-debug-info-dir2", OPT_LIB_DEBUG_INFO_DIR2, "PATH", 0,
+    "set the path to the debug information directory for the second library", 0 },
+  { "libd2", OPT_LIB_DEBUG_INFO_DIR2, "PATH", OPTION_ALIAS, 0, 0 },
+  { "list-undefined-symbols", OPT_LIST_UNDEFINED_SYMBOLS, 0, 0,
+    "display the list of undefined symbols of the application", 0 },
+  { "u", OPT_LIST_UNDEFINED_SYMBOLS, 0, OPTION_ALIAS, 0, 0 },
+  { "no-redundant", OPT_NO_REDUNDANT, 0, 0,
+    "do not display redundant changes", 0 },
+  { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+    "do not show location information", 0 },
+  { "redundant", OPT_REDUNDANT, 0, 0,
+    "display redundant changes (this is the default)", 0 },
+  { "show-base-names", OPT_SHOW_BASE_NAMES, 0, 0,
+    "in the report, only show the base names of the files; not the full paths",
+    0 },
+  { "b", OPT_SHOW_BASE_NAMES, 0, OPTION_ALIAS, 0, 0 },
+  { "suppressions", OPT_SUPPR, "PATH", 0,
+    "specify a suppression file", 0 },
+  { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+  { "weak-mode", OPT_WEAK_MODE, 0, 0,
+    "check compatibility between the application and just one version of "
+    "the library", 0 },
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+  options& opts = *static_cast<options*>(state->input);
+  const string argument = arg ? string(arg) : string();
 
-  if (!parse_command_line(argc, argv, opts))
+  switch (key)
     {
-      if (!opts.unknow_option.empty())
+    case OPT_APP_DEBUG_INFO_DIR:
+      opts.app_di_root_path =
+	abigail::tools_utils::make_path_absolute(argument);
+      break;
+
+#ifdef WITH_BTF
+    case OPT_BTF:
+      opts.use_btf = true;
+      break;
+#endif
+
+#ifdef WITH_CTF
+    case OPT_CTF:
+      opts.use_ctf = true;
+      break;
+#endif
+
+    case OPT_FAIL_NO_DEBUG_INFO:
+      opts.fail_no_debug_info = true;
+      break;
+
+    case OPT_IGNORE_SONAME:
+      opts.ignore_soname = true;
+      break;
+
+    case OPT_LIB_DEBUG_INFO_DIR1:
+      opts.lib1_di_root_path =
+	abigail::tools_utils::make_path_absolute(argument);
+      break;
+
+    case OPT_LIB_DEBUG_INFO_DIR2:
+      opts.lib2_di_root_path =
+	abigail::tools_utils::make_path_absolute(argument);
+      break;
+
+    case OPT_LIST_UNDEFINED_SYMBOLS:
+      opts.list_undefined_symbols_only = true;
+      break;
+
+    case OPT_NO_REDUNDANT:
+      opts.show_redundant = false;
+      opts.no_redundant_opt_set = true;
+      break;
+
+    case OPT_NO_SHOW_LOCS:
+      opts.show_locs = false;
+      break;
+
+    case OPT_REDUNDANT:
+      opts.show_redundant = true;
+      opts.redundant_opt_set = true;
+      break;
+
+    case OPT_SHOW_BASE_NAMES:
+      opts.show_base_names = true;
+      break;
+
+    case OPT_SUPPR:
+      opts.suppression_paths.push_back(argument);
+      break;
+
+    case OPT_WEAK_MODE:
+      opts.weak_mode = true;
+      break;
+
+    case ARGP_KEY_ARG:
+      if (opts.app_path.empty())
+	opts.app_path = argument;
+      else if (opts.lib1_path.empty())
+	opts.lib1_path = argument;
+      else if (opts.lib2_path.empty())
+	opts.lib2_path = argument;
+      else
+	argp_usage(state);
+      break;
+
+    case ARGP_KEY_END:
+      if (!opts.list_undefined_symbols_only)
 	{
-	  emit_prefix(argv[0], cerr)
-	    << "unrecognized option: " << opts.unknow_option << "\n"
-	    << "try the --help option for more information\n";
-	  return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-		  | abigail::tools_utils::ABIDIFF_ERROR);
+	  if (opts.app_path.empty() || opts.lib1_path.empty())
+	    argp_usage(state);
+	  if (!opts.weak_mode && opts.lib2_path.empty())
+	    opts.weak_mode = true;
 	}
+      break;
 
-      emit_prefix(argv[0], cerr)
-	<< "wrong invocation\n"
-	<< "try the --help option for more information\n";
-      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-	      | abigail::tools_utils::ABIDIFF_ERROR);
+    default:
+      return ARGP_ERR_UNKNOWN;
     }
 
-  if (opts.display_help)
-    {
-      display_usage(argv[0], cout);
-      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-		  | abigail::tools_utils::ABIDIFF_ERROR);
-    }
+  return 0;
+}
 
-  if (opts.display_version)
-    {
-      emit_prefix(argv[0], cout)
-	<< abigail::tools_utils::get_library_version_string()
-	<< "\n";
-      return 0;
-    }
+static const char* argp_args_doc = "[<application-path>] [<lib-v1-path>] [<lib-v2-path>]";
+static const char* argp_doc =
+  "Check ABI compatibility of an application against two versions of a library.";
+
+static const struct argp abicompat_argp =
+{
+  argp_options,
+  parse_opt,
+  argp_args_doc,
+  argp_doc,
+  0,
+  0,
+  0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static void
+print_abicompat_version(FILE *stream, struct argp_state* /*state*/)
+{
+  fprintf(stream, "abicompat %s\n",
+	  abigail::tools_utils::get_library_version_string().c_str());
+}
+
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
+static bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+  argp_program_bug_address = "<libabigail@sourceware.org>";
+  argp_program_version_hook = print_abicompat_version;
+
+  if (argp_parse(&abicompat_argp, argc, argv, 0, 0, &opts) != 0)
+    return false;
+  return true;
+}
+
+int
+main(int argc, char* argv[])
+{
+  options opts(argv[0]);
+
+  abigail::tools_utils::initialize();
+
+  if (!parse_command_line(argc, argv, opts))
+    return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+	    | abigail::tools_utils::ABIDIFF_ERROR);
 
   if (opts.weak_mode && !opts.lib2_path.empty())
     {
diff --git a/tools/abidiff.cc b/tools/abidiff.cc
index f263f755..e51a81ce 100644
--- a/tools/abidiff.cc
+++ b/tools/abidiff.cc
@@ -8,6 +8,7 @@ 
 /// @file
 
 #include "config.h"
+#include <argp.h>
 #include <cstring>
 #include <iostream>
 #include <memory>
@@ -71,10 +72,6 @@  using namespace abigail;
 
 struct options
 {
-  bool display_usage;
-  bool display_version;
-  bool missing_operand;
-  string		wrong_option;
   string		file1;
   string		file2;
   vector<string>	suppression_paths;
@@ -145,10 +142,7 @@  struct options
   vector<string> added_bins1;
   vector<string> added_bins2;
 
-  options()
-    : display_usage(),
-      display_version(),
-      missing_operand(),
+  options() :
       drop_private_types(false),
       linux_kernel_mode(true),
       no_default_supprs(),
@@ -210,104 +204,581 @@  struct options
   }
 };//end struct options;
 
-static void
-display_usage(const string& prog_name, ostream& out)
+
+enum option_key
 {
-  emit_prefix(prog_name, out)
-    << "usage: " << prog_name << " [options] [<file1> <file2>]\n"
-    << " where options can be:\n"
-    << " --help|-h  display this message\n "
-    << " --version|-v  display program version information and exit\n"
-    << " --debug-info-dir1|--d1 <path> the root for the debug info of file1\n"
-    << " --debug-info-dir2|--d2 <path> the root for the debug info of file2\n"
-    << " --headers-dir1|--hd1 <path>  the path to headers of file1\n"
-    << " --header-file1|--hf1 <path>  the path to one header of file1\n"
-    << " --headers-dir2|--hd2 <path>  the path to headers of file2\n"
-    << " --header-file2|--hf2 <path>  the path to one header of file2\n"
-    << " --added-binaries-dir1  the path to the dependencies of file1\n"
-    << " --added-binaries-dir2  the path to the dependencies of file2\n"
-    << " --add-binaries1 <bin1,bin2,.>. build corpus groups with "
-    "extra binaries added to the first one and compare them\n"
-    << " --add-binaries2 <bin1,bin2,..> build corpus groups with "
-    "extra binaries added to the second one and compare them\n"
-    << " --follow-dependencies|--fdeps build corpus groups with the "
-    "dependencies of the input files\n"
-    << " --list-dependencies|--ldeps show the dependencies of the input files\n"
-    << " --drop-private-types  drop private types from "
-    "internal representation\n"
-    << " --exported-interfaces-only  analyze exported interfaces only\n"
-    << " --allow-non-exported-interfaces  analyze interfaces that "
-    "might not be exported\n"
-    << " --no-linux-kernel-mode  don't consider the input binaries as "
-       "linux kernel binaries\n"
-    << " --kmi-whitelist|-w  path to a "
-       "linux kernel abi whitelist\n"
-    << " --stat  only display the diff stats\n"
-    << " --symtabs  only display the symbol tables of the corpora\n"
-    << " --no-default-suppression  don't load any "
-       "default suppression specification\n"
-    << " --no-architecture  do not take architecture in account\n"
-    << " --no-corpus-path  do not take the path to the corpora into account\n"
-    << " --ignore-soname  do not take the SONAMEs into account\n"
-    << " --fail-no-debug-info  bail out if no debug info was found\n"
-    << " --leaf-changes-only|-l  only show leaf changes, "
-    "so no change impact analysis (implies --redundant)\n"
-    << " --deleted-fns  display deleted public functions\n"
-    << " --changed-fns  display changed public functions\n"
-    << " --added-fns  display added public functions\n"
-    << " --deleted-vars  display deleted global public variables\n"
-    << " --changed-vars  display changed global public variables\n"
-    << " --added-vars  display added global public variables\n"
-    << " --non-reachable-types|-t  consider types non reachable"
-    " from public interfaces\n"
-    << " --no-added-syms  do not display added functions or variables\n"
-    << " --no-linkage-name  do not display linkage names of "
-    "added/removed/changed\n"
-    << " --no-unreferenced-symbols  do not display changes "
-    "about symbols not referenced by debug info\n"
-    << " --no-show-locs  do now show location information\n"
-    << " --show-bytes  show size and offsets in bytes\n"
-    << " --show-bits  show size and offsets in bits\n"
-    << " --show-hex  show size and offset in hexadecimal\n"
-    << " --show-dec  show size and offset in decimal\n"
-    << " --no-show-relative-offset-changes  do not show relative"
-    " offset changes\n"
-    << " --suppressions|--suppr <path> specify a suppression file\n"
-    << " --drop <regex>  drop functions and variables matching a regexp\n"
-    << " --drop-fn <regex> drop functions matching a regexp\n"
-    << " --drop-var <regex> drop variables matching a regexp\n"
-    << " --keep <regex>  keep only functions and variables matching a regex\n"
-    << " --keep-fn <regex>  keep only functions matching a regex\n"
-    << " --keep-var  <regex>  keep only variables matching a regex\n"
-    << " --harmless  display the harmless changes\n"
-    << " --no-harmful  do not display the harmful changes\n"
-    << " --redundant  display redundant changes\n"
-    << " --no-redundant  do not display redundant changes "
-    "(this is the default)\n"
-    << " --impacted-interfaces  display interfaces impacted by leaf changes\n"
-    << " --no-leverage-dwarf-factorization  do not use DWZ optimisations to "
-    "speed-up the analysis of the binary\n"
-    << " --no-change-categorization | -x don't perform categorization "
-    "of changes, for speed purposes\n"
-    << " --no-assume-odr-for-cplusplus  do not assume the ODR to speed-up the "
-    "analysis of the binary\n"
-    << " --dump-diff-tree  emit a debug dump of the internal diff tree to "
-    "the error output stream\n"
-    <<  " --stats  show statistics about various internal stuff\n"
+  OPT_ADDED_BINS_DIR1 = 256,
+  OPT_ADDED_BINS_DIR2,
+  OPT_ADDED_BINS1,
+  OPT_ADDED_BINS2,
+  OPT_ALLOW_NON_EXPORTED_INTERFACES,
+  OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS,
+  OPT_BTF,
+  OPT_CTF,
+  OPT_D1,
+  OPT_D2,
+  OPT_DEBUG_SELF_COMPARISON,
+  OPT_DEBUG_TC,
+  OPT_DELETED_FNS,
+  OPT_DELETED_VARS,
+  OPT_CHANGED_FNS,
+  OPT_CHANGED_VARS,
+  OPT_ADDED_FNS,
+  OPT_ADDED_VARS,
+  OPT_DROP,
+  OPT_DROP_FN,
+  OPT_DROP_PRIVATE_TYPES,
+  OPT_DROP_VAR,
+  OPT_DUMP_DIFF_TREE,
+  OPT_EXPORTED_INTERFACES_ONLY,
+  OPT_FAIL_NO_DEBUG_INFO,
+  OPT_FDEPS,
+  OPT_HD1,
+  OPT_HD2,
+  OPT_HF1,
+  OPT_HF2,
+  OPT_HARMLESS,
+  OPT_IGNORE_SONAME,
+  OPT_IMPACTED_INTERFACES,
+  OPT_KEEP,
+  OPT_KEEP_FN,
+  OPT_KEEP_VAR,
+  OPT_KMI_WHITELIST,
+  OPT_LDEPS,
+  OPT_LEAF_CHANGES_ONLY,
+  OPT_NO_ADDED_SYMS,
+  OPT_NO_ARCH,
+  OPT_NO_CHANGE_CATEGORIZATION,
+  OPT_NO_CORPUS_PATH,
+  OPT_NO_DEFAULT_SUPPRESSION,
+  OPT_NO_HARMFUL,
+  OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+  OPT_NO_LINKAGE_NAME,
+  OPT_NO_LINUX_KERNEL_MODE,
+  OPT_NO_REDUNDANT,
+  OPT_NO_SHOW_LOCS,
+  OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES,
+  OPT_NO_UNREFERENCED_SYMBOLS,
+  OPT_NON_REACHABLE_TYPES,
+  OPT_REDUNDANT,
+  OPT_SHOW_BITS,
+  OPT_SHOW_BYTES,
+  OPT_SHOW_DEC,
+  OPT_SHOW_HEX,
+  OPT_STAT,
+  OPT_STATS,
+  OPT_SUPPR,
+  OPT_SYMTABS,
+  OPT_VERBOSE,
+};
+
+static const struct argp_option argp_options[] =
+{
+  { "add-binaries1", OPT_ADDED_BINS1, "BIN1,BIN2,..", 0,
+    "build corpus groups with extra binaries added to the first one "
+    "and compare them", 0 },
+  { "add-binaries2", OPT_ADDED_BINS2, "BIN1,BIN2,..", 0,
+    "build corpus groups with extra binaries added to the second one "
+    "and compare them", 0 },
+  { "added-binaries-dir1", OPT_ADDED_BINS_DIR1, "PATH", 0,
+    "the path to the dependencies of file1", 0 },
+  { "abd1", OPT_ADDED_BINS_DIR1, "PATH", OPTION_ALIAS, 0, 0 },
+  { "added-binaries-dir2", OPT_ADDED_BINS_DIR2, "PATH", 0,
+    "the path to the dependencies of file2", 0 },
+  { "abd2", OPT_ADDED_BINS_DIR2, "PATH", OPTION_ALIAS, 0, 0 },
+  { "added-fns", OPT_ADDED_FNS, 0, 0,
+    "display added public functions", 0 },
+  { "added-vars", OPT_ADDED_VARS, 0, 0,
+    "display added global public variables", 0 },
+  { "allow-non-exported-interfaces", OPT_ALLOW_NON_EXPORTED_INTERFACES, 0, 0,
+    "analyze interfaces that might not be exported", 0 },
+#ifdef WITH_BTF
+  { "btf", OPT_BTF, 0, 0,
+    "use BTF instead of DWARF in ELF files", 0 },
+#endif
+  { "changed-fns", OPT_CHANGED_FNS, 0, 0,
+    "display changed public functions", 0 },
+  { "changed-vars", OPT_CHANGED_VARS, 0, 0,
+    "display changed global public variables", 0 },
 #ifdef WITH_CTF
-    << " --ctf use CTF instead of DWARF in ELF files\n"
+  { "ctf", OPT_CTF, 0, 0,
+    "use CTF instead of DWARF in ELF files", 0 },
+#endif
+  { "debug-info-dir1", OPT_D1, "PATH", 0,
+    "the root for the debug info of file1", 0 },
+  { "d1", OPT_D1, "PATH", OPTION_ALIAS, 0, 0 },
+  { "debug-info-dir2", OPT_D2, "PATH", 0,
+    "the root for the debug info of file2", 0 },
+  { "d2", OPT_D2, "PATH", OPTION_ALIAS, 0, 0 },
+#ifdef WITH_DEBUG_SELF_COMPARISON
+  { "debug-self-comparison", OPT_DEBUG_SELF_COMPARISON, 0, 0,
+    "debug the process of comparing an ABI corpus against itself", 0 },
 #endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+  { "debug-tc", OPT_DEBUG_TC, 0, 0,
+    "debug the type canonicalization process", 0 },
+#endif
+  { "deleted-fns", OPT_DELETED_FNS, 0, 0,
+    "display deleted public functions", 0 },
+  { "deleted-vars", OPT_DELETED_VARS, 0, 0,
+    "display deleted global public variables", 0 },
+  { "drop", OPT_DROP, "REGEX", 0,
+    "drop functions and variables matching a regexp", 0 },
+  { "drop-fn", OPT_DROP_FN, "REGEX", 0,
+    "drop functions matching a regexp", 0 },
+  { "drop-private-types", OPT_DROP_PRIVATE_TYPES, 0, 0,
+    "drop private types from internal representation", 0 },
+  { "drop-var", OPT_DROP_VAR, "REGEX", 0,
+    "drop variables matching a regexp", 0 },
+  { "dump-diff-tree", OPT_DUMP_DIFF_TREE, 0, 0,
+    "emit a debug dump of the internal diff tree to the error output stream",
+    0 },
+  { "exported-interfaces-only", OPT_EXPORTED_INTERFACES_ONLY, 0, 0,
+    "analyze exported interfaces only", 0 },
+  { "fail-no-debug-info", OPT_FAIL_NO_DEBUG_INFO, 0, 0,
+    "bail out if no debug info was found", 0 },
+  { "follow-dependencies", OPT_FDEPS, 0, 0,
+    "build corpus groups with the dependencies of the input files", 0 },
+  { "fdeps", OPT_FDEPS, 0, OPTION_ALIAS, 0, 0 },
+  { "harmless", OPT_HARMLESS, 0, 0,
+    "display the harmless changes", 0 },
+  { "header-file1", OPT_HF1, "PATH", 0,
+    "the path to one header of file1", 0 },
+  { "hf1", OPT_HF1, "PATH", OPTION_ALIAS, 0, 0 },
+  { "header-file2", OPT_HF2, "PATH", 0,
+    "the path to one header of file2", 0 },
+  { "hf2", OPT_HF2, "PATH", OPTION_ALIAS, 0, 0 },
+  { "headers-dir1", OPT_HD1, "PATH", 0,
+    "the path to headers of file1", 0 },
+  { "hd1", OPT_HD1, "PATH", OPTION_ALIAS, 0, 0 },
+  { "headers-dir2", OPT_HD2, "PATH", 0,
+    "the path to headers of file2", 0 },
+  { "hd2", OPT_HD2, "PATH", OPTION_ALIAS, 0, 0 },
+  { "ignore-soname", OPT_IGNORE_SONAME, 0, 0,
+    "do not take the SONAMEs into account", 0 },
+  { "impacted-interfaces", OPT_IMPACTED_INTERFACES, 0, 0,
+    "display interfaces impacted by leaf changes", 0 },
+  { "keep", OPT_KEEP, "REGEX", 0,
+    "keep only functions and variables matching a regex", 0 },
+  { "keep-fn", OPT_KEEP_FN, "REGEX", 0,
+    "keep only functions matching a regex", 0 },
+  { "keep-var", OPT_KEEP_VAR, "REGEX", 0,
+    "keep only variables matching a regex", 0 },
+  { "kmi-whitelist", OPT_KMI_WHITELIST, "PATH", 0,
+    "path to a linux kernel abi whitelist", 0 },
+  { "w", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+  { "leaf-changes-only", OPT_LEAF_CHANGES_ONLY, 0, 0,
+    "only show leaf changes, so no change impact analysis "
+    "(implies --redundant)", 0 },
+  { "l", OPT_LEAF_CHANGES_ONLY, 0, OPTION_ALIAS, 0, 0 },
+  { "list-dependencies", OPT_LDEPS, 0, 0,
+    "show the dependencies of the input files", 0 },
+  { "ldeps", OPT_LDEPS, 0, OPTION_ALIAS, 0, 0 },
+  { "no-added-syms", OPT_NO_ADDED_SYMS, 0, 0,
+    "do not display added functions or variables", 0 },
+  { "no-architecture", OPT_NO_ARCH, 0, 0,
+    "do not take architecture in account", 0 },
+  { "no-assume-odr-for-cplusplus", OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS, 0, 0,
+    "do not assume the ODR to speed-up the analysis of the binary", 0 },
+  { "no-change-categorization", OPT_NO_CHANGE_CATEGORIZATION, 0, 0,
+    "don't perform categorization of changes, for speed purposes", 0 },
+  { "x", OPT_NO_CHANGE_CATEGORIZATION, 0, OPTION_ALIAS, 0, 0 },
+  { "no-corpus-path", OPT_NO_CORPUS_PATH, 0, 0,
+    "do not take the path to the corpora into account", 0 },
+  { "no-default-suppression", OPT_NO_DEFAULT_SUPPRESSION, 0, 0,
+    "don't load any default suppression specification", 0 },
+  { "no-harmful", OPT_NO_HARMFUL, 0, 0,
+    "do not display the harmful changes", 0 },
+  { "no-leverage-dwarf-factorization", OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+    0, 0,
+    "do not use DWZ optimisations to speed-up the analysis of the binary",
+    0 },
+  { "no-linkage-name", OPT_NO_LINKAGE_NAME, 0, 0,
+    "do not display linkage names of added/removed/changed", 0 },
+  { "no-linux-kernel-mode", OPT_NO_LINUX_KERNEL_MODE, 0, 0,
+    "don't consider the input binaries as linux kernel binaries", 0 },
+  { "no-redundant", OPT_NO_REDUNDANT, 0, 0,
+    "do not display redundant changes (this is the default)", 0 },
+  { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+    "do not show location information", 0 },
+  { "no-show-relative-offset-changes", OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES,
+    0, 0,
+    "do not show relative offset changes", 0 },
+  { "no-unreferenced-symbols", OPT_NO_UNREFERENCED_SYMBOLS, 0, 0,
+    "do not display changes about symbols not referenced by debug info", 0 },
+  { "non-reachable-types", OPT_NON_REACHABLE_TYPES, 0, 0,
+    "consider types non reachable from public interfaces", 0 },
+  { "t", OPT_NON_REACHABLE_TYPES, 0, OPTION_ALIAS, 0, 0 },
+  { "redundant", OPT_REDUNDANT, 0, 0,
+    "display redundant changes", 0 },
+  { "show-bits", OPT_SHOW_BITS, 0, 0,
+    "show size and offsets in bits", 0 },
+  { "show-bytes", OPT_SHOW_BYTES, 0, 0,
+    "show size and offsets in bytes", 0 },
+  { "show-dec", OPT_SHOW_DEC, 0, 0,
+    "show size and offset in decimal", 0 },
+  { "show-hex", OPT_SHOW_HEX, 0, 0,
+    "show size and offset in hexadecimal", 0 },
+  { "stat", OPT_STAT, 0, 0,
+    "only display the diff stats", 0 },
+  { "stats", OPT_STATS, 0, 0,
+    "show statistics about various internal stuff", 0 },
+  { "suppressions", OPT_SUPPR, "PATH", 0,
+    "specify a suppression file", 0 },
+  { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+  { "symtabs", OPT_SYMTABS, 0, 0,
+    "only display the symbol tables of the corpora", 0 },
+  { "verbose", OPT_VERBOSE, 0, 0,
+    "show verbose messages about internal stuff", 0 },
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+  options& opts = *static_cast<options*>(state->input);
+  const string argument = arg ? string(arg) : string();
+
+  switch (key)
+    {
+    case OPT_ADDED_BINS1:
+      if (argument.find(',') != string::npos)
+	tools_utils::split_string(argument, ",", opts.added_bins1);
+      else
+	opts.added_bins1.push_back(argument);
+      break;
+
+    case OPT_ADDED_BINS2:
+      if (argument.find(',') != string::npos)
+	tools_utils::split_string(argument, ",", opts.added_bins2);
+      else
+	opts.added_bins2.push_back(argument);
+      break;
+
+    case OPT_ADDED_BINS_DIR1:
+      opts.added_bins_dirs1.push_back(argument);
+      break;
+
+    case OPT_ADDED_BINS_DIR2:
+      opts.added_bins_dirs2.push_back(argument);
+      break;
+
+    case OPT_ADDED_FNS:
+      opts.show_added_fns = true;
+      opts.show_all_fns = false;
+      opts.show_all_vars = false;
+      break;
+
+    case OPT_ADDED_VARS:
+      opts.show_added_vars = true;
+      opts.show_all_fns = false;
+      opts.show_all_vars = false;
+      break;
+
+    case OPT_ALLOW_NON_EXPORTED_INTERFACES:
+      opts.exported_interfaces_only = false;
+      break;
+
+    case OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS:
+      opts.assume_odr_for_cplusplus = false;
+      break;
+
 #ifdef WITH_BTF
-    << " --btf use BTF instead of DWARF in ELF files\n"
+    case OPT_BTF:
+      opts.use_btf = true;
+      break;
 #endif
+
+    case OPT_CHANGED_FNS:
+      opts.show_changed_fns = true;
+      opts.show_all_fns = false;
+      opts.show_all_vars = false;
+      break;
+
+    case OPT_CHANGED_VARS:
+      opts.show_changed_vars = true;
+      opts.show_all_fns = false;
+      opts.show_all_vars = false;
+      break;
+
+#ifdef WITH_CTF
+    case OPT_CTF:
+      opts.use_ctf = true;
+      break;
+#endif
+
+    case OPT_D1:
+      opts.di_root_paths1.push_back
+	(abigail::tools_utils::make_path_absolute(argument));
+      break;
+
+    case OPT_D2:
+      opts.di_root_paths2.push_back
+	(abigail::tools_utils::make_path_absolute(argument));
+      break;
+
 #ifdef WITH_DEBUG_SELF_COMPARISON
-    << " --debug-self-comparison debug the process of comparing "
-    "an ABI corpus against itself"
+    case OPT_DEBUG_SELF_COMPARISON:
+      opts.do_debug_self_comparison = true;
+      break;
 #endif
+
 #ifdef WITH_DEBUG_TYPE_CANONICALIZATION
-    << " --debug-tc debug the type canonicalization process"
+    case OPT_DEBUG_TC:
+      opts.do_debug_type_canonicalization = true;
+      break;
 #endif
-    << " --verbose show verbose messages about internal stuff\n";
+
+    case OPT_DELETED_FNS:
+      opts.show_deleted_fns = true;
+      opts.show_all_fns = false;
+      opts.show_all_vars = false;
+      break;
+
+    case OPT_DELETED_VARS:
+      opts.show_deleted_vars = true;
+      opts.show_all_fns = false;
+      opts.show_all_vars = false;
+      break;
+
+    case OPT_DROP:
+      opts.drop_fn_regex_patterns.push_back(argument);
+      opts.drop_var_regex_patterns.push_back(argument);
+      break;
+
+    case OPT_DROP_FN:
+      opts.drop_fn_regex_patterns.push_back(argument);
+      break;
+
+    case OPT_DROP_PRIVATE_TYPES:
+      opts.drop_private_types = true;
+      break;
+
+    case OPT_DROP_VAR:
+      opts.drop_var_regex_patterns.push_back(argument);
+      break;
+
+    case OPT_DUMP_DIFF_TREE:
+      opts.dump_diff_tree = true;
+      break;
+
+    case OPT_EXPORTED_INTERFACES_ONLY:
+      opts.exported_interfaces_only = true;
+      break;
+
+    case OPT_FAIL_NO_DEBUG_INFO:
+      opts.fail_no_debug_info = true;
+      break;
+
+    case OPT_FDEPS:
+      opts.follow_dependencies = true;
+      break;
+
+    case OPT_HARMLESS:
+      opts.show_harmless_changes = true;
+      break;
+
+    case OPT_HD1:
+      opts.headers_dirs1.push_back(argument);
+      break;
+
+    case OPT_HD2:
+      opts.headers_dirs2.push_back(argument);
+      break;
+
+    case OPT_HF1:
+      opts.header_files1.push_back(argument);
+      break;
+
+    case OPT_HF2:
+      opts.header_files2.push_back(argument);
+      break;
+
+    case OPT_IGNORE_SONAME:
+      opts.ignore_soname = true;
+      break;
+
+    case OPT_IMPACTED_INTERFACES:
+      opts.show_impacted_interfaces = true;
+      break;
+
+    case OPT_KEEP:
+      opts.keep_fn_regex_patterns.push_back(argument);
+      opts.keep_var_regex_patterns.push_back(argument);
+      break;
+
+    case OPT_KEEP_FN:
+      opts.keep_fn_regex_patterns.push_back(argument);
+      break;
+
+    case OPT_KEEP_VAR:
+      opts.keep_var_regex_patterns.push_back(argument);
+      break;
+
+    case OPT_KMI_WHITELIST:
+      opts.kernel_abi_whitelist_paths.push_back(argument);
+      break;
+
+    case OPT_LDEPS:
+      opts.list_dependencies = true;
+      break;
+
+    case OPT_LEAF_CHANGES_ONLY:
+      opts.leaf_changes_only = true;
+      break;
+
+    case OPT_NO_ADDED_SYMS:
+      opts.show_added_syms = false;
+      opts.show_added_vars = false;
+      opts.show_added_fns = false;
+      if (!(opts.show_changed_fns
+	    || opts.show_changed_vars
+	    || opts.show_deleted_fns
+	    || opts.show_deleted_vars))
+	{
+	  opts.show_changed_fns = true;
+	  opts.show_changed_vars = true;
+	  opts.show_deleted_vars = true;
+	  opts.show_deleted_fns = true;
+	}
+      opts.show_all_fns = false;
+      opts.show_all_vars = false;
+      break;
+
+    case OPT_NO_ARCH:
+      opts.no_arch = true;
+      break;
+
+    case OPT_NO_CHANGE_CATEGORIZATION:
+      opts.perform_change_categorization = false;
+      break;
+
+    case OPT_NO_CORPUS_PATH:
+      opts.no_corpus = true;
+      break;
+
+    case OPT_NO_DEFAULT_SUPPRESSION:
+      opts.no_default_supprs = true;
+      break;
+
+    case OPT_NO_HARMFUL:
+      opts.show_harmful_changes = false;
+      break;
+
+    case OPT_NO_LEVERAGE_DWARF_FACTORIZATION:
+      opts.leverage_dwarf_factorization = false;
+      break;
+
+    case OPT_NO_LINKAGE_NAME:
+      opts.show_linkage_names = false;
+      break;
+
+    case OPT_NO_LINUX_KERNEL_MODE:
+      opts.linux_kernel_mode = false;
+      break;
+
+    case OPT_NO_REDUNDANT:
+      opts.show_redundant_changes = false;
+      break;
+
+    case OPT_NO_SHOW_LOCS:
+      opts.show_locs = false;
+      break;
+
+    case OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES:
+      opts.show_relative_offset_changes = false;
+      break;
+
+    case OPT_NO_UNREFERENCED_SYMBOLS:
+      opts.show_symbols_not_referenced_by_debug_info = false;
+      break;
+
+    case OPT_NON_REACHABLE_TYPES:
+      opts.show_all_types = true;
+      break;
+
+    case OPT_REDUNDANT:
+      opts.show_redundant_changes = true;
+      break;
+
+    case OPT_SHOW_BITS:
+      opts.show_offsets_sizes_in_bits = true;
+      break;
+
+    case OPT_SHOW_BYTES:
+      opts.show_offsets_sizes_in_bits = false;
+      break;
+
+    case OPT_SHOW_DEC:
+      opts.show_hexadecimal_values = false;
+      break;
+
+    case OPT_SHOW_HEX:
+      opts.show_hexadecimal_values = true;
+      break;
+
+    case OPT_STAT:
+      opts.show_stats_only = true;
+      break;
+
+    case OPT_STATS:
+      opts.show_stats = true;
+      break;
+
+    case OPT_SUPPR:
+      opts.suppression_paths.push_back(argument);
+      break;
+
+    case OPT_SYMTABS:
+      opts.show_symtabs = true;
+      break;
+
+    case OPT_VERBOSE:
+      opts.do_log = true;
+      break;
+
+    case ARGP_KEY_ARG:
+      if (opts.file1.empty())
+	opts.file1 = argument;
+      else if (opts.file2.empty())
+	opts.file2 = argument;
+      else
+	argp_usage(state);
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static const char* argp_args_doc = "[<file1> <file2>]";
+static const char* argp_doc =
+  "Compare the ABI of two ELF files, returning 0 if they are identical, "
+  "or non-zero if they differ.";
+
+static const struct argp abidiff_argp =
+{
+  argp_options,
+  parse_opt,
+  argp_args_doc,
+  argp_doc,
+  0,
+  0,
+  0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static void
+print_abidiff_version(FILE *stream, struct argp_state* /*state*/)
+{
+  fprintf(stream, "abidiff %s\n",
+	  abigail::tools_utils::get_library_version_string().c_str());
 }
 
 /// Parse the command line and set the options accordingly.
@@ -324,445 +795,11 @@  display_usage(const string& prog_name, ostream& out)
 bool
 parse_command_line(int argc, char* argv[], options& opts)
 {
-  if (argc < 2)
-    return false;
-
-  for (int i = 1; i < argc; ++i)
-    {
-      if (argv[i][0] != '-')
-	{
-	  if (opts.file1.empty())
-	    opts.file1 = argv[i];
-	  else if (opts.file2.empty())
-	    opts.file2 = argv[i];
-	  else
-	    return false;
-	}
-      else if (!strcmp(argv[i], "--version")
-	       || !strcmp(argv[i], "-v"))
-	{
-	  opts.display_version = true;
-	  return true;
-	}
-      else if (!strcmp(argv[i], "--debug-info-dir1")
-	       || !strcmp(argv[i], "--d1"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  // elfutils wants the root path to the debug info to be
-	  // absolute.
-	  opts.di_root_paths1.push_back
-	    (abigail::tools_utils::make_path_absolute(string(argv[j])));
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--debug-info-dir2")
-	       || !strcmp(argv[i], "--d2"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  // elfutils wants the root path to the debug info to be
-	  // absolute.
-	  opts.di_root_paths2.push_back
-	    (abigail::tools_utils::make_path_absolute(string(argv[j])));
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--headers-dir1")
-	       || !strcmp(argv[i], "--hd1"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  // The user can specify several header files directories for
-	  // the first binary.
-	  opts.headers_dirs1.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--header-file1")
-	       || !strcmp(argv[i], "--hf1"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.header_files1.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--headers-dir2")
-	       || !strcmp(argv[i], "--hd2"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  // The user can specify several header files directories for
-	  // the first binary.
-	  opts.headers_dirs2.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--header-file2")
-	       || !strcmp(argv[i], "--hf2"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.header_files2.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--follow-dependencies")
-	       || !strcmp(argv[i], "--fdeps"))
-	opts.follow_dependencies = true;
-      else if (!strcmp(argv[i], "--list-dependencies")
-	       || !strcmp(argv[i], "--ldeps"))
-	opts.list_dependencies = true;
-      else if (!strcmp(argv[i], "--added-binaries-dir1")
-	       || !strcmp(argv[i], "--abd1"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.added_bins_dirs1.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--added-binaries-dir2")
-	       || !strcmp(argv[i], "--abd2"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.added_bins_dirs2.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strncmp(argv[i], "--add-binaries1=",
-			strlen("--add-binaries1=")))
-	tools_utils::get_comma_separated_args_of_option(argv[i],
-							"--add-binaries1=",
-							opts.added_bins1);
-      else if (!strcmp(argv[i], "--add-binaries1"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  string s = argv[j];
-	  if (s.find(','))
-	    tools_utils::split_string(s, ",", opts.added_bins1);
-	  else
-	    opts.added_bins1.push_back(s);
-	  ++i;
-	}
-      else if (!strncmp(argv[i], "--add-binaries2=",
-			strlen("--add-binaries2=")))
-	tools_utils::get_comma_separated_args_of_option(argv[i],
-							"--add-binaries2=",
-							opts.added_bins2);
-      else if (!strcmp(argv[i], "--add-binaries2"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  string s = argv[j];
-	  if (s.find(','))
-	    tools_utils::split_string(s, ",", opts.added_bins2);
-	  else
-	    opts.added_bins2.push_back(s);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--kmi-whitelist")
-	       || !strcmp(argv[i], "-w"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.kernel_abi_whitelist_paths.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--stat"))
-	opts.show_stats_only = true;
-      else if (!strcmp(argv[i], "--symtabs"))
-	opts.show_symtabs = true;
-      else if (!strcmp(argv[i], "--help")
-	       || !strcmp(argv[i], "-h"))
-	{
-	  opts.display_usage = true;
-	  return true;
-	}
-      else if (!strcmp(argv[i], "--drop-private-types"))
-	opts.drop_private_types = true;
-      else if (!strcmp(argv[i], "--exported-interfaces-only"))
-	opts.exported_interfaces_only = true;
-      else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
-	opts.exported_interfaces_only = false;
-      else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
-	opts.linux_kernel_mode = false;
-      else if (!strcmp(argv[i], "--no-default-suppression"))
-	opts.no_default_supprs = true;
-      else if (!strcmp(argv[i], "--no-architecture"))
-	opts.no_arch = true;
-      else if (!strcmp(argv[i], "--no-corpus-path"))
-	opts.no_corpus = true;
-      else if (!strcmp(argv[i], "--ignore-soname"))
-	opts.ignore_soname = true;
-      else if (!strcmp(argv[i], "--fail-no-debug-info"))
-	opts.fail_no_debug_info = true;
-      else if (!strcmp(argv[i], "--leaf-changes-only")
-	       ||!strcmp(argv[i], "-l"))
-	opts.leaf_changes_only = true;
-      else if (!strcmp(argv[i], "--deleted-fns"))
-	{
-	  opts.show_deleted_fns = true;
-	  opts.show_all_fns = false;
-	  opts.show_all_vars = false;
-	}
-      else if (!strcmp(argv[i], "--changed-fns"))
-	{
-	  opts.show_changed_fns = true;
-	  opts.show_all_fns = false;
-	  opts.show_all_vars = false;
-	}
-      else if (!strcmp(argv[i], "--added-fns"))
-	{
-	  opts.show_added_fns = true;
-	  opts.show_all_fns = false;
-	  opts.show_all_vars = false;
-	}
-      else if (!strcmp(argv[i], "--deleted-vars"))
-	{
-	  opts.show_deleted_vars = true;
-	  opts.show_all_fns = false;
-	  opts.show_all_vars = false;
-	}
-      else if (!strcmp(argv[i], "--changed-vars"))
-	{
-	  opts.show_changed_vars = true;
-	  opts.show_all_fns = false;
-	  opts.show_all_vars = false;
-	}
-      else if (!strcmp(argv[i], "--added-vars"))
-	{
-	  opts.show_added_vars = true;
-	  opts.show_all_fns = false;
-	  opts.show_all_vars = false;
-	}
-      else if (!strcmp(argv[i], "--non-reachable-types")
-	       || !strcmp(argv[i], "-t"))
-	  opts.show_all_types = true;
-      else if (!strcmp(argv[i], "--no-added-syms"))
-	{
-	  opts.show_added_syms = false;
-	  opts.show_added_vars = false;
-	  opts.show_added_fns = false;
-
-	  // If any of the {changed,deleted}_{vars,fns} is already
-	  // specified, --no-added-syms has no further effect.  If it
-	  // is the only option specified (as of the time of parsing
-	  // it), it shall mean "show everything, except added vars,
-	  // fns and unreferenced symbols.
-	  if (!(opts.show_changed_fns
-		|| opts.show_changed_vars
-		|| opts.show_deleted_fns
-		|| opts.show_deleted_vars))
-	    {
-	      opts.show_changed_fns = true;
-	      opts.show_changed_vars = true;
-
-	      opts.show_deleted_vars = true;
-	      opts.show_deleted_fns = true;
-	    }
-
-	  opts.show_all_fns = false;
-	  opts.show_all_vars = false;
-	}
-      else if (!strcmp(argv[i], "--no-linkage-name"))
-	opts.show_linkage_names = false;
-      else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
-	opts.show_symbols_not_referenced_by_debug_info = false;
-      else if (!strcmp(argv[i], "--no-show-locs"))
-	opts.show_locs = false;
-      else if (!strcmp(argv[i], "--show-bytes"))
-	opts.show_offsets_sizes_in_bits = false;
-      else if (!strcmp(argv[i], "--show-bits"))
-	opts.show_offsets_sizes_in_bits = true;
-      else if (!strcmp(argv[i], "--show-hex"))
-	opts.show_hexadecimal_values = true;
-      else if (!strcmp(argv[i], "--show-dec"))
-	opts.show_hexadecimal_values = false;
-      else if (!strcmp(argv[i], "--no-show-relative-offset-changes"))
-	opts.show_relative_offset_changes = false;
-      else if (!strcmp(argv[i], "--suppressions")
-	       || !strcmp(argv[i], "--suppr"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.suppression_paths.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--drop"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.drop_fn_regex_patterns.push_back(argv[j]);
-	  opts.drop_var_regex_patterns.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--drop-fn"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.drop_fn_regex_patterns.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--drop-var"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.drop_var_regex_patterns.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--keep"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.keep_fn_regex_patterns.push_back(argv[j]);
-	  opts.keep_var_regex_patterns.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--keep-fn"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.keep_fn_regex_patterns.push_back(argv[j]);
-	}
-      else if (!strcmp(argv[i], "--keep-var"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.keep_var_regex_patterns.push_back(argv[j]);
-	}
-      else if (!strcmp(argv[i], "--harmless"))
-	opts.show_harmless_changes = true;
-      else if (!strcmp(argv[i], "--no-harmful"))
-	opts.show_harmful_changes = false;
-      else if (!strcmp(argv[i], "--redundant"))
-	opts.show_redundant_changes = true;
-      else if (!strcmp(argv[i], "--no-redundant"))
-	opts.show_redundant_changes = false;
-      else if (!strcmp(argv[i], "--impacted-interfaces"))
-	opts.show_impacted_interfaces = true;
-      else if (!strcmp(argv[i], "--no-leverage-dwarf-factorization"))
-	opts.leverage_dwarf_factorization = false;
-      else if (!strcmp(argv[i], "--no-change-categorization")
-	       || !strcmp(argv[i], "-x"))
-	opts.perform_change_categorization = false;
-      else if (!strcmp(argv[i], "--no-assume-odr-for-cplusplus"))
-	opts.leverage_dwarf_factorization = false;
-      else if (!strcmp(argv[i], "--dump-diff-tree"))
-	opts.dump_diff_tree = true;
-      else if (!strcmp(argv[i], "--stats"))
-	opts.show_stats = true;
-      else if (!strcmp(argv[i], "--verbose"))
-	opts.do_log = true;
-#ifdef WITH_CTF
-      else if (!strcmp(argv[i], "--ctf"))
-        opts.use_ctf = true;
-#endif
-#ifdef WITH_BTF
-      else if (!strcmp(argv[i], "--btf"))
-        opts.use_btf = true;
-#endif
-#ifdef WITH_DEBUG_SELF_COMPARISON
-      else if (!strcmp(argv[i], "--debug-self-comparison"))
-	opts.do_debug_self_comparison = true;
-#endif
-#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
-      else if (!strcmp(argv[i], "--debug-tc"))
-	opts.do_debug_type_canonicalization = true;
-#endif
-      else
-	{
-	  if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
-	    opts.wrong_option = argv[i];
-	  return false;
-	}
-    }
+  argp_program_version_hook = print_abidiff_version;
+  argp_program_bug_address = "<libabigail@sourceware.org>";
 
+  if (argp_parse(&abidiff_argp, argc, argv, 0, 0, &opts) != 0)
+    return false;
   return true;
 }
 
@@ -1287,38 +1324,8 @@  main(int argc, char* argv[])
 
   options opts;
   if (!parse_command_line(argc, argv, opts))
-    {
-      emit_prefix(argv[0], cerr)
-	<< "unrecognized option: "
-	<< opts.wrong_option << "\n"
-	<< "try the --help option for more information\n";
-      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-	      | abigail::tools_utils::ABIDIFF_ERROR);
-    }
-
-  if (opts.missing_operand)
-    {
-      emit_prefix(argv[0], cerr)
-	<< "missing operand to option: " << opts.wrong_option <<"\n"
-	<< "try the --help option for more information\n";
-      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-	      | abigail::tools_utils::ABIDIFF_ERROR);
-    }
-
-  if (opts.display_usage)
-    {
-      display_usage(argv[0], cout);
-      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-	      | abigail::tools_utils::ABIDIFF_ERROR);
-    }
-
-  if (opts.display_version)
-    {
-      emit_prefix(argv[0], cout)
-	<< abigail::tools_utils::get_library_version_string()
-	<< "\n";
-      return 0;
-    }
+    return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+	    | abigail::tools_utils::ABIDIFF_ERROR);
 
   if (!maybe_check_suppression_files(opts))
     return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
@@ -1859,6 +1866,15 @@  main(int argc, char* argv[])
       else
 	status = abigail::tools_utils::ABIDIFF_ERROR;
     }
+  else
+    {
+      char* prog_name = (char*) "abidiff";
+      emit_prefix(prog_name, cerr)
+	<< "At least one of the files to compare is missing\n";
+      argp_help(&abidiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
+      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
+	      | abigail::tools_utils::ABIDIFF_ERROR);
+    }
 
   return status;
 }
diff --git a/tools/abidw.cc b/tools/abidw.cc
index 4cc60f84..fd466638 100644
--- a/tools/abidw.cc
+++ b/tools/abidw.cc
@@ -12,6 +12,7 @@ 
 /// libabigail XML format.
 
 #include "config.h"
+#include <argp.h>
 #include <unistd.h>
 #include <cassert>
 #include <cstdio>
@@ -84,7 +85,6 @@  using namespace abigail;
 
 struct options
 {
-  string		wrong_option;
   string		in_file_path;
   string		out_file_path;
   vector<string>	di_root_paths;
@@ -96,7 +96,6 @@  struct options
   vector<string>	suppression_paths;
   vector<string>	kabi_whitelist_paths;
   suppressions_type	kabi_whitelist_supprs;
-  bool			display_version;
   bool			display_abixml_version;
   bool			fail_no_debug_info;
   bool			check_alt_debug_info_path;
@@ -145,8 +144,7 @@  struct options
 #endif
 
   options()
-    : display_version(),
-      display_abixml_version(),
+    : display_abixml_version(),
       fail_no_debug_info(),
       check_alt_debug_info_path(),
       show_base_name_alt_debug_info_path(),
@@ -195,324 +193,6 @@  struct options
   }
 };
 
-static void
-display_usage(const string& prog_name, ostream& out)
-{
-  emit_prefix(prog_name, out)
-    << "usage: " << prog_name << " [options] [<path-to-elf-file>]\n"
-    << " where options can be: \n"
-    << "  --abidiff  compare the loaded ABI against itself\n"
-    << "  --abixml-version  display the version of the ABIXML ABI format\n"
-    << "  --add-binaries <bin1,bin2,...>  build a corpus group with "
-    "the added inaries\n"
-    << "  --allow-non-exported-interfaces analyze interfaces that"
-    "might not be exported\n"
-    << "  --annotate  annotate the ABI artifacts emitted in the output\n"
-#ifdef WITH_BTF
-    << "  --btf use BTF instead of DWARF in ELF files\n"
-#endif
-    << "  --check-alternate-debug-info <elf-path>  check alternate debug info "
-    "of <elf-path>\n"
-    << "  --check-alternate-debug-info-base-name <elf-path>  check alternate "
-    "debug info of <elf-path>, and show its base name\n"
-#ifdef WITH_CTF
-    << "  --ctf use CTF instead of DWARF in ELF files\n"
-#endif
-#ifdef WITH_DEBUG_SELF_COMPARISON
-    << "  --debug-abidiff  debug the process of comparing the loaded ABI against itself\n"
-#endif
-    << "  --debug-info-dir|-d <dir-path>  look for debug info under 'dir-path'\n"
-#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
-    << "  --debug-tc  debug the type canonicalization process\n"
-    << "  --debug-dc  debug the DIE canonicalization process\n"
-#endif
-    << "  --drop-undefined-syms  drop undefined symbols from representation\n"
-    << "  --exported-interfaces-only  analyze exported interfaces only\n"
-    << "  --follow-dependencies  build a corpus group with the dependencies\n"
-    << "  --follow-dependencies  build a corpus group with the dependencies\n"
-    << "  --force-early-suppression  drop IR nodes that match suppression specifications\n"
-    << "  --headers-dir|--hd <path> the path to headers of the elf file\n"
-    << "  --header-file|--hf <path> the path one header of the elf file\n"
-    << "  --help|-h  display this message\n"
-    << "  --kmi-whitelist|--kmi-stablelist|-w  path to a linux kernel "
-    "abi whitelist\n"
-    << "  --list-dependencies  list the dependencies of a given binary\n"
-    << "  --added-binaries-dir|--abd <dir-of-deps>  where to look for dependencies "
-    "or added binaries\n"
-    << "  --linux-tree|--lt  emit the ABI for the union of a "
-    "vmlinux and its modules\n"
-    << "  --load-all-types  read all types including those not reachable from "
-    "exported declarations\n"
-    << "  --no-architecture  do not emit architecture info in the output\n"
-    << "  --no-assume-odr-for-cplusplus  do not assume the ODR to speed-up the "
-    "analysis of the binary\n"
-    << "  --no-comp-dir-path  do not show compilation path information\n"
-    << "  --no-corpus-path  do not take the path to the corpora into account\n"
-    << "  --no-elf-needed  do not show the DT_NEEDED information\n"
-    << "  --no-leverage-dwarf-factorization  do not use DWZ optimisations to "
-    "speed-up the analysis of the binary\n"
-    << "  --no-linux-kernel-mode  don't consider the input binary as "
-       "a Linux Kernel binary\n"
-    << "  --no-load-undefined-interfaces  do not consider undefined "
-    "interfaces from the binary"
-    << "  --no-parameter-names  do not show names of function parameters\n"
-    << "  --no-show-locs  do not show location information\n"
-    << "  --no-write-default-sizes  do not emit pointer size when it equals"
-    " the default address size of the translation unit\n"
-    << "  --noout  do not emit anything after reading the binary\n"
-    << "  --out-file|-o  <file-path>  write the output to 'file-path'\n"
-    << "  --short-locs  only print filenames rather than paths\n"
-    << "  --suppressions|--suppr <path> specify a suppression file\n"
-    << "  --type-id-style <sequence|hash>  type id style (sequence(default): "
-       "\"type-id-\" + number; hash: hex-digits)\n"
-    << "  --stats  show statistics about various internal stuff\n"
-    << "  --verbose show verbose messages about internal stuff\n"
-    << "  --version|-v  display program version information and exit\n"
-    << "  --vmlinux <path>  the path to the vmlinux binary to consider to emit "
-       "the ABI of the union of vmlinux and its modules\n" ;
-}
-
-static bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
-  if (argc < 2)
-    return false;
-
-  for (int i = 1; i < argc; ++i)
-    {
-      if (argv[i][0] != '-')
-	{
-	  if (opts.in_file_path.empty())
-	    opts.in_file_path = argv[i];
-	  else
-	    return false;
-	}
-      else if (!strcmp(argv[i], "--version")
-	       || !strcmp(argv[i], "-v"))
-	opts.display_version = true;
-      else if (!strcmp(argv[i], "--abixml-version")
-	       || !strcmp(argv[i], "-v"))
-	opts.display_abixml_version = true;
-      else if (!strcmp(argv[i], "--fail-no-debug-info"))
-	opts.fail_no_debug_info = true;
-      else if (!strcmp(argv[i], "--debug-info-dir")
-	       || !strcmp(argv[i], "-d"))
-	{
-	  if (argc <= i + 1
-	      || argv[i + 1][0] == '-')
-	    return false;
-	  // elfutils wants the root path to the debug info to be
-	  // absolute.
-	  opts.di_root_paths.push_back
-	    (abigail::tools_utils::make_path_absolute(string(argv[i + 1])));
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--headers-dir")
-	       || !strcmp(argv[i], "--hd"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.headers_dirs.push_back(argv[j]);
-	  // The user is indirectly defining private types so she
-	  // really wants those types to be removed from the ABIXML.
-	  // So let's drop them from the IR.
-	  opts.drop_private_types = true;
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--added-binaries-dir")
-	       || !strcmp(argv[i], "--abd"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.added_bins_dirs.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--header-file")
-	       || !strcmp(argv[i], "--hf"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.header_files.push_back(argv[j]);
-	  // The user is indirectly defining private types so she
-	  // really wants those types to be removed from the ABIXML.
-	  // So let's drop them from the IR.
-	  opts.drop_private_types = true;
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--out-file")
-	       || !strcmp(argv[i], "-o"))
-	{
-	  if (argc <= i + 1
-	      || argv[i + 1][0] == '-'
-	      || !opts.out_file_path.empty())
-	    return false;
-
-	  opts.out_file_path = argv[i + 1];
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--suppressions")
-	       || !strcmp(argv[i], "--suppr"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.suppression_paths.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--kmi-whitelist")
-	       || !strcmp(argv[i], "--kmi-stablelist")
-	       || !strcmp(argv[i], "-w"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.kabi_whitelist_paths.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--linux-tree")
-	       || !strcmp(argv[i], "--lt"))
-	opts.corpus_group_for_linux = true;
-      else if (!strcmp(argv[i], "--vmlinux"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.vmlinux = argv[j];
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--noout"))
-	opts.noout = true;
-      else if (!strcmp(argv[i], "--follow-dependencies"))
-	opts.follow_dependencies = true;
-      else if (!strcmp(argv[i], "--list-dependencies"))
-	opts.list_dependencies = true;
-      else if (!strncmp(argv[i], "--add-binaries=",
-			strlen("--add-binaries=")))
-	tools_utils::get_comma_separated_args_of_option(argv[i],
-							"--add-binaries=",
-							opts.added_bins);
-      else if (!strcmp(argv[i], "--add-binaries"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-
-	  string s = argv[j];
-	  if (s.find(','))
-	    tools_utils::split_string(s, ",", opts.added_bins);
-	  else
-	    opts.added_bins.push_back(s);
-	  ++i;
-	}
-#ifdef WITH_CTF
-        else if (!strcmp(argv[i], "--ctf"))
-          opts.use_ctf = true;
-#endif
-#ifdef WITH_BTF
-        else if (!strcmp(argv[i], "--btf"))
-          opts.use_btf = true;
-#endif
-      else if (!strcmp(argv[i], "--no-architecture"))
-	opts.write_architecture = false;
-      else if (!strcmp(argv[i], "--no-corpus-path"))
-	opts.write_corpus_path = false;
-      else if (!strcmp(argv[i], "--no-show-locs"))
-	opts.show_locs = false;
-      else if (!strcmp(argv[i], "--short-locs"))
-	opts.short_locs = true;
-      else if (!strcmp(argv[i], "--no-comp-dir-path"))
-	opts.write_comp_dir = false;
-      else if (!strcmp(argv[i], "--no-elf-needed"))
-	opts.write_elf_needed = false;
-      else if (!strcmp(argv[i], "--no-write-default-sizes"))
-	opts.default_sizes = false;
-      else if (!strcmp(argv[i], "--no-parameter-names"))
-	opts.write_parameter_names = false;
-      else if (!strcmp(argv[i], "--type-id-style"))
-        {
-          ++i;
-          if (i >= argc)
-            return false;
-          if (!strcmp(argv[i], "sequence"))
-            opts.type_id_style = SEQUENCE_TYPE_ID_STYLE;
-          else if (!strcmp(argv[i], "hash"))
-            opts.type_id_style = HASH_TYPE_ID_STYLE;
-          else
-            return false;
-        }
-      else if (!strcmp(argv[i], "--check-alternate-debug-info")
-	       || !strcmp(argv[i], "--check-alternate-debug-info-base-name"))
-	{
-	  if (argc <= i + 1
-	      || argv[i + 1][0] == '-'
-	      || !opts.in_file_path.empty())
-	    return false;
-	  if (!strcmp(argv[i], "--check-alternate-debug-info-base-name"))
-	    opts.show_base_name_alt_debug_info_path = true;
-	  opts.check_alt_debug_info_path = true;
-	  opts.in_file_path = argv[i + 1];
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--load-all-types"))
-	opts.load_all_types = true;
-      else if (!strcmp(argv[i], "--no-load-undefined-interfaces"))
-	opts.load_undefined_interfaces = false;
-      else if (!strcmp(argv[i], "--drop-private-types"))
-	opts.drop_private_types = true;
-      else if (!strcmp(argv[i], "--force-early-suppression"))
-	opts.force_early_suppression = true;
-      else if (!strcmp(argv[i], "--drop-undefined-syms"))
-	opts.drop_undefined_syms = true;
-      else if (!strcmp(argv[i], "--exported-interfaces-only"))
-	opts.exported_interfaces_only = true;
-      else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
-	opts.exported_interfaces_only = false;
-      else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
-	opts.linux_kernel_mode = false;
-      else if (!strcmp(argv[i], "--abidiff"))
-	opts.abidiff = true;
-#ifdef WITH_DEBUG_SELF_COMPARISON
-      else if (!strcmp(argv[i], "--debug-abidiff"))
-	{
-	  opts.abidiff = true;
-	  opts.debug_abidiff = true;
-	}
-#endif
-#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
-      else if (!strcmp(argv[i], "--debug-tc")
-	       || !strcmp(argv[i], "debug-type-canonicalization"))
-	opts.debug_type_canonicalization = true;
-      else if (!strcmp(argv[i], "--debug-dc")
-	       || !strcmp(argv[i], "debug-die-canonicalization"))
-	opts.debug_die_canonicalization = true;
-#endif
-      else if (!strcmp (argv[i], "--no-assume-odr-for-cplusplus"))
-	opts.assume_odr_for_cplusplus = false;
-      else if (!strcmp (argv[i], "--no-leverage-dwarf-factorization"))
-	opts.leverage_dwarf_factorization = false;
-      else if (!strcmp(argv[i], "--annotate"))
-	opts.annotate = true;
-      else if (!strcmp(argv[i], "--stats"))
-	opts.show_stats = true;
-      else if (!strcmp(argv[i], "--verbose"))
-	opts.do_log = true;
-      else if (!strcmp(argv[i], "--help")
-	       || !strcmp(argv[i], "--h"))
-	return false;
-      else
-	{
-	  if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
-	    opts.wrong_option = argv[i];
-	  return false;
-	}
-    }
-
-  return true;
-}
-
 /// Initialize the context use for driving ABI comparison.
 ///
 /// @param ctxt the context to initialize.
@@ -1159,6 +839,445 @@  load_kernel_corpus_group_and_write_abixml(char* argv[],
   return exit_code;
 }
 
+enum option_key
+{
+  OPT_ABIDIFF = 256,
+  OPT_ABIXML_VERSION,
+  OPT_ADD_BINARIES,
+  OPT_ADDED_BINS_DIR,
+  OPT_ALLOW_NON_EXPORTED_INTERFACES,
+  OPT_ANNOTATE,
+  OPT_BTF,
+  OPT_CHECK_ALT_DEBUG_INFO,
+  OPT_CHECK_ALT_DEBUG_INFO_BASE_NAME,
+  OPT_CTF,
+  OPT_DEBUG_ABIDIFF,
+  OPT_DEBUG_DC,
+  OPT_DEBUG_TC,
+  OPT_DEBUG_INFO_DIR,
+  OPT_DROP_PRIVATE_TYPES,
+  OPT_DROP_UNDEFINED_SYMS,
+  OPT_EXPORTED_INTERFACES_ONLY,
+  OPT_FAIL_NO_DEBUG_INFO,
+  OPT_FDEPS,
+  OPT_FORCE_EARLY_SUPPRESSION,
+  OPT_HD,
+  OPT_HF,
+  OPT_KMI_WHITELIST,
+  OPT_LDEPS,
+  OPT_LINUX_TREE,
+  OPT_LOAD_ALL_TYPES,
+  OPT_NO_ARCH,
+  OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS,
+  OPT_NO_COMP_DIR_PATH,
+  OPT_NO_CORPUS_PATH,
+  OPT_NO_ELF_NEEDED,
+  OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+  OPT_NO_LINUX_KERNEL_MODE,
+  OPT_NO_LOAD_UNDEFINED_INTERFACES,
+  OPT_NO_PARAMETER_NAMES,
+  OPT_NO_SHOW_LOCS,
+  OPT_NO_WRITE_DEFAULT_SIZES,
+  OPT_NOOUT,
+  OPT_OUT_FILE,
+  OPT_SHORT_LOCS,
+  OPT_SHOW_STATS,
+  OPT_SUPPR,
+  OPT_TYPE_ID_STYLE,
+  OPT_VERBOSE,
+  OPT_VMLINUX,
+};
+
+static const struct argp_option argp_options[] =
+{
+  { "abidiff", OPT_ABIDIFF, 0, 0,
+    "compare the loaded ABI against itself", 0 },
+  { "abixml-version", OPT_ABIXML_VERSION, 0, 0,
+    "display the version of the ABIXML ABI format", 0 },
+  { "add-binaries", OPT_ADD_BINARIES, "BIN1,BIN2,..", 0,
+    "build a corpus group with the added binaries", 0 },
+  { "added-binaries-dir", OPT_ADDED_BINS_DIR, "PATH", 0,
+    "where to look for dependencies or added binaries", 0 },
+  { "abd", OPT_ADDED_BINS_DIR, "PATH", OPTION_ALIAS, 0, 0 },
+  { "allow-non-exported-interfaces", OPT_ALLOW_NON_EXPORTED_INTERFACES, 0, 0,
+    "analyze interfaces that might not be exported", 0 },
+  { "annotate", OPT_ANNOTATE, 0, 0,
+    "annotate the ABI artifacts emitted in the output", 0 },
+#ifdef WITH_BTF
+  { "btf", OPT_BTF, 0, 0,
+    "use BTF instead of DWARF in ELF files", 0 },
+#endif
+  { "check-alternate-debug-info", OPT_CHECK_ALT_DEBUG_INFO, "ELF-PATH", 0,
+    "check alternate debug info of <elf-path>", 0 },
+  { "check-alternate-debug-info-base-name",
+    OPT_CHECK_ALT_DEBUG_INFO_BASE_NAME, "ELF-PATH", 0,
+    "check alternate debug info of <elf-path>, and show its base name", 0 },
+#ifdef WITH_CTF
+  { "ctf", OPT_CTF, 0, 0,
+    "use CTF instead of DWARF in ELF files", 0 },
+#endif
+#ifdef WITH_DEBUG_SELF_COMPARISON
+  { "debug-abidiff", OPT_DEBUG_ABIDIFF, 0, 0,
+    "debug the process of comparing the loaded ABI against itself", 0 },
+#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+  { "debug-dc", OPT_DEBUG_DC, 0, 0,
+    "debug the DIE canonicalization process", 0 },
+  { "debug-tc", OPT_DEBUG_TC, 0, 0,
+    "debug the type canonicalization process", 0 },
+#endif
+  { "debug-info-dir", OPT_DEBUG_INFO_DIR, "PATH", 0,
+    "look for debug info under 'dir-path'", 0 },
+  { "d", OPT_DEBUG_INFO_DIR, "PATH", OPTION_ALIAS, 0, 0 },
+  { "drop-private-types", OPT_DROP_PRIVATE_TYPES, 0, 0,
+    "drop private types from internal representation", 0 },
+  { "drop-undefined-syms", OPT_DROP_UNDEFINED_SYMS, 0, 0,
+    "drop undefined symbols from representation", 0 },
+  { "exported-interfaces-only", OPT_EXPORTED_INTERFACES_ONLY, 0, 0,
+    "analyze exported interfaces only", 0 },
+  { "fail-no-debug-info", OPT_FAIL_NO_DEBUG_INFO, 0, 0,
+    "bail out if no debug info was found", 0 },
+  { "follow-dependencies", OPT_FDEPS, 0, 0,
+    "build a corpus group with the dependencies", 0 },
+  { "force-early-suppression", OPT_FORCE_EARLY_SUPPRESSION, 0, 0,
+    "drop IR nodes that match suppression specifications", 0 },
+  { "headers-dir", OPT_HD, "PATH", 0,
+    "the path to headers of the elf file", 0 },
+  { "hd", OPT_HD, "PATH", OPTION_ALIAS, 0, 0 },
+  { "header-file", OPT_HF, "PATH", 0,
+    "the path to one header of the elf file", 0 },
+  { "hf", OPT_HF, "PATH", OPTION_ALIAS, 0, 0 },
+  { "kmi-whitelist", OPT_KMI_WHITELIST, "PATH", 0,
+    "path to a linux kernel abi whitelist", 0 },
+  { "kmi-stablelist", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+  { "w", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+  { "list-dependencies", OPT_LDEPS, 0, 0,
+    "list the dependencies of a given binary", 0 },
+  { "linux-tree", OPT_LINUX_TREE, 0, 0,
+    "emit the ABI for the union of a vmlinux and its modules", 0 },
+  { "lt", OPT_LINUX_TREE, 0, OPTION_ALIAS, 0, 0 },
+  { "load-all-types", OPT_LOAD_ALL_TYPES, 0, 0,
+    "read all types including those not reachable from exported declarations",
+    0 },
+  { "no-architecture", OPT_NO_ARCH, 0, 0,
+    "do not emit architecture info in the output", 0 },
+  { "no-assume-odr-for-cplusplus", OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS, 0, 0,
+    "do not assume the ODR to speed-up the analysis of the binary", 0 },
+  { "no-comp-dir-path", OPT_NO_COMP_DIR_PATH, 0, 0,
+    "do not show compilation path information", 0 },
+  { "no-corpus-path", OPT_NO_CORPUS_PATH, 0, 0,
+    "do not take the path to the corpora into account", 0 },
+  { "no-elf-needed", OPT_NO_ELF_NEEDED, 0, 0,
+    "do not show the DT_NEEDED information", 0 },
+  { "no-leverage-dwarf-factorization", OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+    0, 0,
+    "do not use DWZ optimisations to speed-up the analysis of the binary", 0 },
+  { "no-linux-kernel-mode", OPT_NO_LINUX_KERNEL_MODE, 0, 0,
+    "don't consider the input binary as a Linux Kernel binary", 0 },
+  { "no-load-undefined-interfaces", OPT_NO_LOAD_UNDEFINED_INTERFACES, 0, 0,
+    "do not consider undefined interfaces from the binary", 0 },
+  { "no-parameter-names", OPT_NO_PARAMETER_NAMES, 0, 0,
+    "do not show names of function parameters", 0 },
+  { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+    "do not show location information", 0 },
+  { "no-write-default-sizes", OPT_NO_WRITE_DEFAULT_SIZES, 0, 0,
+    "do not emit pointer size when it equals the default address size of "
+    "the translation unit", 0 },
+  { "noout", OPT_NOOUT, 0, 0,
+    "do not emit anything after reading the binary", 0 },
+  { "out-file", OPT_OUT_FILE, "PATH", 0,
+    "write the output to 'file-path'", 0 },
+  { "o", OPT_OUT_FILE, "PATH", OPTION_ALIAS, 0, 0 },
+  { "short-locs", OPT_SHORT_LOCS, 0, 0,
+    "only print filenames rather than paths", 0 },
+  { "stats", OPT_SHOW_STATS, 0, 0,
+    "show statistics about various internal stuff", 0 },
+  { "suppressions", OPT_SUPPR, "PATH", 0,
+    "specify a suppression file", 0 },
+  { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+  { "type-id-style", OPT_TYPE_ID_STYLE, "STYLE", 0,
+    "type id style (sequence(default): \"type-id-\" + number; hash: "
+    "hex-digits)", 0 },
+  { "verbose", OPT_VERBOSE, 0, 0,
+    "show verbose messages about internal stuff", 0 },
+  { "vmlinux", OPT_VMLINUX, "PATH", 0,
+    "the path to the vmlinux binary to consider to emit the ABI of the union "
+    "of vmlinux and its modules", 0 },
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+  options& opts = *static_cast<options*>(state->input);
+  const string argument = arg ? string(arg) : string();
+
+  switch (key)
+    {
+    case OPT_ABIDIFF:
+      opts.abidiff = true;
+      break;
+
+    case OPT_ABIXML_VERSION:
+      opts.display_abixml_version = true;
+      break;
+
+    case OPT_ADD_BINARIES:
+      if (argument.find(',') != string::npos)
+	tools_utils::split_string(argument, ",", opts.added_bins);
+      else
+	opts.added_bins.push_back(argument);
+      break;
+
+    case OPT_ADDED_BINS_DIR:
+      opts.added_bins_dirs.push_back(argument);
+      break;
+
+    case OPT_ALLOW_NON_EXPORTED_INTERFACES:
+      opts.exported_interfaces_only = false;
+      break;
+
+    case OPT_ANNOTATE:
+      opts.annotate = true;
+      break;
+
+#ifdef WITH_BTF
+    case OPT_BTF:
+      opts.use_btf = true;
+      break;
+#endif
+
+    case OPT_CHECK_ALT_DEBUG_INFO:
+      if (opts.in_file_path.empty())
+	opts.in_file_path = argument;
+      else
+	argp_usage(state);
+      opts.check_alt_debug_info_path = true;
+      break;
+
+    case OPT_CHECK_ALT_DEBUG_INFO_BASE_NAME:
+      if (opts.in_file_path.empty())
+	opts.in_file_path = argument;
+      else
+	argp_usage(state);
+      opts.check_alt_debug_info_path = true;
+      opts.show_base_name_alt_debug_info_path = true;
+      break;
+
+#ifdef WITH_CTF
+    case OPT_CTF:
+      opts.use_ctf = true;
+      break;
+#endif
+
+#ifdef WITH_DEBUG_SELF_COMPARISON
+    case OPT_DEBUG_ABIDIFF:
+      opts.abidiff = true;
+      opts.debug_abidiff = true;
+      break;
+#endif
+
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+    case OPT_DEBUG_DC:
+      opts.debug_die_canonicalization = true;
+      break;
+
+    case OPT_DEBUG_TC:
+      opts.debug_type_canonicalization = true;
+      break;
+#endif
+
+    case OPT_DEBUG_INFO_DIR:
+      opts.di_root_paths.push_back
+	(abigail::tools_utils::make_path_absolute(argument));
+      break;
+
+    case OPT_DROP_PRIVATE_TYPES:
+      opts.drop_private_types = true;
+      break;
+
+    case OPT_DROP_UNDEFINED_SYMS:
+      opts.drop_undefined_syms = true;
+      break;
+
+    case OPT_EXPORTED_INTERFACES_ONLY:
+      opts.exported_interfaces_only = true;
+      break;
+
+    case OPT_FAIL_NO_DEBUG_INFO:
+      opts.fail_no_debug_info = true;
+      break;
+
+    case OPT_FDEPS:
+      opts.follow_dependencies = true;
+      break;
+
+    case OPT_FORCE_EARLY_SUPPRESSION:
+      opts.force_early_suppression = true;
+      break;
+
+    case OPT_HD:
+      opts.headers_dirs.push_back(argument);
+      opts.drop_private_types = true;
+      break;
+
+    case OPT_HF:
+      opts.header_files.push_back(argument);
+      opts.drop_private_types = true;
+      break;
+
+    case OPT_KMI_WHITELIST:
+      opts.kabi_whitelist_paths.push_back(argument);
+      break;
+
+    case OPT_LDEPS:
+      opts.list_dependencies = true;
+      break;
+
+    case OPT_LINUX_TREE:
+      opts.corpus_group_for_linux = true;
+      break;
+
+    case OPT_LOAD_ALL_TYPES:
+      opts.load_all_types = true;
+      break;
+
+    case OPT_NO_ARCH:
+      opts.write_architecture = false;
+      break;
+
+    case OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS:
+      opts.assume_odr_for_cplusplus = false;
+      break;
+
+    case OPT_NO_COMP_DIR_PATH:
+      opts.write_comp_dir = false;
+      break;
+
+    case OPT_NO_CORPUS_PATH:
+      opts.write_corpus_path = false;
+      break;
+
+    case OPT_NO_ELF_NEEDED:
+      opts.write_elf_needed = false;
+      break;
+
+    case OPT_NO_LEVERAGE_DWARF_FACTORIZATION:
+      opts.leverage_dwarf_factorization = false;
+      break;
+
+    case OPT_NO_LINUX_KERNEL_MODE:
+      opts.linux_kernel_mode = false;
+      break;
+
+    case OPT_NO_LOAD_UNDEFINED_INTERFACES:
+      opts.load_undefined_interfaces = false;
+      break;
+
+    case OPT_NO_PARAMETER_NAMES:
+      opts.write_parameter_names = false;
+      break;
+
+    case OPT_NO_SHOW_LOCS:
+      opts.show_locs = false;
+      break;
+
+    case OPT_NO_WRITE_DEFAULT_SIZES:
+      opts.default_sizes = false;
+      break;
+
+    case OPT_NOOUT:
+      opts.noout = true;
+      break;
+
+    case OPT_OUT_FILE:
+      if (!opts.out_file_path.empty())
+	argp_usage(state);
+      opts.out_file_path = argument;
+      break;
+
+    case OPT_SHORT_LOCS:
+      opts.short_locs = true;
+      break;
+
+    case OPT_SHOW_STATS:
+      opts.show_stats = true;
+      break;
+
+    case OPT_SUPPR:
+      opts.suppression_paths.push_back(argument);
+      break;
+
+    case OPT_TYPE_ID_STYLE:
+      if (argument == "sequence")
+	opts.type_id_style = SEQUENCE_TYPE_ID_STYLE;
+      else if (argument == "hash")
+	opts.type_id_style = HASH_TYPE_ID_STYLE;
+      else
+	argp_usage(state);
+      break;
+
+    case OPT_VERBOSE:
+      opts.do_log = true;
+      break;
+
+    case OPT_VMLINUX:
+      opts.vmlinux = argument;
+      break;
+
+    case ARGP_KEY_ARG:
+      if (opts.in_file_path.empty())
+	opts.in_file_path = argument;
+      else
+	argp_usage(state);
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static const char* argp_args_doc = "[<path-to-elf-file>]";
+static const char* argp_doc =
+  "Read an ELF file, load its debug info and emit it in the native "
+  "libabigail XML format.";
+
+static const struct argp abidw_argp =
+{
+  argp_options,
+  parse_opt,
+  argp_args_doc,
+  argp_doc,
+  0,
+  0,
+  0
+};
+
+static void
+print_abidw_version(FILE *stream, struct argp_state* /*state*/)
+{
+  fprintf(stream, "abidw %s\n",
+	  abigail::tools_utils::get_library_version_string().c_str());
+}
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+  argp_program_version_hook = print_abidw_version;
+  argp_program_bug_address = "<libabigail@sourceware.org>";
+
+  if (argp_parse(&abidw_argp, argc, argv, 0, 0, &opts) != 0)
+    return false;
+  return true;
+}
+
 int
 main(int argc, char* argv[])
 {
@@ -1168,24 +1287,13 @@  main(int argc, char* argv[])
 
   if (!parse_command_line(argc, argv, opts)
       || (opts.in_file_path.empty()
-	  && !opts.display_version
 	  && !opts.display_abixml_version))
     {
-      if (!opts.wrong_option.empty())
-	emit_prefix(argv[0], cerr)
-	  << "unrecognized option: " << opts.wrong_option << "\n";
-      display_usage(argv[0], cerr);
+      char* prog_name = (char*) "abidw";
+      argp_help(&abidw_argp, stderr, ARGP_HELP_USAGE, prog_name);
       return 1;
     }
 
-  if (opts.display_version)
-    {
-      emit_prefix(argv[0], cout)
-	<< abigail::tools_utils::get_library_version_string()
-	<< "\n";
-      return 0;
-    }
-
     if (opts.display_abixml_version)
       {
 	emit_prefix(argv[0], cout)
diff --git a/tools/abilint.cc b/tools/abilint.cc
index 4799d429..85d10789 100644
--- a/tools/abilint.cc
+++ b/tools/abilint.cc
@@ -14,6 +14,7 @@ 
 /// be empty.
 
 #include "config.h"
+#include <argp.h>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
@@ -74,9 +75,7 @@  using abigail::xml_writer::write_corpus_to_archive;
 
 struct options
 {
-  string			wrong_option;
   string			file_path;
-  bool				display_version;
   bool				read_from_stdin;
   bool				read_tu;
   bool				diff;
@@ -95,8 +94,7 @@  struct options
 #endif
 
   options()
-    : display_version(false),
-      read_from_stdin(false),
+    : read_from_stdin(false),
       read_tu(false),
       diff(false),
       noout(false),
@@ -480,157 +478,6 @@  show_how_type_is_used(abigail::fe_iface &iface, const string& type_id)
 }
 #endif // WITH_SHOW_TYPE_USE_IN_ABILINT
 
-static void
-display_usage(const string& prog_name, ostream& out)
-{
-  emit_prefix(prog_name, out)
-    << "usage: " << prog_name << " [options] [<abi-file1>]\n"
-    << " where options can be:\n"
-    << "  --annotate  annotate the ABI artifacts emitted in the output\n"
-    << "  --verbose  show verbose messages about internal stuff\n"
-#ifdef WITH_CTF
-    << "  --ctf use CTF instead of DWARF in ELF files\n"
-#endif
-    << "  --debug-info-dir <path> the path under which to look for "
-    "debug info for the elf <abi-file>\n"
-    << "  --diff  for xml inputs, perform a text diff between "
-    "the input and the memory model saved back to disk\n"
-    << "  --header-file|--hf <path> the path to one header of the elf file\n"
-    << "  --headers-dir|--hd <path> the path to headers of the elf file\n"
-    << "  --help  display this message\n"
-    << "  --noout  do not display anything on stdout\n"
-#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
-    << "  --show-type-use <type-id>  show how a type is used from the abixml file\n"
-#endif
-    << "  --stdin  read abi-file content from stdin\n"
-    << "  --suppressions|--suppr <path> specify a suppression file\n"
-    << "  --tu  expect a single translation unit file\n"
-    << "  --version|-v  display program version information and exit\n"
-    ;
-}
-
-bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
-  if (argc < 2)
-    {
-      opts.read_from_stdin = true;
-      return true;
-    }
-
-    for (int i = 1; i < argc; ++i)
-      {
-	if (argv[i][0] != '-')
-	  {
-	    if (opts.file_path.empty())
-	      opts.file_path = argv[i];
-	    else
-	      return false;
-	  }
-	else if (!strcmp(argv[i], "--help"))
-	  return false;
-	else if (!strcmp(argv[i], "--version")
-		 || !strcmp(argv[i], "-v"))
-	  {
-	    opts.display_version = true;
-	    return true;
-	  }
-	else if (!strcmp(argv[i], "--debug-info-dir"))
-	  {
-	    if (argc <= i + 1
-		|| argv[i + 1][0] == '-')
-	      return false;
-	    // elfutils wants the root path to the debug info to be
-	    // absolute.
-	    opts.di_root_path =
-	      abigail::tools_utils::make_path_absolute(string(argv[i + 1]));
-	    ++i;
-	  }
-      else if (!strcmp(argv[i], "--headers-dir")
-	       || !strcmp(argv[i], "--hd"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.headers_dir = argv[j];
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--header-file")
-	       || !strcmp(argv[i], "--hf"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.header_files.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--suppressions")
-	       || !strcmp(argv[i], "--suppr"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.suppression_paths.push_back(argv[j]);
-	  ++i;
-	}
-	else if (!strcmp(argv[i], "--stdin"))
-	  opts.read_from_stdin = true;
-	else if (!strcmp(argv[i], "--tu"))
-	  opts.read_tu = true;
-#ifdef WITH_CTF
-        else if (!strcmp(argv[i], "--ctf"))
-          opts.use_ctf = true;
-#endif
-	else if (!strcmp(argv[i], "--diff"))
-	  opts.diff = true;
-	else if (!strcmp(argv[i], "--noout"))
-	  opts.noout = true;
-	else if (!strcmp(argv[i], "--annotate"))
-	  opts.annotate = true;
-	else if (!strcmp(argv[i], "--verbose"))
-	  opts.do_log = true;
-#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
-      else if (!strcmp(argv[i], "--show-type-use"))
-	{
-	  ++i;
-	  if (i >= argc || argv[i][0] == '-')
-	    return false;
-	  opts.type_id_to_show = argv[i];
-	}
-#endif
-	else
-	  {
-	    if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
-	      opts.wrong_option = argv[i];
-	    return false;
-	  }
-      }
-
-#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
-    if (!opts.type_id_to_show.empty()
-	&& opts.file_path.empty())
-      emit_prefix(argv[0], cout)
-	<< "WARNING: --show-type-use <type-id> "
-	"must be accompanied with an abixml file\n";
-
-    if (opts.file_path.empty()
-	&& opts.type_id_to_show.empty())
-      opts.read_from_stdin = true;
-#endif
-
-    if (opts.read_from_stdin && !opts.file_path.empty())
-    {
-      emit_prefix(argv[0], cout)
-        << "WARNING: The \'--stdin\' option is used. The "
-        << opts.file_path << " will be ignored automatically\n";
-    }
-
-    return true;
-}
-
 /// Check that the suppression specification files supplied are
 /// present.  If not, emit an error on stderr.
 ///
@@ -696,6 +543,203 @@  set_reader_options(abigail::fe_iface& reader, const options& opts)
   reader.options().do_log = opts.do_log;
 }
 
+enum option_key
+{
+  OPT_ANNOTATE = 256,
+#ifdef WITH_CTF
+  OPT_CTF,
+#endif
+  OPT_DEBUG_INFO_DIR,
+  OPT_DIFF,
+  OPT_HD,
+  OPT_HF,
+  OPT_NOOUT,
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+  OPT_SHOW_TYPE_USE,
+#endif
+  OPT_STDIN,
+  OPT_SUPPR,
+  OPT_TU,
+  OPT_VERBOSE
+};
+
+static const struct argp_option argp_options[] =
+{
+  { "annotate", OPT_ANNOTATE, 0, 0,
+    "annotate the ABI artifacts emitted in the output", 0 },
+#ifdef WITH_CTF
+  { "ctf", OPT_CTF, 0, 0,
+    "use CTF instead of DWARF in ELF files", 0 },
+#endif
+  { "debug-info-dir", OPT_DEBUG_INFO_DIR, "PATH", 0,
+    "the path under which to look for debug info for the elf <abi-file>", 0 },
+  { "diff", OPT_DIFF, 0, 0,
+    "for xml inputs, perform a text diff between "
+    "the input and the memory model saved back to disk", 0 },
+  { "headers-dir", OPT_HD, "PATH", 0,
+    "the path to headers of the elf file", 0 },
+  { "hd", OPT_HD, "PATH", OPTION_ALIAS, 0, 0 },
+  { "header-file", OPT_HF, "PATH", 0,
+    "the path to one header of the elf file", 0 },
+  { "hf", OPT_HF, "PATH", OPTION_ALIAS, 0, 0 },
+  { "noout", OPT_NOOUT, 0, 0,
+    "do not display anything on stdout", 0 },
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+  { "show-type-use", OPT_SHOW_TYPE_USE, "TYPE-ID", 0,
+    "show how a type is used from the abixml file", 0 },
+#endif
+  { "stdin", OPT_STDIN, 0, 0,
+    "read abi-file content from stdin", 0 },
+  { "suppressions", OPT_SUPPR, "PATH", 0,
+    "specify a suppression file", 0 },
+  { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+  { "tu", OPT_TU, 0, 0,
+    "expect a single translation unit file", 0 },
+  { "verbose", OPT_VERBOSE, 0, 0,
+    "show verbose messages about internal stuff", 0 },
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+  options& opts = *static_cast<options*>(state->input);
+  const string argument = arg ? string(arg) : string();
+
+  switch (key)
+    {
+    case OPT_ANNOTATE:
+      opts.annotate = true;
+      break;
+
+#ifdef WITH_CTF
+    case OPT_CTF:
+      opts.use_ctf = true;
+      break;
+#endif
+
+    case OPT_DEBUG_INFO_DIR:
+      opts.di_root_path =
+	abigail::tools_utils::make_path_absolute(argument);
+      break;
+
+    case OPT_DIFF:
+      opts.diff = true;
+      break;
+
+    case OPT_HD:
+      opts.headers_dir = argument;
+      break;
+
+    case OPT_HF:
+      opts.header_files.push_back(argument);
+      break;
+
+    case OPT_NOOUT:
+      opts.noout = true;
+      break;
+
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+    case OPT_SHOW_TYPE_USE:
+      opts.type_id_to_show = argument;
+      break;
+#endif
+
+    case OPT_STDIN:
+      opts.read_from_stdin = true;
+      break;
+
+    case OPT_SUPPR:
+      opts.suppression_paths.push_back(argument);
+      break;
+
+    case OPT_TU:
+      opts.read_tu = true;
+      break;
+
+    case OPT_VERBOSE:
+      opts.do_log = true;
+      break;
+
+    case ARGP_KEY_ARG:
+      if (opts.file_path.empty())
+	opts.file_path = argument;
+      else
+	argp_usage(state);
+      break;
+
+    case ARGP_KEY_END:
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+      if (!opts.type_id_to_show.empty() && opts.file_path.empty())
+	emit_prefix("abilint", cout)
+	  << "WARNING: --show-type-use <type-id> "
+	  "must be accompanied with an abixml file\n";
+
+      if (opts.file_path.empty() && opts.type_id_to_show.empty())
+	opts.read_from_stdin = true;
+#else
+      if (opts.file_path.empty())
+	opts.read_from_stdin = true;
+#endif
+      if (opts.read_from_stdin && !opts.file_path.empty())
+	emit_prefix("abilint", cout)
+	  << "WARNING: The '--stdin' option is used. The "
+	  << opts.file_path << " will be ignored automatically\n";
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static const char* argp_args_doc = "[<abi-file>]";
+static const char* argp_doc =
+  "Read an ABI instrumentation file in native XML format and check its "
+  "integrity.";
+
+static const struct argp abilint_argp =
+{
+  argp_options,
+  parse_opt,
+  argp_args_doc,
+  argp_doc,
+  0,
+  0,
+  0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+void
+print_abilint_version(FILE *stream, struct argp_state* /*state*/)
+{
+  fprintf(stream, "abilint %s\n",
+	  abigail::tools_utils::get_library_version_string().c_str());
+}
+
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
+bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+  argp_program_bug_address = "<libabigail@sourceware.org>";
+  argp_program_version_hook = print_abilint_version;
+
+  if (argp_parse(&abilint_argp, argc, argv, 0, 0, &opts) != 0)
+    return false;
+  return true;
+}
+
 /// Reads a bi (binary instrumentation) file, saves it back to a
 /// temporary file and run a diff on the two versions.
 int
@@ -706,21 +750,11 @@  main(int argc, char* argv[])
   options opts;
   if (!parse_command_line(argc, argv, opts))
     {
-      if (!opts.wrong_option.empty())
-	emit_prefix(argv[0], cerr)
-	  << "unrecognized option: " << opts.wrong_option << "\n";
-      display_usage(argv[0], cerr);
+      char* prog_name = (char*)"abilint";
+      argp_help(&abilint_argp, stderr, ARGP_HELP_USAGE, prog_name);
       return 1;
     }
 
-  if (opts.display_version)
-    {
-      emit_prefix(argv[0], cout)
-	<< abigail::tools_utils::get_library_version_string()
-	<< "\n";
-      return 0;
-    }
-
   if (!maybe_check_suppression_files(opts))
     return 1;
 
diff --git a/tools/abipkgdiff.cc b/tools/abipkgdiff.cc
index 3bb90dfb..eb28f4b2 100644
--- a/tools/abipkgdiff.cc
+++ b/tools/abipkgdiff.cc
@@ -55,6 +55,7 @@ 
 #include "config.h"
 
 #include <assert.h>
+#include <argp.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 
@@ -178,12 +179,8 @@  class options
   options();
 
 public:
-  string	wrong_option;
-  string	wrong_arg;
+  vector<string> wrong_args;
   string	prog_name;
-  bool		display_usage;
-  bool		display_version;
-  bool		missing_operand;
   bool		nonexistent_file;
   bool		abignore;
   bool		parallel;
@@ -194,6 +191,7 @@  public:
   string	devel_package1;
   string	devel_package2;
   size_t	num_workers;
+  unsigned 	last_seen_set;
   bool		verbose;
   bool		verbose_diff;
   bool		drop_private_types;
@@ -238,12 +236,10 @@  public:
 
   options(const string& program_name)
     : prog_name(program_name),
-      display_usage(),
-      display_version(),
-      missing_operand(),
       nonexistent_file(),
       abignore(true),
       parallel(true),
+      last_seen_set(0),
       verbose(),
       verbose_diff(),
       drop_private_types(),
@@ -971,82 +967,6 @@  package::extracted_packages_parent_dir()
 /// A convenience typedef for shared_ptr of package.
 typedef shared_ptr<package> package_sptr;
 
-/// Show the usage of this program.
-///
-/// @param prog_name the name of the program.
-///
-/// @param out the output stream to emit the usage to .
-static void
-display_usage(const string& prog_name, ostream& out)
-{
-  emit_prefix(prog_name, out)
-    << "usage: " << prog_name << " [options] <package1> <package2>\n"
-    << " where options can be:\n"
-    << " --debug-info-pkg1|--d1 <path>  path of debug-info package of package1\n"
-    << " --debug-info-pkg2|--d2 <path>  path of debug-info package of package2\n"
-    << " --devel-pkg1|--devel1 <path>   path of devel package of pakage1\n"
-    << " --devel-pkg2|--devel2 <path>   path of devel package of pakage1\n"
-    << " --drop-private-types  drop private types from "
-    "internal representation\n"
-    << " --no-default-suppression       don't load any default "
-       "suppression specifications\n"
-    << " --suppressions|--suppr <path>  specify supression specification path\n"
-    << " --linux-kernel-abi-whitelist|-w path to a "
-    "linux kernel abi whitelist\n"
-    << " --wp <path>                    path to a linux kernel abi whitelist package\n"
-    << " --keep-tmp-files               don't erase created temporary files\n"
-    << " --dso-only                     compare shared libraries only\n"
-    << " --private-dso                  compare DSOs that are private "
-    "to the package as well\n"
-    << " --leaf-changes-only|-l  only show leaf changes, "
-    "so no change impact analysis (implies --redundant)\n"
-    << " --impacted-interfaces|-i  display interfaces impacted by leaf changes\n"
-    << " --full-impact|-f  when comparing kernel packages, show the "
-    "full impact analysis report rather than the default leaf changes reports\n"
-    << " --non-reachable-types|-t  consider types non reachable"
-    " from public interfaces\n"
-    << " --exported-interfaces-only  analyze exported interfaces only\n"
-    << " --allow-non-exported-interfaces  analyze interfaces that "
-    "might not be exported\n"
-    << " --no-linkage-name		do not display linkage names of "
-    "added/removed/changed\n"
-    << " --redundant                    display redundant changes\n"
-    << " --harmless                     display the harmless changes\n"
-    << " --no-show-locs                 do not show location information\n"
-    << " --ignore-soname                do not take the SONAMEs into account\n"
-    << " --show-bytes  show size and offsets in bytes\n"
-    << " --show-bits  show size and offsets in bits\n"
-    << " --show-hex  show size and offset in hexadecimal\n"
-    << " --show-dec  show size and offset in decimal\n"
-    << " --no-show-relative-offset-changes  do not show relative"
-    " offset changes\n"
-    << " --no-added-syms                do not display added functions or variables\n"
-    << " --no-unreferenced-symbols do not display changes "
-    "about symbols not referenced by debug info\n"
-    << " --no-added-binaries            do not display added binaries\n"
-    << " --no-abignore                  do not look for *.abignore files\n"
-    << " --no-parallel                  do not execute in parallel\n"
-    << " --fail-no-dbg                  fail if no debug info was found\n"
-    << " --show-identical-binaries      show the names of identical binaries\n"
-    << " --no-leverage-dwarf-factorization  do not use DWZ optimisations to "
-    "speed-up the analysis of the binary\n"
-    << " --no-assume-odr-for-cplusplus  do not assume the ODR to speed-up the"
-    "analysis of the binary\n"
-    << " --verbose                      emit verbose progress messages\n"
-    << " --verbose-diff                 emit verbose diff progress messages\n"
-    << " --self-check                   perform a sanity check by comparing "
-    "binaries inside the input package against their ABIXML representation\n"
-#ifdef WITH_CTF
-    << " --ctf                          use CTF instead of DWARF in ELF files\n"
-#endif
-#ifdef WITH_BTF
-    << " --btf                          use BTF instead of DWARF in ELF files\n"
-#endif
-    << " --help|-h                      display this help message\n"
-    << " --version|-v                   display program version information"
-    " and exit\n";
-}
-
 #ifdef WITH_RPM
 
 /// Extract an RPM package.
@@ -3759,278 +3679,489 @@  compare(const package_set_sptr& first_ps,
   return compare(first_ps, second_ps, diff, opts);
 }
 
-/// Parse the command line of the current program.
-///
-/// @param argc the number of arguments in the @p argv parameter.
-///
-/// @param argv the array of arguemnts passed to the function.  The
-/// first argument is the name of this program.
-///
-/// @param opts the resulting options.
-///
-/// @return true upon successful parsing.
-static bool
-parse_command_line(int argc, char* argv[], options& opts)
-{
-  if (argc < 2)
-    return false;
 
-  for (int i = 1; i < argc; ++i)
-    {
-      if (argv[i][0] != '-')
-        {
-          if (opts.package_set_paths1.empty())
-            {
-              string path = make_path_absolute(argv[i]).get();
-	      opts.package_set_paths1.insert(path);
-              opts.nonexistent_file = !file_exists(path);
-            }
-          else if (opts.package_set_paths2.empty())
-            {
-              string path = make_path_absolute(argv[i]).get();
-	      opts.package_set_paths2.insert(path);
-              opts.nonexistent_file = !file_exists(path);
-            }
-          else
-	    {
-	      opts.wrong_arg = argv[i];
-	      return false;
-	    }
+enum option_key
+{
+  OPT_ALLOW_NON_EXPORTED_INTERFACES = 256,
+  OPT_BTF,
+  OPT_CTF,
+  OPT_D1,
+  OPT_D2,
+  OPT_DEVEL1,
+  OPT_DEVEL2,
+  OPT_DROP_PRIVATE_TYPES,
+  OPT_DSO_ONLY,
+  OPT_EXPORTED_INTERFACES_ONLY,
+  OPT_FAIL_NO_DBG,
+  OPT_FULL_IMPACT,
+  OPT_HARMLESS,
+  OPT_IGNORE_SONAME,
+  OPT_IMPACTED_INTERFACES,
+  OPT_KEEP_TMP_FILES,
+  OPT_KMI_WHITELIST,
+  OPT_KMI_WHITELIST_PKG,
+  OPT_LEAF_CHANGES_ONLY,
+  OPT_NO_ABIGNORE,
+  OPT_NO_ADDED_BINARIES,
+  OPT_NO_ADDED_SYMS,
+  OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS,
+  OPT_NO_DEFAULT_SUPPRESSION,
+  OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+  OPT_NO_LINKAGE_NAME,
+  OPT_NO_PARALLEL,
+  OPT_NO_SHOW_LOCS,
+  OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES,
+  OPT_NO_UNREFERENCED_SYMBOLS,
+  OPT_NON_REACHABLE_TYPES,
+  OPT_PRIVATE_DSO,
+  OPT_REDUNDANT,
+  OPT_SELF_CHECK,
+  OPT_SET1,
+  OPT_SET2,
+  OPT_SHOW_BITS,
+  OPT_SHOW_BYTES,
+  OPT_SHOW_DEC,
+  OPT_SHOW_HEX,
+  OPT_SHOW_IDENTICAL_BINARIES,
+  OPT_SUPPR,
+  OPT_VERBOSE,
+  OPT_VERBOSE_DIFF
+};
 
-          if (opts.nonexistent_file)
-            {
-              opts.wrong_arg = argv[i];
-              return true;
-            }
-        }
-      else if (!strcmp(argv[i], "--set1")
-	       || !strcmp(argv[i], "--set2"))
-	{
-	  bool is_set1 = !strcmp(argv[i], "--set1");
-	  set<string>& set =
-	    is_set1 ? opts.package_set_paths1 : opts.package_set_paths2;
-	  for (int j = i + 1; j < argc; ++j)
-	    {
-	      if (argv[j][0] == '-')
-		{
-		  i = j - 1;
-		  break;
-		}
-	      const char* p = argv[j];
-	      string pkg_path = make_path_absolute(p).get();
-	      if (!opts.nonexistent_file)
-		opts.nonexistent_file = !file_exists(pkg_path);
-	      if (opts.nonexistent_file && opts.wrong_arg.empty())
-		opts.wrong_arg = pkg_path;
-	      set.insert(pkg_path);
-	      i = j;
-	    }
-	  if (!opts.wrong_arg.empty())
-	    return false;
-	}
-      else if (!strcmp(argv[i], "--debug-info-pkg1")
-	       || !strcmp(argv[i], "--d1"))
-        {
-          int j = i + 1;
-          if (j >= argc)
-            {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-            }
-          opts.debug_packages1.push_back
-	    (abigail::tools_utils::make_path_absolute(argv[j]).get());
-          ++i;
-        }
-      else if (!strcmp(argv[i], "--debug-info-pkg2")
-	       || !strcmp(argv[i], "--d2"))
-        {
-          int j = i + 1;
-          if (j >= argc)
-            {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-            }
-          opts.debug_packages2.push_back
-	    (abigail::tools_utils::make_path_absolute(argv[j]).get());
-          ++i;
-        }
-      else if (!strcmp(argv[i], "--devel-pkg1")
-	       || !strcmp(argv[i], "--devel1"))
-        {
-          int j = i + 1;
-          if (j >= argc)
-            {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-            }
-          opts.devel_package1 =
-	    abigail::tools_utils::make_path_absolute(argv[j]).get();
-          ++i;
-        }
-      else if (!strcmp(argv[i], "--devel-pkg2")
-	       || !strcmp(argv[i], "--devel2"))
-        {
-          int j = i + 1;
-          if (j >= argc)
-            {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-            }
-          opts.devel_package2 =
-	    abigail::tools_utils::make_path_absolute(argv[j]).get();
-          ++i;
-        }
-      else if (!strcmp(argv[i], "--drop-private-types"))
-	opts.drop_private_types = true;
-      else if (!strcmp(argv[i], "--no-default-suppression"))
-	opts.no_default_suppression = true;
-      else if (!strcmp(argv[i], "--keep-tmp-files"))
-	opts.keep_tmp_files = true;
-      else if (!strcmp(argv[i], "--dso-only"))
-	opts.compare_dso_only = true;
-      else if (!strcmp(argv[i], "--private-dso"))
-	opts.compare_private_dsos = true;
-      else if (!strcmp(argv[i], "--leaf-changes-only")
-	       ||!strcmp(argv[i], "-l"))
-	opts.leaf_changes_only = true;
-      else if (!strcmp(argv[i], "--impacted-interfaces")
-	       ||!strcmp(argv[i], "-i"))
-	opts.show_impacted_interfaces = true;
-      else if (!strcmp(argv[i], "--non-reachable-types")
-	       ||!strcmp(argv[i], "-t"))
-	opts.show_all_types = true;
-      else if (!strcmp(argv[i], "--full-impact")
-	       ||!strcmp(argv[i], "-f"))
-	opts.show_full_impact_report = true;
-      else if (!strcmp(argv[i], "--exported-interfaces-only"))
-	opts.exported_interfaces_only = true;
-      else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
-	opts.exported_interfaces_only = false;
-      else if (!strcmp(argv[i], "--no-linkage-name"))
-	opts.show_linkage_names = false;
-      else if (!strcmp(argv[i], "--redundant"))
-	opts.show_redundant_changes = true;
-      else if (!strcmp(argv[i], "--harmless"))
-	opts.show_harmless_changes = true;
-      else if (!strcmp(argv[i], "--no-show-locs"))
-	opts.show_locs = false;
-      else if (!strcmp(argv[i], "--show-bytes"))
-	opts.show_offsets_sizes_in_bits = false;
-      else if (!strcmp(argv[i], "--show-bits"))
-	opts.show_offsets_sizes_in_bits = true;
-      else if (!strcmp(argv[i], "--show-hex"))
-	opts.show_hexadecimal_values = true;
-      else if (!strcmp(argv[i], "--show-dec"))
-	opts.show_hexadecimal_values = false;
-      else if (!strcmp(argv[i], "--no-show-relative-offset-changes"))
-	opts.show_relative_offset_changes = false;
-      else if (!strcmp(argv[i], "--no-added-syms"))
-	opts.show_added_syms = false;
-      else if (!strcmp(argv[i], "--no-unreferenced-symbols"))
-	opts.show_symbols_not_referenced_by_debug_info = false;
-      else if (!strcmp(argv[i], "--no-added-binaries"))
-	opts.show_added_binaries = false;
-      else if (!strcmp(argv[i], "--fail-no-dbg"))
-	opts.fail_if_no_debug_info = true;
-      else if (!strcmp(argv[i], "--no-leverage-dwarf-factorization"))
-	opts.leverage_dwarf_factorization = false;
-      else if (!strcmp(argv[i], "--no-assume-odr-for-cplusplus"))
-	opts.assume_odr_for_cplusplus = false;
-      else if (!strcmp(argv[i], "--verbose"))
-	opts.verbose = true;
-      else if (!strcmp(argv[i], "--verbose-diff"))
-	{
-	  opts.verbose_diff = true;
-	  opts.verbose = true;
-	}
-      else if (!strcmp(argv[i], "--no-abignore"))
-	opts.abignore = false;
-      else if (!strcmp(argv[i], "--no-parallel"))
-	opts.parallel = false;
-      else if (!strcmp(argv[i], "--show-identical-binaries"))
-	opts.show_identical_binaries = true;
-      else if (!strcmp(argv[i], "--self-check"))
-	opts.self_check = true;
-      else if (!strcmp(argv[i], "--suppressions")
-	       || !strcmp(argv[i], "--suppr"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    return false;
-	  opts.suppression_paths.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--linux-kernel-abi-whitelist")
-	       || !strcmp(argv[i], "-w"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  if (guess_file_type(argv[j]) == abigail::tools_utils::FILE_TYPE_RPM)
-	    // The kernel abi stablelist is actually a stablelist
-	    // *package*.  Take that into account.
-	    opts.kabi_stablelist_packages.push_back
-	      (make_path_absolute(argv[j]).get());
-	  else
-	    // We assume the kernel abi stablelist is a white list
-	    // file.
-	    opts.kabi_stablelist_paths.push_back(argv[j]);
-	  ++i;
-	}
-      else if (!strcmp(argv[i], "--wp"))
-	{
-	  int j = i + 1;
-	  if (j >= argc)
-	    {
-	      opts.missing_operand = true;
-	      opts.wrong_option = argv[i];
-	      return true;
-	    }
-	  opts.kabi_stablelist_packages.push_back
-	    (make_path_absolute(argv[j]).get());
-	  ++i;
-	}
+static const struct argp_option argp_options[] =
+{
+  { "allow-non-exported-interfaces", OPT_ALLOW_NON_EXPORTED_INTERFACES, 0, 0,
+    "analyze interfaces that might not be exported", 0 },
+#ifdef WITH_BTF
+  { "btf", OPT_BTF, 0, 0,
+    "use BTF instead of DWARF in ELF files", 0 },
+#endif
 #ifdef WITH_CTF
-	else if (!strcmp(argv[i], "--ctf"))
-          opts.use_ctf = true;
+  { "ctf", OPT_CTF, 0, 0,
+    "use CTF instead of DWARF in ELF files", 0 },
 #endif
+  { "debug-info-pkg1", OPT_D1, "PATH", 0,
+    "path of debug-info package of package1", 0 },
+  { "d1", OPT_D1, "PATH", OPTION_ALIAS, 0, 0 },
+  { "debug-info-pkg2", OPT_D2, "PATH", 0,
+    "path of debug-info package of package2", 0 },
+  { "d2", OPT_D2, "PATH", OPTION_ALIAS, 0, 0 },
+  { "devel-pkg1", OPT_DEVEL1, "PATH", 0,
+    "path of devel package of package1", 0 },
+  { "devel1", OPT_DEVEL1, "PATH", OPTION_ALIAS, 0, 0 },
+  { "devel-pkg2", OPT_DEVEL2, "PATH", 0,
+    "path of devel package of package2", 0 },
+  { "devel2", OPT_DEVEL2, "PATH", OPTION_ALIAS, 0, 0 },
+  { "drop-private-types", OPT_DROP_PRIVATE_TYPES, 0, 0,
+    "drop private types from internal representation", 0 },
+  { "dso-only", OPT_DSO_ONLY, 0, 0,
+    "compare shared libraries only", 0 },
+  { "exported-interfaces-only", OPT_EXPORTED_INTERFACES_ONLY, 0, 0,
+    "analyze exported interfaces only", 0 },
+  { "fail-no-dbg", OPT_FAIL_NO_DBG, 0, 0,
+    "fail if no debug info was found", 0 },
+  { "full-impact", OPT_FULL_IMPACT, 0, 0,
+    "when comparing kernel packages, show the full impact analysis report "
+    "rather than the default leaf changes reports", 0 },
+  { "f", OPT_FULL_IMPACT, 0, OPTION_ALIAS, 0, 0 },
+  { "harmless", OPT_HARMLESS, 0, 0,
+    "display the harmless changes", 0 },
+  { "ignore-soname", OPT_IGNORE_SONAME, 0, 0,
+    "do not take the SONAMEs into account", 0 },
+  { "impacted-interfaces", OPT_IMPACTED_INTERFACES, 0, 0,
+    "display interfaces impacted by leaf changes", 0 },
+  { "i", OPT_IMPACTED_INTERFACES, 0, OPTION_ALIAS, 0, 0 },
+  { "keep-tmp-files", OPT_KEEP_TMP_FILES, 0, 0,
+    "don't erase created temporary files", 0 },
+  { "linux-kernel-abi-whitelist", OPT_KMI_WHITELIST, "PATH", 0,
+    "path to a linux kernel abi whitelist", 0 },
+  { "w", OPT_KMI_WHITELIST, "PATH", OPTION_ALIAS, 0, 0 },
+  { "wp", OPT_KMI_WHITELIST_PKG, "PATH", 0,
+    "path to a linux kernel abi whitelist package", 0 },
+  { "leaf-changes-only", OPT_LEAF_CHANGES_ONLY, 0, 0,
+    "only show leaf changes, so no change impact analysis "
+    "(implies --redundant)", 0 },
+  { "l", OPT_LEAF_CHANGES_ONLY, 0, OPTION_ALIAS, 0, 0 },
+  { "no-abignore", OPT_NO_ABIGNORE, 0, 0,
+    "do not look for *.abignore files", 0 },
+  { "no-added-binaries", OPT_NO_ADDED_BINARIES, 0, 0,
+    "do not display added binaries", 0 },
+  { "no-added-syms", OPT_NO_ADDED_SYMS, 0, 0,
+    "do not display added functions or variables", 0 },
+  { "no-assume-odr-for-cplusplus", OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS, 0, 0,
+    "do not assume the ODR to speed-up the analysis of the binary", 0 },
+  { "no-default-suppression", OPT_NO_DEFAULT_SUPPRESSION, 0, 0,
+    "don't load any default suppression specifications", 0 },
+  { "no-leverage-dwarf-factorization", OPT_NO_LEVERAGE_DWARF_FACTORIZATION,
+    0, 0,
+    "do not use DWZ optimisations to speed-up the analysis of the binary",
+    0 },
+  { "no-linkage-name", OPT_NO_LINKAGE_NAME, 0, 0,
+    "do not display linkage names of added/removed/changed", 0 },
+  { "no-parallel", OPT_NO_PARALLEL, 0, 0,
+    "do not execute in parallel", 0 },
+  { "no-show-locs", OPT_NO_SHOW_LOCS, 0, 0,
+    "do not show location information", 0 },
+  { "no-show-relative-offset-changes",
+    OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES, 0, 0,
+    "do not show relative offset changes", 0 },
+  { "no-unreferenced-symbols", OPT_NO_UNREFERENCED_SYMBOLS, 0, 0,
+    "do not display changes about symbols not referenced by debug info", 0 },
+  { "non-reachable-types", OPT_NON_REACHABLE_TYPES, 0, 0,
+    "consider types non reachable from public interfaces", 0 },
+  { "t", OPT_NON_REACHABLE_TYPES, 0, OPTION_ALIAS, 0, 0 },
+  { "private-dso", OPT_PRIVATE_DSO, 0, 0,
+    "compare DSOs that are private to the package as well", 0 },
+  { "redundant", OPT_REDUNDANT, 0, 0,
+    "display redundant changes", 0 },
+  { "self-check", OPT_SELF_CHECK, 0, 0,
+    "perform a sanity check by comparing binaries inside the input package "
+    "against their ABIXML representation", 0 },
+  { "set1", OPT_SET1, "PKG1[,PKG2,..] | PKG1[ PKG2 ..]", 0,
+    "the first set of packages to compare", 0 },
+  { "set2", OPT_SET2, "PKG1[,PKG2,..] | PKG1[ PKG2 ..]", 0,
+    "the second set of packages to compare", 0 },
+  { "show-bits", OPT_SHOW_BITS, 0, 0,
+    "show size and offsets in bits", 0 },
+  { "show-bytes", OPT_SHOW_BYTES, 0, 0,
+    "show size and offsets in bytes", 0 },
+  { "show-dec", OPT_SHOW_DEC, 0, 0,
+    "show size and offset in decimal", 0 },
+  { "show-hex", OPT_SHOW_HEX, 0, 0,
+    "show size and offset in hexadecimal", 0 },
+  { "show-identical-binaries", OPT_SHOW_IDENTICAL_BINARIES, 0, 0,
+    "show the names of identical binaries", 0 },
+  { "suppressions", OPT_SUPPR, "PATH", 0,
+    "specify a suppression file", 0 },
+  { "suppr", OPT_SUPPR, "PATH", OPTION_ALIAS, 0, 0 },
+  { "verbose", OPT_VERBOSE, 0, 0,
+    "emit verbose progress messages", 0 },
+  { "verbose-diff", OPT_VERBOSE_DIFF, 0, 0,
+    "emit verbose diff progress messages", 0 },
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+  options& opts = *static_cast<options*>(state->input);
+  const string argument = arg ? string(arg) : string();
+
+  if (key != OPT_SET1
+      && key != OPT_SET2
+      && key != ARGP_KEY_ARG)
+    // Reset the opts.last_seen_set if we see an option that is neiter
+    // --set1 not --set2.
+    opts.last_seen_set = 0;
+
+    
+  switch (key)
+    {
+    case OPT_ALLOW_NON_EXPORTED_INTERFACES:
+      opts.exported_interfaces_only = false;
+      break;
+
 #ifdef WITH_BTF
-	else if (!strcmp(argv[i], "--btf"))
-          opts.use_btf = true;
+    case OPT_BTF:
+      opts.use_btf = true;
+      break;
 #endif
-      else if (!strcmp(argv[i], "--ignore-soname"))
-	{
-	  opts.ignore_soname = true;
-	  opts.show_identical_binaries = true;
-	}
-      else if (!strcmp(argv[i], "--help")
-	       || !strcmp(argv[i], "-h"))
-        {
-          opts.display_usage = true;
-          return true;
-        }
-      else if (!strcmp(argv[i], "--version")
-	       || !strcmp(argv[i], "-v"))
-	{
-	  opts.display_version = true;
-	  return true;
-	}
+
+#ifdef WITH_CTF
+    case OPT_CTF:
+      opts.use_ctf = true;
+      break;
+#endif
+
+    case OPT_D1:
+      opts.debug_packages1.push_back
+	(abigail::tools_utils::make_path_absolute(argument));
+      break;
+
+    case OPT_D2:
+      opts.debug_packages2.push_back
+	(abigail::tools_utils::make_path_absolute(argument));
+      break;
+
+    case OPT_DEVEL1:
+      opts.devel_package1 =
+	abigail::tools_utils::make_path_absolute(argument);
+      break;
+
+    case OPT_DEVEL2:
+      opts.devel_package2 =
+	abigail::tools_utils::make_path_absolute(argument);
+      break;
+
+    case OPT_DROP_PRIVATE_TYPES:
+      opts.drop_private_types = true;
+      break;
+
+    case OPT_DSO_ONLY:
+      opts.compare_dso_only = true;
+      break;
+
+    case OPT_EXPORTED_INTERFACES_ONLY:
+      opts.exported_interfaces_only = true;
+      break;
+
+    case OPT_FAIL_NO_DBG:
+      opts.fail_if_no_debug_info = true;
+      break;
+
+    case OPT_FULL_IMPACT:
+      opts.show_full_impact_report = true;
+      break;
+
+    case OPT_HARMLESS:
+      opts.show_harmless_changes = true;
+      break;
+
+    case OPT_IGNORE_SONAME:
+      opts.ignore_soname = true;
+      opts.show_identical_binaries = true;
+      break;
+
+    case OPT_IMPACTED_INTERFACES:
+      opts.show_impacted_interfaces = true;
+      break;
+
+    case OPT_KEEP_TMP_FILES:
+      opts.keep_tmp_files = true;
+      break;
+
+    case OPT_KMI_WHITELIST:
+      if (guess_file_type(argument) == abigail::tools_utils::FILE_TYPE_RPM)
+	opts.kabi_stablelist_packages.push_back(make_path_absolute(argument));
       else
-	{
-	  if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
-	    opts.wrong_option = argv[i];
-	  return false;
-	}
+	opts.kabi_stablelist_paths.push_back(argument);
+      break;
+
+    case OPT_KMI_WHITELIST_PKG:
+      opts.kabi_stablelist_packages.push_back
+	(make_path_absolute(argument));
+      break;
+
+    case OPT_LEAF_CHANGES_ONLY:
+      opts.leaf_changes_only = true;
+      break;
+
+    case OPT_NO_ABIGNORE:
+      opts.abignore = false;
+      break;
+
+    case OPT_NO_ADDED_BINARIES:
+      opts.show_added_binaries = false;
+      break;
+
+    case OPT_NO_ADDED_SYMS:
+      opts.show_added_syms = false;
+      break;
+
+    case OPT_NO_ASSUME_ODR_FOR_CPLUSPLUS:
+      opts.assume_odr_for_cplusplus = false;
+      break;
+
+    case OPT_NO_DEFAULT_SUPPRESSION:
+      opts.no_default_suppression = true;
+      break;
+
+    case OPT_NO_LEVERAGE_DWARF_FACTORIZATION:
+      opts.leverage_dwarf_factorization = false;
+      break;
+
+    case OPT_NO_LINKAGE_NAME:
+      opts.show_linkage_names = false;
+      break;
+
+    case OPT_NO_PARALLEL:
+      opts.parallel = false;
+      break;
+
+    case OPT_NO_SHOW_LOCS:
+      opts.show_locs = false;
+      break;
+
+    case OPT_NO_SHOW_RELATIVE_OFFSET_CHANGES:
+      opts.show_relative_offset_changes = false;
+      break;
+
+    case OPT_NO_UNREFERENCED_SYMBOLS:
+      opts.show_symbols_not_referenced_by_debug_info = false;
+      break;
+
+    case OPT_NON_REACHABLE_TYPES:
+      opts.show_all_types = true;
+      break;
+
+    case OPT_PRIVATE_DSO:
+      opts.compare_private_dsos = true;
+      break;
+
+    case OPT_REDUNDANT:
+      opts.show_redundant_changes = true;
+      break;
+
+    case OPT_SELF_CHECK:
+      opts.self_check = true;
+      break;
+
+    case OPT_SET1:
+      {
+	opts.last_seen_set = 1;
+	vector<string> paths;
+	if (!tools_utils::split_string(argument, ",", paths)
+	    && !argument.empty())
+	  paths.push_back(argument);
+	for (auto& p : paths)
+	  {
+	    string abs_p = make_path_absolute(p);
+	    if (!file_exists(abs_p))
+	      {
+		opts.nonexistent_file = true;
+		opts.wrong_args.push_back(abs_p);
+	      }
+	    else
+	      opts.package_set_paths1.insert(abs_p);
+	  }
+      }
+      break;
+
+    case OPT_SET2:
+      {
+	opts.last_seen_set = 2;
+	vector<string> paths;
+	if (!tools_utils::split_string(argument, ",", paths)
+	    && !argument.empty())
+	  paths.push_back(argument);
+	for (auto& p : paths)
+	  {
+	    string abs_p = make_path_absolute(p);
+	    if (!file_exists(abs_p))
+	      {
+		opts.nonexistent_file = true;
+		opts.wrong_args.push_back(abs_p);
+	      }
+	    else
+	      opts.package_set_paths2.insert(abs_p);
+	  }
+      }
+      break;
+
+    case OPT_SHOW_BITS:
+      opts.show_offsets_sizes_in_bits = true;
+      break;
+
+    case OPT_SHOW_BYTES:
+      opts.show_offsets_sizes_in_bits = false;
+      break;
+
+    case OPT_SHOW_DEC:
+      opts.show_hexadecimal_values = false;
+      break;
+
+    case OPT_SHOW_HEX:
+      opts.show_hexadecimal_values = true;
+      break;
+
+    case OPT_SHOW_IDENTICAL_BINARIES:
+      opts.show_identical_binaries = true;
+      break;
+
+    case OPT_SUPPR:
+      opts.suppression_paths.push_back(argument);
+      break;
+
+    case OPT_VERBOSE:
+      opts.verbose = true;
+      break;
+
+    case OPT_VERBOSE_DIFF:
+      opts.verbose_diff = true;
+      opts.verbose = true;
+      break;
+
+    case ARGP_KEY_ARG:
+      {
+	string path = make_path_absolute(argument);
+	if (!file_exists(path))
+	  {
+	    // This is later handled in the main function so that the
+	    // program can return an appropriate return code from
+	    // there, so we don't need an ARGP_KEY_END case to handle
+	    // it.
+	    opts.nonexistent_file = true;
+	    opts.wrong_args.push_back(path);
+	  }
+	else if (opts.last_seen_set == 1)
+	  opts.package_set_paths1.insert(path);
+	else if (opts.last_seen_set == 2)
+	  opts.package_set_paths2.insert(path);
+	else
+	  {
+	    if (opts.package_set_paths1.empty())
+	      opts.package_set_paths1.insert(path);
+	    else if (opts.package_set_paths2.empty())
+	      opts.package_set_paths2.insert(path);
+	    else
+	      argp_usage(state);
+	  }
+      }
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
     }
 
+  return 0;
+}
+
+static const char* argp_args_doc = "[<package1> <package2>]";
+static const char* argp_doc =
+  "Compare the ABI of binaries in two packages, returning 0 if they are "
+  "identical, or non-zero if they differ.";
+
+static const struct argp abipkgdiff_argp =
+{
+  argp_options,
+  parse_opt,
+  argp_args_doc,
+  argp_doc,
+  0,
+  0,
+  0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
+static void
+print_abipkgdiff_version(FILE *stream, struct argp_state* /*state*/)
+{
+  fprintf(stream, "abipkgdiff %s\n",
+	  abigail::tools_utils::get_library_version_string().c_str());
+}
+
+typedef void (*program_version_hook_type) (FILE *__restrict __stream,
+					   struct argp_state *__restrict __state);
+
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
+static bool
+parse_command_line(int argc, char* argv[], options& opts)
+{
+  argp_program_version_hook = print_abipkgdiff_version;
+  argp_program_bug_address = "<libabigail@sourceware.org>";
+
+  if (argp_parse(&abipkgdiff_argp, argc, argv, 0, 0, &opts) != 0)
+    return false;
   return true;
 }
 
@@ -4043,34 +4174,39 @@  main(int argc, char* argv[])
 
   if (!parse_command_line(argc, argv, opts))
     {
-      if (!opts.wrong_option.empty())
-	emit_prefix("abipkgdiff", cerr)
-	  << "unrecognized option: " << opts.wrong_option
-	  << "\ntry the --help option for more information\n";
-      if (!opts.wrong_arg.empty())
-	emit_prefix("abipkgdiff", cerr)
-	  << "unrecognized argument: " << opts.wrong_arg
-	  << "\ntry the --help option for more information\n";
-      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-	      | abigail::tools_utils::ABIDIFF_ERROR);
-    }
-
-  if (opts.missing_operand)
-    {
-      emit_prefix("abipkgdiff", cerr)
-	<< "missing operand\n"
-        "try the --help option for more information\n";
+      char* prog_name = (char*) "abipkgdiff";
+      argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 	      | abigail::tools_utils::ABIDIFF_ERROR);
     }
 
   if (opts.nonexistent_file)
     {
+      vector<string> input_files;
       string input_file;
-      base_name(opts.wrong_arg, input_file);
-      emit_prefix("abipkgdiff", cerr)
-	<< "The input file " << input_file << " doesn't exist\n"
-	"try the --help option for more information\n";
+      for (auto& f : opts.wrong_args)
+	{
+	  base_name(f, input_file);
+	  input_files.push_back(input_file);
+	}
+      bool plural = input_files.size() > 1;
+
+      if (plural)
+	emit_prefix("abipkgdiff", cerr)
+	  << "Input files";
+      else
+	emit_prefix("abipkgdiff", cerr)
+	  << "The input file";
+      for (auto& f : input_files)
+	cerr << " " << f;
+
+      if (plural)
+	cerr << " don't exist\n";
+      else
+	cerr << " doesn't exist\n";
+
+      cerr << "try the --help option for more information\n";
+
       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 	      | abigail::tools_utils::ABIDIFF_ERROR);
     }
@@ -4083,21 +4219,6 @@  main(int argc, char* argv[])
 	      | abigail::tools_utils::ABIDIFF_ERROR);
     }
 
-  if (opts.display_usage)
-    {
-      display_usage(argv[0], cout);
-      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
-	      | abigail::tools_utils::ABIDIFF_ERROR);
-    }
-
-  if (opts.display_version)
-    {
-      emit_prefix(argv[0], cout)
-	<< abigail::tools_utils::get_library_version_string()
-	<< "\n";
-      return 0;
-    }
-
     if (!opts.no_default_suppression && opts.suppression_paths.empty())
     {
       // Load the default system and user suppressions.
@@ -4129,9 +4250,11 @@  main(int argc, char* argv[])
 	}
       if (opts.package_set_paths1.empty())
 	{
+	  char* prog_name = (char*) "abipkgdiff";
 	  // We need at least one package to work with!
 	  emit_prefix("abipkgdiff", cerr)
 	    << "missing input package\n";
+	  argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
 	  if (bail_out)
 	    return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 		    | abigail::tools_utils::ABIDIFF_ERROR);
@@ -4139,8 +4262,10 @@  main(int argc, char* argv[])
     }
   else if(opts.package_set_paths1.empty() && opts.package_set_paths2.empty())
     {
+      char* prog_name = (char*) "abipkgdiff";
       emit_prefix("abipkgdiff", cerr)
 	<< "please enter two packages to compare" << "\n";
+      argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
       return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 	      | abigail::tools_utils::ABIDIFF_ERROR);
     }
@@ -4217,9 +4342,11 @@  main(int argc, char* argv[])
 	  if (is_kernel_package(first_package_set)
 	      != is_kernel_package(second_package_set))
 	    {
+	      char* prog_name = (char*) "abipkgdiff";
 	      emit_prefix("abipkgdiff", cerr)
 		<< "a Linux kernel package can only be compared to another "
 		"Linux kernel package\n";
+	      argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
 	      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 		      | abigail::tools_utils::ABIDIFF_ERROR);
 	    }
@@ -4228,9 +4355,11 @@  main(int argc, char* argv[])
 	      || (!second_package_set->path().empty()
 		  && second_package_set->debug_info_packages().empty()))
 	    {
+	      char* prog_name = (char*) "abipkgdiff";
 	      emit_prefix("abipkgdiff", cerr)
 		<< "a Linux Kernel package must be accompanied with its "
 		"debug info package\n";
+	      argp_help(&abipkgdiff_argp, stderr, ARGP_HELP_USAGE, prog_name);
 	      return (abigail::tools_utils::ABIDIFF_USAGE_ERROR
 		      | abigail::tools_utils::ABIDIFF_ERROR);
 	    }
diff --git a/tools/abisym.cc b/tools/abisym.cc
index 9b37ca1a..1c4a2513 100644
--- a/tools/abisym.cc
+++ b/tools/abisym.cc
@@ -10,6 +10,7 @@ 
 /// This program takes parameters to open an elf file, lookup a symbol
 /// in its symbol tables and report what it sees.
 
+#include <argp.h>
 #include <libgen.h>
 #include <elf.h>
 #include <cstring>
@@ -34,76 +35,112 @@  using abigail::elf_symbol_sptr;
 
 struct options
 {
-  bool	show_help;
-  bool	display_version;
-  char* elf_path;
-  char* symbol_name;
+  string elf_path;
+  string symbol_name;
   bool	demangle;
   bool absolute_path;
 
   options()
-    : show_help(false),
-      display_version(false),
-      elf_path(0),
-      symbol_name(0),
-      demangle(false),
+    : demangle(false),
       absolute_path(true)
   {}
 };
 
+
+enum option_key
+{
+  OPT_DEMANGLE = 256,
+  OPT_NO_ABSOLUTE_PATH,
+};
+
+static const struct argp_option argp_options[] =
+{
+  { "demangle", OPT_DEMANGLE, 0, 0,
+    "demangle the symbols from the symbol table", 0 },
+  { "no-absolute-path", OPT_NO_ABSOLUTE_PATH, 0, 0,
+    "do not show absolute paths in messages", 0 },
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+  options& opts = *static_cast<options*>(state->input);
+  const string argument = arg ? string(arg) : string();
+
+  switch (key)
+    {
+    case OPT_DEMANGLE:
+      opts.demangle = true;
+      break;
+
+    case OPT_NO_ABSOLUTE_PATH:
+      opts.absolute_path = false;
+      break;
+
+    case ARGP_KEY_ARG:
+      if (opts.elf_path.empty())
+	opts.elf_path = argument;
+      else if (opts.symbol_name.empty())
+	opts.symbol_name = argument;
+      else
+	argp_usage(state);
+      break;
+
+    case ARGP_KEY_END:
+      if (opts.elf_path.empty() || opts.symbol_name.empty())
+	argp_usage(state);
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static const char* argp_args_doc = "<elf-file> <symbol-name>";
+static const char* argp_doc =
+  "Look up a symbol in the symbol table of an ELF file and report "
+  "information about it.";
+
+static const struct argp abisym_argp =
+{
+  argp_options,
+  parse_opt,
+  argp_args_doc,
+  argp_doc,
+  0,
+  0,
+  0
+};
+
+/// Version printing hook to be passed to ARGP.
+///
+/// @param stream the output stream.
+///
+/// @param state the ARGP state.
 static void
-display_usage(const string& prog_name, ostream &out)
+print_abisym_version(FILE *stream, struct argp_state* /*state*/)
 {
-  out << "usage: " << prog_name << " [options] <elf file> <symbol-name>\n"
-      << "where [options] can be:\n"
-      << "  --help  display this help string\n"
-      << "  --version|-v  display program version information and exit\n"
-      << "  --demangle  demangle the symbols from the symbol table\n"
-      << "  --no-absolute-path  do not show absolute paths in messages\n";
+  fprintf(stream, "abisym %s\n",
+	  abigail::tools_utils::get_library_version_string().c_str());
 }
 
+/// Parse the command line
+///
+/// @param argc number of args
+///
+/// @param argv the array of arguments.
+///
+/// @param opts the options set as result of command line parsing.
 static void
 parse_command_line(int argc, char* argv[], options& opts)
 {
-  if (argc < 2)
-    {
-      opts.show_help = true;
-      return;
-    }
+  argp_program_version_hook = print_abisym_version;
+  argp_program_bug_address = "<libabigail@sourceware.org>";
 
-  for (int i = 1; i < argc; ++i)
-    {
-      if (argv[i][0] != '-')
-	{
-	  if (!opts.elf_path)
-	    opts.elf_path = argv[i];
-	  else if (!opts.symbol_name)
-	    opts.symbol_name = argv[i] ;
-	  else
-	    {
-	      opts.show_help = true;
-	      return;
-	    }
-	}
-      else if (!strcmp(argv[i], "--help")
-	       || !strcmp(argv[i], "-h"))
-	{
-	  opts.show_help = true;
-	  return;
-	}
-      else if (!strcmp(argv[i], "--version")
-	       || !strcmp(argv[i], "-v"))
-	{
-	  opts.display_version = true;
-	  return;
-	}
-      else if (!strcmp(argv[i], "--demangle"))
-	opts.demangle = true;
-      else if (!strcmp(argv[i], "--no-absolute-path"))
-	opts.absolute_path = false;
-      else
-	opts.show_help = true;
-    }
+  argp_parse(&abisym_argp, argc, argv, 0, 0, &opts);
 }
 
 int
@@ -112,23 +149,6 @@  main(int argc, char* argv[])
   options opts;
   parse_command_line(argc, argv, opts);
 
-  if (opts.show_help)
-    {
-      display_usage(argv[0], cout);
-      return 1;
-    }
-
-  if (opts.display_version)
-    {
-      abigail::tools_utils::emit_prefix(argv[0], cout)
-	<< abigail::tools_utils::get_library_version_string()
-	<< "\n";
-      return 0;
-    }
-
-  assert(opts.elf_path != 0
-	 && opts.symbol_name != 0);
-
   string p = opts.elf_path, n = opts.symbol_name;
   environment env;
   vector<elf_symbol_sptr> syms;
@@ -140,7 +160,11 @@  main(int argc, char* argv[])
       if (opts.absolute_path)
 	cout << opts.elf_path << "'\n";
       else
-	cout << basename(opts.elf_path);
+	{
+	  string b;
+	  abigail::tools_utils::base_name(opts.elf_path, b);
+	  cout << b;
+	}
       return 0;
     }
 
diff --git a/tools/binilint.cc b/tools/binilint.cc
index c410256e..2958ad0d 100644
--- a/tools/binilint.cc
+++ b/tools/binilint.cc
@@ -12,9 +12,11 @@ 
 /// abigail::ini::* functions, but one could also use it to make sure
 /// that an ini file can be handled by the abigail::ini::* facilities
 
+#include <argp.h>
 #include <cstring>
 #include <iostream>
 #include "abg-ini.h"
+#include "abg-tools-utils.h"
 
 using std::cout;
 using std::cerr;
@@ -28,83 +30,108 @@  using abigail::ini::write_config;
 
 struct options
 {
-  bool display_usage;
   bool read_from_stdin;
   bool no_out;
-  bool wrong_command_line_usage;
   string path;
 
   options ()
-    : display_usage(false),
-      read_from_stdin(false),
-      no_out(false),
-      wrong_command_line_usage(false)
+    : read_from_stdin(false),
+      no_out(false)
   {}
 };
 
+enum option_key
+{
+  OPT_FROM_STDIN = 256,
+  OPT_NOOUT,
+};
+
+static const struct argp_option argp_options[] =
+{
+  { "from-stdin", OPT_FROM_STDIN, 0, 0,
+    "read the input ini file from stdin", 0 },
+  { "noout", OPT_NOOUT, 0, 0,
+    "do not output anything on stdout", 0 },
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+static error_t
+parse_opt(int key, char* arg, struct argp_state* state)
+{
+  options& opts = *static_cast<options*>(state->input);
+  const string argument = arg ? string(arg) : string();
+
+  switch (key)
+    {
+    case OPT_FROM_STDIN:
+      opts.read_from_stdin = true;
+      break;
+
+    case OPT_NOOUT:
+      opts.no_out = true;
+      break;
+
+    case ARGP_KEY_ARG:
+      if (opts.path.empty())
+	opts.path = argument;
+      else
+	argp_usage(state);
+      break;
+
+    default:
+      return ARGP_ERR_UNKNOWN;
+    }
+
+  return 0;
+}
+
+static const char* argp_args_doc = "[<ini-file>]";
+static const char* argp_doc =
+  "Read an ini file and print it back on standard output.";
+
+static const struct argp binilint_argp =
+{
+  argp_options,
+  parse_opt,
+  argp_args_doc,
+  argp_doc,
+  0,
+  0,
+  0
+};
+
 static void
-display_usage(const string& prog_name, ostream& out)
+print_binilint_version(FILE *stream, struct argp_state* /*state*/)
 {
-  out << "usage: " << prog_name << " [options] <ini file>\n"
-      << "where options can be:\n"
-      << "--help  display this help\n"
-      << "--from-stdin  read the input ini file from stdin\n"
-      << "--noout  do not output anything on stdout\n"
-    ;
+  fprintf(stream, "abinilint %s\n",
+	  abigail::tools_utils::get_library_version_string().c_str());
 }
 
 static bool
 parse_command_line(int argc, char* argv[], options& opts)
 {
-  if (argc == 1)
-    return false;
+  argp_program_version_hook = print_binilint_version;
+  argp_program_bug_address = "<libabigail@sourceware.org>";
 
-  for (int i = 1; i < argc; ++i)
-    {
-      if (argv[i][0] != '-')
-	{
-	  if (opts.path.empty())
-	    opts.path = argv[i];
-	  else
-	    opts.wrong_command_line_usage = true;
-	}
-      else if (!strcmp(argv[i], "--help"))
-	opts.display_usage = true;
-      else if (!strcmp(argv[i], "--from-stdin"))
-	opts.read_from_stdin = true;
-      else if (!strcmp(argv[i], "--noout"))
-	opts.no_out = true;
-      else
-	return false;
-    }
+  if (argp_parse(&binilint_argp, argc, argv, 0, 0, &opts) != 0)
+    return false;
   return true;
 }
 
 int
 main(int argc, char* argv[])
 {
-  // First, parse the command line.
-
   options opts;
-
-  if (argc == 1 || !parse_command_line(argc, argv, opts))
+  if (!parse_command_line(argc, argv, opts))
     {
-      cerr << argv[0] << ": bad command usage\n"
-	   << "Try " << argv[0] << " --help for more information\n";
+      char* prog_name = (char*) "abinilint";
+      argp_help(&binilint_argp, stderr, ARGP_HELP_USAGE, prog_name);
       return 1;
     }
 
-  // Then if we need to display the help, display it and get out.
-
-  if (opts.display_usage)
-    {
-      display_usage(argv[0], cout);
-      return 0;
-    }
-
-  // Otherwise, do the real work we are supposed to do after all.
-  // That real work is driven by the options the user set; these
-  // options are recorded in the opts variable.
+  // Do the real work we are supposed to do after all.  That real work
+  // is driven by the options the user set; these options are recorded
+  // in the opts variable.
 
   config_sptr conf;