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 }