/*      $NetBSD: autoopts.h,v 1.12 2024/08/18 20:47:24 christos Exp $   */


/*
*  \file autoopts.h
*
*  This file defines all the global structures and special values
*  used in the automated option processing library.
*
* @group autoopts
* @{
*/
/*
*  This file is part of AutoOpts, a companion to AutoGen.
*  AutoOpts is free software.
*  AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
*
*  AutoOpts is available under any one of two licenses.  The license
*  in use must be one of these two and the choice is under the control
*  of the user of the license.
*
*   The GNU Lesser General Public License, version 3 or later
*      See the files "COPYING.lgplv3" and "COPYING.gplv3"
*
*   The Modified Berkeley Software Distribution License
*      See the file "COPYING.mbsd"
*
*  These files have the following sha256 sums:
*
*  8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95  COPYING.gplv3
*  4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b  COPYING.lgplv3
*  13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239  COPYING.mbsd
*/

#ifndef AUTOGEN_AUTOOPTS_H
#define AUTOGEN_AUTOOPTS_H
#if 0
#include <stdnoreturn.h>
#else
#ifndef noreturn
#define noreturn __dead
#endif
#endif

#define AO_NAME_LIMIT           127
#define AO_NAME_SIZE            ((size_t)(AO_NAME_LIMIT + 1))

#ifndef AG_PATH_MAX
#  ifdef PATH_MAX
#    define AG_PATH_MAX         ((size_t)PATH_MAX)
#  else
#    ifdef __gnu_hurd__
#      define size_t unsigned long
#    endif
#    define AG_PATH_MAX         ((size_t)4096)
#  endif
#else
#  if defined(PATH_MAX) && (PATH_MAX > MAXPATHLEN)
#     undef  AG_PATH_MAX
#     define AG_PATH_MAX        ((size_t)PATH_MAX)
#  endif
#endif

#undef  EXPORT
#define EXPORT

#ifndef NUL
#define NUL                     '\0'
#endif
#define BEL                     '\a'
#define BS                      '\b'
#define HT                      '\t'
#define LF                      '\n'
#define VT                      '\v'
#define FF                      '\f'
#define CR                      '\r'

#if defined(_WIN32) && !defined(__CYGWIN__)
# define DIRCH                  '\\'
#else
# define DIRCH                  '/'
#endif

#ifndef EX_USAGE
  /**
   *  Command line usage problem
   */
#  define EX_USAGE              64
#endif
#ifndef EX_DATAERR
  /**
   *  The input data was incorrect in some way.
   */
#  define EX_DATAERR            64
#endif
#ifndef EX_NOINPUT
  /**
   *  option state was requested from a file that cannot be loaded.
   */
#  define EX_NOINPUT            66
#endif
#ifndef EX_SOFTWARE
  /**
   *  AutoOpts Software failure.
   */
#  define EX_SOFTWARE           70
#endif
#ifndef EX_OSERR
  /**
   *  Command line usage problem
   */
#  define EX_OSERR              71
#endif

#define NL '\n'
#ifndef C
/**
*  Coercive cast.  Compel an address to be interpreted as the type
*  of the first argument.  No complaints, just do it.
*/
#define C(_t,_p)  ((_t)VOIDP(_p))
#endif

/* The __attribute__((__warn_unused_result__)) feature
  is available in gcc versions 3.4 and newer,
  while the typeof feature has been available since 2.7 at least.  */
# if __GNUC__ < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ < 4)
#  define ignore_val(x) ((void) (x))
# else
#  define ignore_val(x) (({ __typeof__ (x) __x = (x); (void) __x; }))
# endif

/*
*  Convert the number to a list usable in a printf call
*/
#define NUM_TO_VER(n)           ((n) >> 12), ((n) >> 7) & 0x001F, (n) & 0x007F

#define NAMED_OPTS(po) \
       (((po)->fOptSet & (OPTPROC_SHORTOPT | OPTPROC_LONGOPT)) == 0)

