Wrong text spacing in help messages with argp.h

  argp, c++, text, text-parsing

About the situation:

Greetings, I am working on a project "osfi" in C (with cmake but targeted to be run only on Linux-based OSes), i required a function that parses arguments passed from command line, so i wrote one using the GNU C argp.h. I ended up creating a library osfi_arg_parser.c that contains that function.

Here’s what my program snippet and library looks like:

osfi.c

#include <stdbool.h>
#include "project_config.h"
#include "arg_parser.h"
// other includes...

int main(int argc, char **argv)
{
  struct arg_parser_arguments *arguments_p = arg_parser_parse(argc, argv);
  // other code...
  return(0);
}

arg_parser.c

#include <stdbool.h>
#include <argp.h>
#include "project_config.h"
#include "arg_parser.h"

#define OPTION_NO_LOGS 1
#define OPTION_NO_CONFIRM 2
#define OPTION_NO_PROGRESS_BAR 3

// version string is passed by cmake through project_config.h.in and project_config.h
// need to add if a define passed by cmake is NULL, then disable program_version program_bug_address
const char *argp_program_version = "osfi " PROJECT_VERSION;
const char *argp_program_bug_address = PROJECT_BUG_ADDRESS;
#define argp_program_version_hook
#define argp_err_exit_status  0
static char doc[] = "Tool that aims automating and speeding up installation "
                    "and configuration process of Linux-based OSes.";
static char args_doc[] = "[OPTION]...";
static struct argp_option option[] =
{
  { 0, 0, 0, 0, "Main options:", 0 },
  {
    "interface",       'I',                    "INTERFACE",
    0,
    "Start with a specified interface",
    0
  },
  {
    "action",          'A',                    "ACTION",
    0,
    "Perform a specified action",
    0
  },
  {
    "list",            'L',                    "OPTION_NAME",
    0,
    "List avaliable values of a specified option "
    "<I|interface|A|action>",
    0
  },

  { 0, 0, 0, 0, "Specify a directory:", 0 },
  {
    "input-dir",       'i',                    "INPUT_DIR",
    0,
    "Path to directory where to search for files"
    "  General path: <path-to-directory>"
    "  Specific path: <i|install|p|progress|l|log>:<path-to-directory> [...]",
    0
  },
  {
    "output-dir",      'o',                    "[FILE_TYPE:]OUTPUT_DIR",
    0,
    "Path to directory where to output files of type FILE_TYPE: FILE_TYPE can be "none" (default if omitted), "i", "p", "l", "install", "progress", or "log"",
    0
  },

  { 0, 0, 0, 0, "Specify a file:", 0 },
  {
    "use-file",        'u',                    "USE_FILE",
    0,
    "Use specified files",
    0
  },
  {
    "output-file",     'p',                    "PRODUCE_FILE",
    0,
    "",
    0
  },

  { 0, 0, 0, 0, "Disable features:", 0 },
  {
    "no-logs",         OPTION_NO_LOGS,         0,
    0,
    "Don't produce any logs",
    0
  },
  {
    "no-confirm",      OPTION_NO_CONFIRM,      "WARNING_TYPE",
    OPTION_ARG_OPTIONAL,
    "Specify the type of confirmations to skip: WARNING_TYPE can be "safe" (default if omitted), or "all"",
    0
  },
  {
    "no-progress-bar", OPTION_NO_PROGRESS_BAR, 0,
    0,
    "Show progress bar while performing an action",
    0
  },

  { 0, 0, 0, 0, "Prompt options:", 0 },
  {
    "verbose",         'v',                    0,
    0,
    "Produce detailed output",
    0
  },
  {
    "quiet",           'q',                    0,
    0,
    "Don't produce any output",
    0
  },

  { 0, 0, 0, 0, 0, 0 }
};
/*
//add another parser for plugin options
static error_t parse_plug_in_opt()
{
}
*/
static error_t parse_opt(int key, char *arg, struct argp_state *state)
{
  struct arg_parser_arguments *arguments_p = state->input;
  switch (key)
  {
    case 'I':
      arguments_p->option_interface = true;
      arguments_p->argument_interface = arg;
      break;
    case 'A':
      arguments_p->option_action = true;
      arguments_p->argument_action = arg;
      break;
    case 'L':
      arguments_p->option_list = true;
      arguments_p->argument_list = arg;
      break;
    case 'i':
      arguments_p->option_input_dir = true;
      arguments_p->argument_input_dir = arg;
      break;
    case 'o':
      arguments_p->option_output_dir = true;
      arguments_p->argument_output_dir = arg;
      break;
    case 'u':
      arguments_p->option_use_file = true;
      arguments_p->argument_use_file = arg;
      break;
    case 'p':
      arguments_p->option_produce_file = true;
      arguments_p->argument_produce_file = arg;
      break;
    case OPTION_NO_LOGS:
      arguments_p->option_no_logs = true;
      break;
    case OPTION_NO_CONFIRM:
      arguments_p->option_no_confirm = true;
      arguments_p->argument_no_confirm = arg;
      break;
    case OPTION_NO_PROGRESS_BAR:
      arguments_p->option_no_progress_bar = true;
      break;
    case 'v':
      arguments_p->option_verbose = true;
      break;
    case 'q':
      arguments_p->option_quiet = true;
      break;
    case ARGP_KEY_NO_ARGS:
      break;

    default:
      return ARGP_ERR_UNKNOWN;
  }
  return 0;
}

