root/lib/strutil/xstrtol.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. bkm_scale
  2. bkm_scale_by_power
  3. xstrtoumax

   1 /* A more useful interface to strtol.
   2 
   3    Copyright (C) 1995-2024
   4    Free Software Foundation, Inc.
   5 
   6    This program is free software: you can redistribute it and/or modify
   7    it under the terms of the GNU General Public License as published by
   8    the Free Software Foundation; either version 3 of the License, or
   9    (at your option) any later version.
  10 
  11    This program is distributed in the hope that it will be useful,
  12    but WITHOUT ANY WARRANTY; without even the implied warranty of
  13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14    GNU General Public License for more details.
  15 
  16    You should have received a copy of the GNU General Public License
  17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
  18 
  19 /* Written by Jim Meyering. */
  20 
  21 #include <config.h>
  22 
  23 #include <ctype.h>
  24 #include <errno.h>
  25 #include <inttypes.h>
  26 #include <limits.h>
  27 #include <stdlib.h>
  28 #include <string.h>
  29 
  30 #include "lib/strutil.h"
  31 
  32 /*** global variables ****************************************************************************/
  33 
  34 /*** file scope macro definitions ****************************************************************/
  35 
  36 /*** file scope type declarations ****************************************************************/
  37 
  38 /*** forward declarations (file scope functions) *************************************************/
  39 
  40 /*** file scope variables ************************************************************************/
  41 
  42 /* --------------------------------------------------------------------------------------------- */
  43 /*** file scope functions ************************************************************************/
  44 /* --------------------------------------------------------------------------------------------- */
  45 
  46 static strtol_error_t
  47 bkm_scale (uintmax_t *x, int scale_factor)
     /* [previous][next][first][last][top][bottom][index][help]  */
  48 {
  49     if (UINTMAX_MAX / scale_factor < *x)
  50     {
  51         *x = UINTMAX_MAX;
  52         return LONGINT_OVERFLOW;
  53     }
  54 
  55     *x *= scale_factor;
  56     return LONGINT_OK;
  57 }
  58 
  59 /* --------------------------------------------------------------------------------------------- */
  60 
  61 static strtol_error_t
  62 bkm_scale_by_power (uintmax_t *x, int base, int power)
     /* [previous][next][first][last][top][bottom][index][help]  */
  63 {
  64     strtol_error_t err = LONGINT_OK;
  65     while (power-- != 0)
  66         err |= bkm_scale (x, base);
  67     return err;
  68 }
  69 
  70 /* --------------------------------------------------------------------------------------------- */
  71 /*** public functions ****************************************************************************/
  72 /* --------------------------------------------------------------------------------------------- */
  73 
  74 /* Act like the system's strtol (NPTR, ENDPTR, BASE) except:
  75    - The TYPE of the result might be something other than long int.
  76    - Return strtol_error, and store any result through an additional
  77      TYPE *VAL pointer instead of returning the result.
  78    - If TYPE is unsigned, reject leading '-'.
  79    - Behavior is undefined if BASE is negative, 1, or greater than 36.
  80      (In this respect xstrtol acts like the C standard, not like POSIX.)
  81    - Accept an additional char const *VALID_SUFFIXES pointer to a
  82      possibly-empty string containing allowed numeric suffixes,
  83      which multiply the value.  These include SI suffixes like 'k' and 'M';
  84      these normally stand for powers of 1024, but if VALID_SUFFIXES also
  85      includes '0' they can be followed by "B" to stand for the usual
  86      SI powers of 1000 (or by "iB" to stand for powers of 1024 as before).
  87      Other supported suffixes include 'K' for 1024 or 1000, 'b' for 512,
  88      'c' for 1, and 'w' for 2.
  89    - Suppose that after the initial whitespace, the number is missing
  90      but there is a valid suffix.  Then the number is treated as 1.
  91 */
  92 strtol_error_t
  93 xstrtoumax (const char *nptr, char **endptr, int base, uintmax_t *val, const char *valid_suffixes)
     /* [previous][next][first][last][top][bottom][index][help]  */
  94 {
  95     char *t_ptr;
  96     char **p;
  97     uintmax_t tmp;
  98     strtol_error_t err = LONGINT_OK;
  99 
 100     p = endptr != NULL ? endptr : &t_ptr;
 101 
 102     {
 103         const char *q = nptr;
 104         unsigned char ch = *q;
 105 
 106         while (isspace (ch))
 107             ch = *++q;
 108 
 109         if (ch == '-')
 110         {
 111             *p = (char *) nptr;
 112             return LONGINT_INVALID;
 113         }
 114     }
 115 
 116     errno = 0;
 117     tmp = strtol (nptr, p, base);
 118 
 119     if (*p == nptr)
 120     {
 121         /* If there is no number but there is a valid suffix, assume the
 122            number is 1.  The string is invalid otherwise.  */
 123         if (!(valid_suffixes != NULL && *nptr != '\0' && strchr (valid_suffixes, *nptr) != NULL))
 124             return LONGINT_INVALID;
 125         tmp = 1;
 126     }
 127     else if (errno != 0)
 128     {
 129         if (errno != ERANGE)
 130             return LONGINT_INVALID;
 131         err = LONGINT_OVERFLOW;
 132     }
 133 
 134     /* Let valid_suffixes == NULL mean "allow any suffix".  */
 135     /* FIXME: update all callers except the ones that allow suffixes
 136        after the number, changing last parameter NULL to "".  */
 137     if (valid_suffixes == NULL)
 138     {
 139         *val = tmp;
 140         return err;
 141     }
 142 
 143     if (**p != '\0')
 144     {
 145         int xbase = 1024;
 146         int suffixes = 1;
 147         strtol_error_t overflow;
 148 
 149         if (strchr (valid_suffixes, **p) == NULL)
 150         {
 151             *val = tmp;
 152             return err | LONGINT_INVALID_SUFFIX_CHAR;
 153         }
 154 
 155         switch (**p)
 156         {
 157         case 'E':
 158         case 'G':
 159         case 'g':
 160         case 'k':
 161         case 'K':
 162         case 'M':
 163         case 'm':
 164         case 'P':
 165         case 'Q':
 166         case 'R':
 167         case 'T':
 168         case 't':
 169         case 'Y':
 170         case 'Z':
 171             if (strchr (valid_suffixes, '0') != NULL)
 172             {
 173                 /* The "valid suffix" '0' is a special flag meaning that
 174                    an optional second suffix is allowed, which can change
 175                    the base.  A suffix "B" (e.g. "100MB") stands for a power
 176                    of 1000, whereas a suffix "iB" (e.g. "100MiB") stands for
 177                    a power of 1024.  If no suffix (e.g. "100M"), assume
 178                    power-of-1024.  */
 179 
 180                 switch (p[0][1])
 181                 {
 182                 case 'i':
 183                     if (p[0][2] == 'B')
 184                         suffixes += 2;
 185                     break;
 186 
 187                 case 'B':
 188                 case 'D':      /* 'D' is obsolescent */
 189                     xbase = 1000;
 190                     suffixes++;
 191                     break;
 192                 default:
 193                     break;
 194                 }
 195             }
 196             break;
 197         default:
 198             break;
 199         }
 200 
 201         switch (**p)
 202         {
 203         case 'b':
 204             overflow = bkm_scale (&tmp, 512);
 205             break;
 206 
 207         case 'B':
 208             /* This obsolescent first suffix is distinct from the 'B'
 209                second suffix above.  E.g., 'tar -L 1000B' means change
 210                the tape after writing 1000 KiB of data.  */
 211             overflow = bkm_scale (&tmp, 1024);
 212             break;
 213 
 214         case 'c':
 215             overflow = LONGINT_OK;
 216             break;
 217 
 218         case 'E':              /* exa or exbi */
 219             overflow = bkm_scale_by_power (&tmp, xbase, 6);
 220             break;
 221 
 222         case 'G':              /* giga or gibi */
 223         case 'g':              /* 'g' is undocumented; for compatibility only */
 224             overflow = bkm_scale_by_power (&tmp, xbase, 3);
 225             break;
 226 
 227         case 'k':              /* kilo */
 228         case 'K':              /* kibi */
 229             overflow = bkm_scale_by_power (&tmp, xbase, 1);
 230             break;
 231 
 232         case 'M':              /* mega or mebi */
 233         case 'm':              /* 'm' is undocumented; for compatibility only */
 234             overflow = bkm_scale_by_power (&tmp, xbase, 2);
 235             break;
 236 
 237         case 'P':              /* peta or pebi */
 238             overflow = bkm_scale_by_power (&tmp, xbase, 5);
 239             break;
 240 
 241         case 'Q':              /* quetta or 2**100 */
 242             overflow = bkm_scale_by_power (&tmp, xbase, 10);
 243             break;
 244 
 245         case 'R':              /* ronna or 2**90 */
 246             overflow = bkm_scale_by_power (&tmp, xbase, 9);
 247             break;
 248 
 249         case 'T':              /* tera or tebi */
 250         case 't':              /* 't' is undocumented; for compatibility only */
 251             overflow = bkm_scale_by_power (&tmp, xbase, 4);
 252             break;
 253 
 254         case 'w':
 255             overflow = bkm_scale (&tmp, 2);
 256             break;
 257 
 258         case 'Y':              /* yotta or 2**80 */
 259             overflow = bkm_scale_by_power (&tmp, xbase, 8);
 260             break;
 261 
 262         case 'Z':              /* zetta or 2**70 */
 263             overflow = bkm_scale_by_power (&tmp, xbase, 7);
 264             break;
 265 
 266         default:
 267             *val = tmp;
 268             return err | LONGINT_INVALID_SUFFIX_CHAR;
 269         }
 270 
 271         err |= overflow;
 272         *p += suffixes;
 273         if (**p != '\0')
 274             err |= LONGINT_INVALID_SUFFIX_CHAR;
 275     }
 276 
 277     *val = tmp;
 278     return err;
 279 }
 280 
 281 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */