#ifndef argparser_H
#define argparser_H

/* Facilities for parsing command line arguments. */
/* Last edited on 2005-01-16 15:00:23 by stolfi */
/* Copyright © 2005 Jorge Stolfi, UNICAMP. See note at end of file. */

/* Based on Params.i3 by J.Stolfi, DEC-SRC, 1988.  */

/* This interface provides simple and robust tools for parsing the
  command line arguments given to a process when it is started. Check
  the usage example at the end of this interface. */

#include <vec.h>
#include <js.h>
#include <stdio.h>

typedef struct argparser_t /* A parser for command line arguments. */
  { string_vec_t arg;   /* Command line arguments; {arg[0]} is prog name. */
    bool_vec_t parsed;  /* parsed[i] is {TRUE} if {arg[i]} has been parsed. */
    unsigned next;      /* The next argument to parse is {arg[next]} */
    FILE *wr;           /* File for errors */
    unsigned nusage;    /* Number of lines of error help text. */
    string_vec_t usage; /* {usage.el[0..nusage-1]}  is the help text for errors. */
  } argparser_t;
  
argparser_t *argparser_new(FILE *wr, int argc, char **argv);
  /* Saves pointers to the given command line arguments. Marks the
   command name {arg[0]} as parsed, all other arguments as unparsed.
   The next argument to be parsed will be {arg[1]}. Any parsing
   errors will be printed to {wr}. */

void argparser_set_usage(argparser_t *pp, char *msg);
  /* Appends {msg} to the ``usage'' help text. If a syntax error
    is found during argument parsing, this text will be
    written to {pp->wr}, just before halting the program. */

bool argparser_keyword_present(argparser_t *pp, char *key);
  /*  Looks for the first unparsed argument {arg[i]} that is equal to
    {key}. If found, marks it as parsed, sets {pp->next} to {i+1}, and
    returns {TRUE}. Otherwise returns {FALSE} and leaves {pp->next}
    unchanged. */

void argparser_get_keyword(argparser_t *pp, char *key);
  /* Same as {argparser_keyword_present}, but raises error if the 
    keyword is not found. */

bool argparser_is_next(argparser_t *pp, char *key);
  /* Returns TRUE if and only if {arg[pp->next]} exists, is still 
    unparsed, and is equal to {key}. */

bool argparser_test_next(argparser_t *pp, char *key);
  /* If {argparser_is_next(pp, key)} is true, marks the next argument
    as parsed, increments {pp->next} and returns TRUE. Otherwise does
    none of these things and returns {FALSE}. */

void argparser_match_next(argparser_t *pp, char *key);
  /* If {argparser_is_next(pp, key)} is true, marks the next argument
    as parsed and increments {pp->next}. Otherwise raises an error. */

char *argparser_get_next(argparser_t *pp);
  /* Returns {arg[pp->next]}, marks it as parsed and increments {pp->next}.  
    Raises error if {arg[pp->next]} does not exist or has already 
    been parsed. */

int argparser_get_next_int(argparser_t *pp, int min, int max);
double argparser_get_next_double(argparser_t *pp, double min, double max);
  /* Same as {argparser_get_next}, but converts the result to the
    approriate type (using {strtol} and {strtod}, respectively).
    Raises error if the parameter is not a valid literal, or lies
    outside of the range {[min..max]}.  */

int_vec_t argparser_get_int_list(argparser_t *pp, char *key, int min, int max);
  /* Parses all (zero or more) unparsed occurrences of the keyword
    {key}, not necessarily in consecutive positions. Requires that each
    occurrence is immediately followed by an integer in {[min..max]}.
    Returns an array with those integers, in the order found. */

void argparser_error(argparser_t *pp, char *msg);
  /* Prints the given message, the help text, and terminates the program. */

void argparser_skip_parsed(argparser_t *pp);
  /* Points {pp->next} at the first unparsed argument. If there are
    any parsed arguments beyond that one, prints a message and raises
    error. */

void argparser_finish(argparser_t *pp);
  /* Checks whether all parameters have been parsed; if not, prints a
    message and raises error. Also reclaims {*pp} and all its internal
    storage. */