void argp_initialize_arg_parser_arguments(struct arg_parser_arguments
                                          *arguments_p)
{
  arguments_p->option_interface = false;
  arguments_p->option_action = false;
  arguments_p->option_list = false;
  arguments_p->option_input_dir = false;
  arguments_p->option_output_dir = false;
  arguments_p->option_use_file = false;
  arguments_p->option_produce_file = false;
  arguments_p->option_no_logs = false;
  arguments_p->option_no_confirm = false;
  arguments_p->option_no_progress_bar = false;
  arguments_p->option_verbose = false;

  arguments_p->argument_interface = "-";
  arguments_p->argument_action = "-";
  arguments_p->argument_list = "-";
  arguments_p->argument_input_dir = "-";
  arguments_p->argument_output_dir = "-";
  arguments_p->argument_use_file = "-";
  arguments_p->argument_produce_file = "-";
  arguments_p->argument_no_confirm = "-";
}

//static struct argp_child *argp_child = {  };
static struct argp argp = { option, parse_opt, args_doc, doc, 0, 0, 0};

struct arg_parser_arguments *arg_parser_parse(int argc, char **argv)
{
  struct arg_parser_arguments arguments, *arguments_p = &arguments;
  argp_initialize_arg_parser_arguments(arguments_p);
  argp_parse(&argp, argc, argv, 0, 0, arguments_p);
  return(arguments_p);
}

osfi_arg_parser.h

#ifndef ARG_PARSER_H_INCLUDED
#define ARG_PARSER_H_INCLUDED

struct arg_parser_arguments
{
  bool option_interface,
       option_action,
       option_list,
       option_input_dir,
       option_output_dir,
       option_use_file,
       option_produce_file,
       option_no_logs,
       option_no_confirm,
       option_no_progress_bar,
       option_quiet,
       option_verbose;
  char *argument_interface,
       *argument_action,
       *argument_list,
       *argument_input_dir,
       *argument_output_dir,
       *argument_use_file,
       *argument_produce_file,
       *argument_no_confirm;

};

struct arg_parser_arguments *arg_parser_parse(int argc, char **argv);

#endif

NOTE: I know that code isn’t refactored, to 79 characters lenght and possibly uses concepts that may be old, unpractical or unoptimized for speed or different architecture use cases (I’m only learning and hope with further reading & OTHER questions on stack I will turn my code better in many aspects)

The behaviour of my code:

[[email protected] osfi]$ ./build/bin/osfi  --help
Usage: osfi [OPTION...] [OPTION]...
Tool that aims automating and speeding up installation and configuration
process of Linux-based OSes.

 Main options:
  -A, --action=ACTION        Perform a specified action
  -I, --interface=INTERFACE  Start with a specified interface
  -L, --list=OPTION_NAME     List avaliable values of a specified option
                             <I|interface|A|action>

 Specify a directory:
  -i, --input-dir=INPUT_DIR  Path to directory where to search for files
                             General path: <path-to-directory>  Specific path:
                             <i|install|p|progress|l|log>:<path-to-directory>
                             [...]
  -o,                              --output-dir=[FILE_TYPE:]OUTPUT_DIR
Path to directory where to output files of type
                             FILE_TYPE: FILE_TYPE can be "none" (default if
                             omitted), "i", "p", "l", "install", "progress", or
                             "log"

 Specify a file:
  -p, --output-file=PRODUCE_FILE
  -u, --use-file=USE_FILE    Use specified files

 Disable features:
      --no-confirm[=WARNING_TYPE]
                             Specify the type of confirmations to skip:
                             WARNING_TYPE can be "safe" (default if omitted),
                             or "all"
      --no-logs              Don't produce any logs
      --no-progress-bar      Show progress bar while performing an action

 Prompt options:
  -q, --quiet                Don't produce any output
  -v, --verbose              Produce detailed output

  -?, --help                 Give this help list
      --usage                Give a short usage message
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report bugs to .

The Questions:

  1. How to make argp aware of where i want my --output-dir=[FILE_TYPE:]OUTPUT_DIR and <documertation-of-the-option> generated strings placed?
  2. Is there a permanent solution for solving this text behaviour problem? (I have tried to use OPTION_DOC and adding an option just with text, all kinds of escape sequences.)
-o,                              --output-dir=[FILE_TYPE:]OUTPUT_DIR
Path to directory where to output files of type
                             FILE_TYPE: FILE_TYPE can be "none" (default if
                             omitted), "i", "p", "l", "install", "progress", or
                             "log"

Source: Windows Questions C++

LEAVE A COMMENT