#define SKIP_OPT(p)  (((p)->fOptState & OPTST_IMMUTABLE_MASK) != 0)

typedef int tDirection;
/**
* handling option presets.  Start with command line and work through
* config settings in reverse order.
*/
#define DIRECTION_PRESET        -1
/**
* handling normal options.  Start with first config file, then environment
* variables and finally the command line.
*/
#define DIRECTION_PROCESS       1
/**
* An initialzation phase or an option being loaded from program sources.
*/
#define DIRECTION_CALLED        0

#define PROCESSING(d)           ((d)>0)
#define PRESETTING(d)           ((d)<0)
#define CALLED(d)               ((d)==0)

/**
*  When loading a line (or block) of text as an option, the value can
*  be processed in any of several modes.
*/
typedef enum {
   /**
    *  If the value looks like a quoted string, then process it.  Double
    *  quoted strings are processed the way strings are in "C" programs,
    *  except they are treated as regular characters if the following
    *  character is not a well-established escape sequence.  Single quoted
    *  strings (quoted with apostrophies) are handled the way strings are
    *  handled in shell scripts, *except* that backslash escapes are
    *  honored before backslash escapes and apostrophies.
    */
   OPTION_LOAD_COOKED,

   /**
    * Even if the value begins with quote characters, do not do quote
    * processing.  Strip leading and trailing white space.
    */
   OPTION_LOAD_UNCOOKED,

   /**
    * Keep every part of the value between the delimiters.
    */
   OPTION_LOAD_KEEP
} tOptionLoadMode;

static tOptionLoadMode option_load_mode;

/**
*  The pager state is used by optionPagedUsage() procedure.
*  When it runs, it sets itself up to be called again on exit.
*  If, however, a routine needs a child process to do some work
*  before it is done, then 'pagerState' must be set to
*  'PAGER_STATE_CHILD' so that optionPagedUsage() will not try
*  to run the pager program before its time.
*/
typedef enum {
   PAGER_STATE_INITIAL, //@< initial option paging state

   /**
    * temp file created and optionPagedUsage is scheduled to run at exit
    */
   PAGER_STATE_READY,

   /**
    *  This is a child process used in creating shell script usage.
    */
   PAGER_STATE_CHILD
} tePagerState;

typedef enum {
   ENV_ALL,
   ENV_IMM,
   ENV_NON_IMM
} teEnvPresetType;

typedef enum {
   TOPT_UNDEFINED = 0,
   TOPT_SHORT,
   TOPT_LONG,
   TOPT_DEFAULT
} teOptType;

typedef struct {
   tOptDesc *          pOD;
   char const *        pzOptArg;
   opt_state_mask_t    flags;
   teOptType           optType;
} tOptState;
#define OPTSTATE_INITIALIZER(st) \
   { NULL, NULL, OPTST_ ## st, TOPT_UNDEFINED }

#define TEXTTO_TABLE \
       _TT_(LONGUSAGE) \
       _TT_(USAGE) \
       _TT_(VERSION)
#define _TT_(n) \
       TT_ ## n ,

typedef enum { TEXTTO_TABLE COUNT_TT } teTextTo;

#undef _TT_

/**
* option argument types.  Used to create usage information for
* particular options.
*/
typedef struct {
   char const * pzStr;
   char const * pzReq;
   char const * pzNum;
   char const * pzFile;
   char const * pzKey;
   char const * pzKeyL;
   char const * pzBool;
   char const * pzNest;
   char const * pzOpt;
   char const * pzNo;
   char const * pzBrk;
   char const * pzNoF;
   char const * pzSpc;
   char const * pzOptFmt;
   char const * pzTime;
} arg_types_t;

#define AGALOC(_c, _w)        ao_malloc((size_t)_c)
#define AGREALOC(_p, _c, _w)  ao_realloc(VOIDP(_p), (size_t)_c)
#define AGFREE(_p)            free(VOIDP(_p))
#define AGDUPSTR(_p, _s, _w)  (_p = ao_strdup(_s))

static void *
ao_malloc(size_t sz);

static void *
ao_realloc(void *p, size_t sz);

#define ao_free(_p) free(VOIDP(_p))

static char *
ao_strdup(char const * str);

/**
*  DO option handling?
*
*  Options are examined at two times:  at immediate handling time and at
*  normal handling time.  If an option is disabled, the timing may be
*  different from the handling of the undisabled option.  The OPTST_DIABLED
*  bit indicates the state of the currently discovered option.
*  So, here's how it works:
*
*  A) handling at "immediate" time, either 1 or 2:
*
*  1.  OPTST_DISABLED is not set:
*      IMM           must be set
*      DISABLE_IMM   don't care
*      TWICE         don't care
*      DISABLE_TWICE don't care
*      0 -and-  1 x x x
*
*  2.  OPTST_DISABLED is set:
*      IMM           don't care
*      DISABLE_IMM   must be set
*      TWICE         don't care
*      DISABLE_TWICE don't care
*      1 -and-  x 1 x x
*/
#define DO_IMMEDIATELY(_flg) \
   (  (((_flg) & (OPTST_DISABLED|OPTST_IMM)) == OPTST_IMM) \
   || (   ((_flg) & (OPTST_DISABLED|OPTST_DISABLE_IMM))    \
       == (OPTST_DISABLED|OPTST_DISABLE_IMM)  ))

/**
*  B) handling at "regular" time because it was not immediate
*
*  1.  OPTST_DISABLED is not set:
*      IMM           must *NOT* be set
*      DISABLE_IMM   don't care
*      TWICE         don't care
*      DISABLE_TWICE don't care
*      0 -and-  0 x x x
*
*  2.  OPTST_DISABLED is set:
*      IMM           don't care
*      DISABLE_IMM   don't care
*      TWICE         must be set
*      DISABLE_TWICE don't care
*      1 -and-  x x 1 x
*/
#define DO_NORMALLY(_flg) ( \
      (((_flg) & (OPTST_DISABLED|OPTST_IMM))            == 0)  \
   || (((_flg) & (OPTST_DISABLED|OPTST_DISABLE_IMM))    ==     \
                 OPTST_DISABLED)  )

/**
*  C)  handling at "regular" time because it is to be handled twice.
*      The immediate bit was already tested and found to be set:
*
*  3.  OPTST_DISABLED is not set:
*      IMM           is set (but don't care)
*      DISABLE_IMM   don't care
*      TWICE         must be set
*      DISABLE_TWICE don't care
*      0 -and-  ? x 1 x
*
*  4.  OPTST_DISABLED is set:
*      IMM           don't care
*      DISABLE_IMM   is set (but don't care)
*      TWICE         don't care
*      DISABLE_TWICE must be set
*      1 -and-  x ? x 1
*/
#define DO_SECOND_TIME(_flg) ( \
      (((_flg) & (OPTST_DISABLED|OPTST_TWICE))          ==     \
                 OPTST_TWICE)                                  \
   || (((_flg) & (OPTST_DISABLED|OPTST_DISABLE_TWICE))  ==     \
                 (OPTST_DISABLED|OPTST_DISABLE_TWICE)  ))

/*
*  text_mmap structure.  Only active on platforms with mmap(2).
*/
#ifdef HAVE_SYS_MMAN_H
#  include <sys/mman.h>
#else
#  ifndef  PROT_READ
#   define PROT_READ            0x01
#  endif
#  ifndef  PROT_WRITE
#   define PROT_WRITE           0x02
#  endif
#  ifndef  MAP_SHARED
#   define MAP_SHARED           0x01
#  endif
#  ifndef  MAP_PRIVATE
#   define MAP_PRIVATE          0x02
#  endif
#endif

#ifndef MAP_FAILED
#  define  MAP_FAILED           VOIDP(-1)
#endif