/*
  Most Unix programs expect their command-line arguments to consist
  of a string of keywords and keyword-labeled arguments (`options',
  `switches', etc.), followed by a list of positional arguments.

  For the user's convenience, programs generally allow the switches
  and keyword-labeled arguments to be given in any order. Some of
  those parameters may be optional and/or repeatable, some may be
  mandatory; some may be required or forbidden depending on the values
  of the other parameters. Furthermore, the value of an argument may
  be just a number or a text string, or may be a cluster of two or
  more values with their own little syntax.

  This module simplifies the parsing of such command-line parameters,
  by allowing the program to scan the arguments in the order which
  most suits the program. This module also detects automatically many
  kinds of common mistakes --- such as arguments that are missing, repeated,
  extraneous, malformed, or out of range --- and prints the appropriate
  error messages.

  For example, here is how this module could be used by an
  hypothetical program {prt} that concatenates a bunch of files and
  prints selected line ranges of the result, possibly in reverse
  order, with several formatting options.

    #define MaxLines MAX_INT
    #define MaxRanges 100
    #define MaxFiles 100
    #define MinFontSize 1
    #define MaxFontSize 100
    
    / * Arguments from command line: * /
    int fontSize;
    bool landscape;
    int nRanges = 0;
    int ini[MaxRanges], fin[MaxRanges];
    bool reverse[MaxRanges];
    int nFiles = 0;
    char *files[MaxFiles];
    
    void parse_args(int argc, char **argv)
      {
        static char *usage = 
          "prt \\\n"
          "  -fontSize NUM \\\n"
          "  [ -landscape | -portrait ] \\\n"
          "  [ -lines NUM NUM [ -reverse ] ]..."
          "  FNAME...";
    
        / * Initialize the argument parser: * /
        argparser_t *pp = argparser_new(stderr, argc, argv);
        argparser_set_usage(pp, usage);
    
        / * The "-fontSize" parameter is mandatory: * /
        argparser_get_keyword(pp, "-fontSize");
        fontSize = argparser_get_next_int(pp, MinFontsize, MaxFontSize);
    
        / * Either "-landscape" or "-portrait", but not both: * /
        if (argparser_keyword_present(pp, "-landscape"))
          { landscape = TRUE; } 
        else if (argparser_keyword_present(pp, "-portrait"))
          { landscape = FALSE; }
        else
          { / * Default is "-portrait" unless font is too big. * /
            landscape = (fontSize > 8);
          }
    
        / * Parse the line ranges: * /
        nRanges = 0;
        while (argparser_keyword_present(pp, "-lines"))
          { if (nRanges >= MaxRanges) 
              { argparser_error(pp, "Too many page ranges"); }
            ini[nRanges] = argparser_get_next_int(pp, 1,MaxLines);
            fin[nRanges] = argparser_get_next_int(pp, ini[nRanges],MaxLines);
            rev[nRanges] = argparser_test_next(pp, "-reverse");
            nRanges = nRanges+1;
          }
    
        / * By default, print all lines: * /
        if (nRanges == 0)
          { ini[0] = 1; fin[0] = MaxLines; rev[0] = FALSE;
            nRanges = 1;
          }
    
        / * Parse the file list (after all keyword options): * /
        argparser_skip_parsed(pp);
        nFiles = argc - pp->next;
        if (nFiles == 0)
          { argparser_error(pp, "no files specified"); }
        for (i = 0; i < nFiles; i++)
          { files[i] = argparser_get_next(pp); }
    
        / * Check for any unparsed parameters: * /
        argparser_finish(pp);
      }

  Note that this code allows the user to give the options
  "-fontSize" and "-landscape"/"-portrait" in any order, even
  anywhere among or after the "-range" arguments. However, each
  "-range" flag must be immediately followed by two numbers; and the
  "-reverse" flag, if given, must immediately follow the second
  number.  Also, all file names must follow all the other options
  and arguments.
*/  

#endif
/* COPYRIGHT AND AUTHORSHIP NOTICE

  Copyright © 2005 Jorge Stolfi, Universidade Estadual de Campinas (UNICAMP).
  Created by Jorge Stolfi in 1992--2005.
  
  This source file can be freely distributed, used, and modified,
  provided that this copyright and authorship notice is preserved in
  all copies, and that any modified versions of this file are clearly
  marked as such.
  
  This software has NO WARRANTY of correctness or applicability for
  any purpose. Neither the author nor his employers shall be held
  responsible for any losses or damages that may result from its use.

  END OF NOTICE */