/*      $NetBSD: numeric.c,v 1.9 2024/08/18 20:47:24 christos Exp $     */


/**
* \file numeric.c
*
* Handle options with numeric (integer) arguments.
*
* @addtogroup 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
*/

/*=export_func  optionShowRange
* private:
*
* what:  Show info about range constraints
* arg:   + tOptions * + pOpts     + program options descriptor  +
* arg:   + tOptDesc * + pOptDesc  + the descriptor for this arg +
* arg:   + void *     + rng_table + the value range tables      +
* arg:   + int        + rng_count + the number of entries       +
*
* doc:
*   Show information about a numeric option with range constraints.
=*/
void
optionShowRange(tOptions * pOpts, tOptDesc * pOD, void * rng_table, int rng_ct)
{
   const struct {long const rmin, rmax;} * rng = rng_table;

   char const * pz_indent = zTabHyp + tab_skip_ct;

   /*
    * The range is shown only for full usage requests and an error
    * in this particular option.
    */
   if (pOpts != OPTPROC_EMIT_USAGE) {
       if (pOpts <= OPTPROC_EMIT_LIMIT)
           return;
       pz_indent = ONE_TAB_STR;

       fprintf(option_usage_fp, zRangeErr, pOpts->pzProgName,
               pOD->pz_Name, pOD->optArg.argInt);
       pz_indent = "";
   }

   if (pOD->fOptState & OPTST_SCALED_NUM)
       fprintf(option_usage_fp, zRangeScaled, pz_indent);

   fprintf(option_usage_fp, (rng_ct > 1) ? zRangeLie : zRangeOnly, pz_indent);
   pz_indent = (pOpts != OPTPROC_EMIT_USAGE)
       ? ONE_TAB_STR
       : (zTabSpace + tab_skip_ct);

   for (;;) {
       if (rng->rmax == LONG_MIN)
           fprintf(option_usage_fp, zRangeExact, pz_indent, rng->rmin);
       else if (rng->rmin == LONG_MIN)
           fprintf(option_usage_fp, zRangeUpto, pz_indent, rng->rmax);
       else if (rng->rmax == LONG_MAX)
           fprintf(option_usage_fp, zRangeAbove, pz_indent, rng->rmin);
       else
           fprintf(option_usage_fp, zRange, pz_indent, rng->rmin,
                   rng->rmax);

       if  (--rng_ct <= 0) {
           fputc(NL, option_usage_fp);
           break;
       }
       fputs(zRangeOr, option_usage_fp);
       rng++;
   }

   if (pOpts > OPTPROC_EMIT_LIMIT)
       pOpts->pUsageProc(pOpts, EXIT_FAILURE);
}

/*=export_func  optionNumericVal
* private:
*
* what:  process an option with a numeric value.
* arg:   + tOptions * + opts + program options descriptor +
* arg:   + tOptDesc * + od   + the descriptor for this arg +
*
* doc:
*  Decipher a numeric value.
=*/
void
optionNumericVal(tOptions * opts, tOptDesc * od)
{
   char * pz;
   long   val;

   /*
    *  Guard against all the different ways this procedure might get invoked
    *  when there is no string argument provided.
    */
   if (INQUERY_CALL(opts, od) || (od->optArg.argString == NULL))
       return;

   /*
    *  Numeric options may have a range associated with it.
    *  If it does, the usage procedure requests that it be
    *  emitted by passing a NULL od pointer.  Also bail out
    *  if there is no option argument or if we are being reset.
    */
   if (  (od == NULL)
      || (od->optArg.argString == NULL)
      || ((od->fOptState & OPTST_RESET) != 0)
      || (opts <= OPTPROC_EMIT_LIMIT))
       return;

   errno = 0;
   val = strtol(od->optArg.argString, &pz, 0);
   if ((pz == od->optArg.argString) || (errno != 0))
       goto bad_number;

   if ((od->fOptState & OPTST_SCALED_NUM) != 0)
       switch (*(pz++)) {
       case NUL:  pz--; break;
       case 't':  val *= 1000; /* FALLTHROUGH */
       case 'g':  val *= 1000; /* FALLTHROUGH */
       case 'm':  val *= 1000; /* FALLTHROUGH */
       case 'k':  val *= 1000; break;

       case 'T':  val *= 1024; /* FALLTHROUGH */
       case 'G':  val *= 1024; /* FALLTHROUGH */
       case 'M':  val *= 1024; /* FALLTHROUGH */
       case 'K':  val *= 1024; break;

       default:   goto bad_number;
       }

   if (*pz != NUL)
       goto bad_number;

   if (od->fOptState & OPTST_ALLOC_ARG) {
       AGFREE(od->optArg.argString);
       od->fOptState &= ~OPTST_ALLOC_ARG;
   }

   od->optArg.argInt = val;
   return;

   bad_number:

   fprintf( stderr, zNotNumber, opts->pzProgName, od->optArg.argString );
   if ((opts->fOptSet & OPTPROC_ERRSTOP) != 0)
       (*(opts->pUsageProc))(opts, EXIT_FAILURE);

   errno = EINVAL;
   od->optArg.argInt = ~0;
}

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