#ifndef  _SC_PAGESIZE
# ifdef  _SC_PAGE_SIZE
#  define _SC_PAGESIZE          _SC_PAGE_SIZE
# endif
#endif

#ifndef HAVE_STRCHR
extern char * strchr(char const * s, int c);
extern char * strrchr(char const * s, int c);
#endif

/**
* INQUERY_CALL() tests whether the option handling function has been
* called by an inquery (help text needed, or option being reset),
* or called by a set-the-option operation.
*/
#define INQUERY_CALL(_o, _d) (                  \
   ((_o) <= OPTPROC_EMIT_LIMIT)                \
   || ((_d) == NULL)                           \
   || (((_d)->fOptState & OPTST_RESET) != 0) )

/**
*  Define and initialize all the user visible strings.
*  We do not do translations.  If translations are to be done, then
*  the client will provide a callback for that purpose.
*/
#undef DO_TRANSLATIONS
#include "autoopts/usage-txt.h"

/**
*  File pointer for usage output
*/
FILE * option_usage_fp;
/**
*  If provided in the option structure
*/
static char const * program_pkgdatadir;
/**
* privately exported functions
*/
extern tOptProc optionPrintVersion, optionPagedUsage, optionLoadOpt;

#ifdef AUTOOPTS_INTERNAL

#ifndef PKGDATADIR
#  define PKGDATADIR ""
#endif
#define APOSTROPHE '\''

#define OPTPROC_L_N_S  (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)
#if defined(ENABLE_NLS) && defined(HAVE_LIBINTL_H)
# include <libintl.h>
#endif

typedef struct {
   size_t          fnm_len;
   uint32_t        fnm_mask;
   char const *    fnm_name;
} ao_flag_names_t;

/**
* Automated Options Usage Flags.
* NB: no entry may be a prefix of another entry
*/
#define AOFLAG_TABLE                            \
   _aof_(gnu,             OPTPROC_GNUUSAGE )   \
   _aof_(autoopts,        ~OPTPROC_GNUUSAGE)   \
   _aof_(no_misuse_usage, OPTPROC_MISUSE   )   \
   _aof_(misuse_usage,    ~OPTPROC_MISUSE  )   \
   _aof_(compute,         OPTPROC_COMPUTE  )

#define _aof_(_n, _f)   AOUF_ ## _n ## _ID,
typedef enum { AOFLAG_TABLE AOUF_COUNT } ao_flag_id_t;
#undef  _aof_

#define _aof_(_n, _f)   AOUF_ ## _n = (1 << AOUF_ ## _n ## _ID),
typedef enum { AOFLAG_TABLE } ao_flags_t;
#undef  _aof_

static char const   zNil[] = "";
static arg_types_t  argTypes             = { .pzStr = NULL };
static char         line_fmt_buf[32];
static bool         displayEnum          = false;
static char const   pkgdatadir_default[] = PKGDATADIR;
static char const * program_pkgdatadir   = pkgdatadir_default;
static tOptionLoadMode option_load_mode  = OPTION_LOAD_UNCOOKED;
static tePagerState pagerState           = PAGER_STATE_INITIAL;

static lo_noreturn void option_exits(int exit_code);
static lo_noreturn void fserr_exit(char const * prog, char const * op,
                                  char const * fname);
static             void fserr_warn(char const * prog, char const * op,
                                  char const * fname);
static lo_noreturn void ao_bug(char const * msg);

      FILE *       option_usage_fp      = NULL;

static char const * pz_enum_err_fmt;

tOptions * optionParseShellOptions = NULL;

static char const * shell_prog = NULL;
static char * script_leader    = NULL;
static char * script_trailer   = NULL;
static char * script_text      = NULL;
static bool   print_exit       = false;
#endif /* AUTOOPTS_INTERNAL */

#endif /* AUTOGEN_AUTOOPTS_H */
/**
* @}
* Local Variables:
* mode: C
* c-file-style: "stroustrup"
* indent-tabs-mode: nil
* End:
* end of autoopts/autoopts.h */