1 #include <sys/types.h>
   2 #include <sys/stat.h>
   3 #include <fcntl.h>
   4 #include <stdio.h>
   5 #include <stdlib.h>
   6 #include <string.h>
   7 #include <errno.h>
   8 #include <locale.h>
   9 #include <stddef.h>
  10 #include <limits.h>
  11 
  12 #include <project.h>
  13 
  14 #include <ctype.h>
  15 
  16 #include "util.h"
  17 
  18 
  19 
  20 #define BOSTR_REG_EXP   "^"
  21 #define EOSTR_REG_EXP   "$"
  22 #define FLTNM_REG_EXP   "([[:digit:]]+(\\.[[:digit:]]+)?)"
  23 #define MODIF_REG_EXP   "([kmgtpeKMGTPE])?"
  24 #define UNIT__REG_EXP   "([bsBS])?"
  25 #define TOKEN_REG_EXP   "[[:alnum:]_./=+-]*"
  26 #define VALUE_REG_EXP   FLTNM_REG_EXP MODIF_REG_EXP UNIT__REG_EXP
  27 
  28 #define TO_EXP(X)       BOSTR_REG_EXP X EOSTR_REG_EXP
  29 #define TOKEN_EXP       TO_EXP(TOKEN_REG_EXP)
  30 #define VALUE_EXP       TO_EXP(VALUE_REG_EXP)
  31 
  32 #define BYTES_SCALE     1
  33 #define SCNDS_SCALE     2
  34 
  35 char
  36 *util_safe_strdup(char *str)
  37 {
  38         char *ptr;
  39         if (str == NULL)
  40                 return (NULL);
  41 
  42         if ((ptr = strdup(str)) == NULL) {
  43                 (void) fprintf(stderr, gettext("error allocating memory"));
  44                 exit(1);
  45         }
  46         return (ptr);
  47 }
  48 
  49 void *
  50 util_safe_realloc(void *ptr, size_t sz)
  51 {
  52         if ((ptr = realloc(ptr, sz)) == NULL) {
  53                 (void) fprintf(stderr, gettext(
  54                     "error reallocating %d bytes of memory"), sz);
  55                 exit(1);
  56         }
  57         return (ptr);
  58 }
  59 
  60 
  61 void *
  62 util_safe_malloc(size_t sz)
  63 {
  64         char *ptr;
  65         if ((ptr = malloc(sz)) == NULL) {
  66                 (void) fprintf(stderr, gettext(
  67                     "error allocating %d bytes of memory\n"), sz);
  68                 exit(1);
  69         }
  70         return (ptr);
  71 }
  72 
  73 void *
  74 util_safe_zmalloc(size_t sz)
  75 {
  76         return (memset(util_safe_malloc(sz), 0, sz));
  77 }
  78 
  79 void
  80 util_print_errmsgs(lst_t *errlst)
  81 {
  82         char *errmsg;
  83         while (!lst_is_empty(errlst)) {
  84                 errmsg = lst_at(errlst, 0);
  85                 (void) lst_remove(errlst, errmsg);
  86                 (void) fprintf(stderr, "%s\n", errmsg);
  87                 free(errmsg);
  88         }
  89 }
  90 
  91 
  92 void
  93 util_add_errmsg(lst_t *errlst, char *format, ...)
  94 {
  95         va_list args;
  96         char *errmsg;
  97 
  98         va_start(args, format);
  99         if (vasprintf(&errmsg, format, args) < 0) {
 100                 va_end(args);
 101                 (void) fprintf(stderr, gettext(
 102                     "error allocating memory\n"));
 103                 exit(1);
 104         }
 105         va_end(args);
 106         lst_insert_tail(errlst, errmsg);
 107 }
 108 
 109 char *
 110 util_str_append(char *str, int nargs, ...)
 111 {
 112         va_list ap;
 113         int i, len;
 114         char *s;
 115 
 116         if (str == NULL)
 117                 str = util_safe_zmalloc(1);
 118 
 119         len = strlen(str) + 1;
 120         va_start(ap, nargs);
 121         for (i = 0; i < nargs; i++) {
 122                 s = va_arg(ap, char *);
 123                 len += strlen(s);
 124                 str = util_safe_realloc(str, len);
 125                 (void) strcat(str, s);
 126         }
 127         va_end(ap);
 128         return (str);
 129 }
 130 
 131 char *
 132 util_substr(regex_t *reg, regmatch_t *mat, char *str, int idx)
 133 {
 134         int mat_len;
 135         char *ret;
 136 
 137         if (idx < 0 || idx > reg->re_nsub)
 138                 return (NULL);
 139 
 140         mat_len = mat[idx].rm_eo - mat[idx].rm_so;
 141 
 142         ret = util_safe_malloc(mat_len + 1);
 143         *ret = '\0';
 144 
 145         (void) strlcpy(ret, str + mat[idx].rm_so, mat_len + 1);
 146         return (ret);
 147 }
 148 
 149 typedef struct {
 150         char unit;
 151         uint64_t val;
 152 } scl;
 153 
 154 #define SCLS    7
 155 
 156 int
 157 util_scale(char *unit, int scale, uint64_t *res, lst_t *errlst)
 158 {
 159         int i;
 160         scl *sc;
 161         scl bscale[SCLS] = {
 162                 {'\0', 1ULL},
 163                 {'k', 1024ULL},
 164                 {'m', 1048576ULL},
 165                 {'g', 1073741824ULL},
 166                 {'t', 1099511627776ULL},
 167                 {'p', 1125899906842624ULL},
 168                 {'e', 1152921504606846976ULL},
 169         };
 170 
 171         scl oscale[SCLS] = {
 172                 {'\0', 1ULL},
 173                 {'k', 1000ULL},
 174                 {'m', 1000000ULL},
 175                 {'g', 1000000000ULL},
 176                 {'t', 1000000000000ULL},
 177                 {'p', 1000000000000000ULL},
 178                 {'e', 1000000000000000000ULL},
 179         };
 180 
 181         sc = (scale == BYTES_SCALE) ? bscale : oscale;
 182 
 183         for (i = 0; i < SCLS; i++) {
 184                 if (tolower(*unit) == sc[i].unit) {
 185                         *res = sc[i].val;
 186                         return (0);
 187                 }
 188         }
 189 
 190         util_add_errmsg(errlst, gettext(
 191             "Invalid scale: %d"), scale);
 192         return (1);
 193 }
 194 
 195 
 196 int
 197 util_val2num(char *value, int scale, lst_t *errlst, char **retnum,
 198     char **retmod, char **retunit)
 199 {
 200         int ret = 1;
 201         regex_t valueexp;
 202         regmatch_t *mat;
 203         int nmatch;
 204         char *num, *modifier, *unit;
 205         char *ptr;
 206 
 207         uint64_t mul64;
 208         long double dnum;
 209 
 210         *retnum = *retmod = *retunit = NULL;
 211 
 212         if (regcomp(&valueexp, VALUE_EXP, REG_EXTENDED) != 0) {
 213                 util_add_errmsg(errlst, gettext(
 214                     "Failed to compile regex: '%s'"), VALUE_EXP);
 215                 return (1);
 216         }
 217 
 218         nmatch = valueexp.re_nsub + 1;
 219         mat = util_safe_malloc(nmatch * sizeof (regmatch_t));
 220 
 221         if (regexec(&valueexp, value, nmatch, mat, 0) != 0) {
 222                 util_add_errmsg(errlst, gettext(
 223                     "Invalid value: '%s'"), value);
 224                 regfree(&valueexp);
 225                 return (1);
 226         }
 227         regfree(&valueexp);
 228 
 229         num = util_substr(&valueexp, mat, value, 1);
 230         modifier = util_substr(&valueexp, mat, value, 3);
 231         unit = util_substr(&valueexp, mat, value, 4);
 232 
 233         if ((num == NULL || modifier == NULL || unit == NULL) ||
 234             (strlen(modifier) == 0 && strchr(num, '.') != NULL) ||
 235             (util_scale(modifier, scale, &mul64, errlst) != 0) ||
 236             (scale == BYTES_SCALE && *unit != '\0' && tolower(*unit) != 'b') ||
 237             (scale == SCNDS_SCALE && *unit != '\0' && tolower(*unit) != 's') ||
 238             (scale == COUNT_SCALE && *unit != '\0') ||
 239             (scale == UNKWN_SCALE && *unit != '\0'))  {
 240                 util_add_errmsg(errlst, gettext("Error near: \"%s\""),
 241                     value);
 242                 goto out;
 243         }
 244 
 245         dnum = strtold(num, &ptr);
 246         if (dnum == 0 &&
 247             (errno == EINVAL || errno == ERANGE) &&
 248             *ptr != '\0') {
 249                 util_add_errmsg(errlst, gettext("Invalid value:  \"%s\""),
 250                     value);
 251                 goto out;
 252         }
 253 
 254         if (UINT64_MAX / mul64 <= dnum) {
 255                 util_add_errmsg(errlst, gettext("Too big value:  \"%s\""),
 256                     value);
 257                 goto out;
 258         }
 259 
 260         if (asprintf(retnum, "%llu",
 261             (unsigned long long)(mul64 * dnum)) == -1) {
 262                 goto out;
 263         }
 264 
 265         free(num);
 266         *retmod = modifier;
 267         *retunit = unit;
 268         return (0);
 269 out:
 270         free(num); free(modifier); free(unit);
 271         return (ret);
 272 }
 273 
 274 void
 275 util_free_tokens(char **tokens)
 276 {
 277         char *token;
 278         while ((token = *tokens++) != NULL) {
 279                 free(token);
 280         }
 281 }
 282 
 283 char **
 284 util_tokenize(char *values, lst_t *errlst)
 285 {
 286         char *token, *t;
 287         char *v;
 288         regex_t tokenexp;
 289         char **ctoken, **tokens = NULL;
 290 
 291         if (regcomp(&tokenexp, TOKEN_EXP, REG_EXTENDED | REG_NOSUB) != 0)
 292                 return (tokens);
 293 
 294         /* Assume each character will be a token + NULL terminating value. */
 295         ctoken = tokens = util_safe_malloc(
 296             (strlen(values) + 1) * sizeof (char *));
 297         token = util_safe_malloc(strlen(values) + 1);
 298 
 299         v = values;
 300         *ctoken = NULL;
 301         while (v < (values + strlen(values))) {
 302 
 303                 /* get the next token */
 304                 t = token;
 305                 while ((*t = *v++) != '\0') {
 306                         if (*t == '(' || *t == ')' || *t == ','||
 307                             *v == '(' || *v == ')' || *v == ',') {
 308                                 *++t = '\0';
 309                                 break;
 310                         }
 311                         t++;
 312                 }
 313 
 314                 if (strcmp(token, "(") != 0 && strcmp(token, ")") != 0 &&
 315                     strcmp(token, ",") != 0) {
 316                         if (regexec(&tokenexp, token, 0, NULL, 0) != 0) {
 317                                 util_add_errmsg(errlst, gettext(
 318                                     "Invalid Character at or near \"%s\""),
 319                                     token);
 320                                 util_free_tokens(tokens);
 321                                 UTIL_FREE_SNULL(tokens);
 322                                 goto out1;
 323                         }
 324                 }
 325                 *ctoken++ = util_safe_strdup(token);
 326                 *ctoken = NULL;
 327         }
 328 
 329 out1:
 330         free(token);
 331         regfree(&tokenexp);
 332         return (tokens);
 333 }