1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <sys/param.h> 27 #include <libintl.h> 28 #include <string.h> 29 #include <stdlib.h> 30 #include <stdarg.h> 31 #include <stdio.h> 32 #include <errno.h> 33 #include <strings.h> 34 #include <sys/types.h> 35 #include <limits.h> 36 #include "utils.h" 37 38 static char PNAME_FMT[] = "%s: "; 39 static char ERRNO_FMT[] = ": %s\n"; 40 static char EOL_FMT[] = "\n"; 41 42 static char *pname; 43 44 char * 45 setpname(char *arg0) 46 { 47 char *p = strrchr(arg0, '/'); 48 49 if (p == NULL) 50 p = arg0; 51 else 52 p++; 53 pname = p; 54 return (pname); 55 } 56 57 /*PRINTFLIKE1*/ 58 void 59 warn(const char *format, ...) 60 { 61 int err = errno; 62 va_list alist; 63 if (pname != NULL) 64 (void) fprintf(stderr, gettext(PNAME_FMT), pname); 65 va_start(alist, format); 66 (void) vfprintf(stderr, format, alist); 67 va_end(alist); 68 if (strchr(format, '\n') == NULL) 69 if (err) 70 (void) fprintf(stderr, 71 gettext(ERRNO_FMT), strerror(err)); 72 else 73 (void) fprintf(stderr, gettext(EOL_FMT)); 74 75 76 } 77 78 static char *__metric_modifiers[] = { "K", "M", "G", "T", "P", "E", NULL }; 79 static uint64_t __metric_scales[] = { 80 1000LLU, 81 1000LLU * 1000, 82 1000LLU * 1000 * 1000, 83 1000LLU * 1000 * 1000 * 1000, 84 1000LLU * 1000 * 1000 * 1000 * 1000, 85 1000LLU * 1000 * 1000 * 1000 * 1000 * 1000 86 }; 87 static scale_t __metric_scale = { __metric_modifiers, __metric_scales }; 88 89 static char *__binary_modifiers[] = {"K", "M", "G", "T", "P", "E", NULL}; 90 static uint64_t __binary_scales[] = { 91 1024LLU, 92 1024LLU * 1024, 93 1024LLU * 1024 * 1024, 94 1024LLU * 1024 * 1024 * 1024, 95 1024LLU * 1024 * 1024 * 1024 * 1024, 96 1024LLU * 1024 * 1024 * 1024 * 1024 * 1024 97 }; 98 static scale_t __binary_scale = { __binary_modifiers, __binary_scales }; 99 100 scale_t *scale_metric = &__metric_scale; 101 scale_t *scale_binary = &__binary_scale; 102 103 int 104 scaledtouint64(char *scaledin, 105 uint64_t *uint64out, 106 int *widthout, char **modifierout, char **unitout, 107 scale_t *scale, char *unit, int flags) { 108 109 double result; 110 double value; 111 int index = 0; 112 uint64_t multiplier = 1; 113 char string[SCALED_STRLEN]; 114 char *endptr; 115 int cmp; 116 int hasmodifier = 0; 117 char **modifiers = scale->modifers; 118 uint64_t *scales = scale->scales; 119 120 if (modifierout) 121 *modifierout = NULL; 122 if (unitout) 123 *unitout = NULL; 124 125 /* 126 * first check for hex value, which cannot be scaled, as 127 * hex letters cannot be disserned from modifier or unit letters 128 */ 129 if ((strncmp("0x", scaledin, 2) == 0) || 130 (strncmp("0X", scaledin, 2) == 0)) { 131 132 /* unit cannot be required on hex values */ 133 if ((unit && *unit != '\0') && 134 !(flags & SCALED_UNIT_OPTIONAL_FLAG)) 135 return (SCALED_INVALID_UNIT); 136 137 errno = 0; 138 *uint64out = strtoull(scaledin, &endptr, 16); 139 if (errno) { 140 if (errno == ERANGE) 141 return (SCALED_OVERFLOW); 142 else 143 return (SCALED_INVALID_NUMBER); 144 } 145 if (*endptr != '\0') 146 return (SCALED_INVALID_NUMBER); 147 148 /* compute width of decimal equivalent */ 149 if (widthout) { 150 (void) snprintf( 151 string, SCALED_STRLEN, "%llu", *uint64out); 152 *widthout = strlen(string); 153 } 154 return (0); 155 } 156 157 /* scan out numeric value */ 158 errno = 0; 159 value = strtod(scaledin, &endptr); 160 if (errno) { 161 if (errno == ERANGE) 162 return (SCALED_OVERFLOW); 163 else 164 return (SCALED_INVALID_NUMBER); 165 166 } 167 if (endptr == scaledin) 168 return (SCALED_INVALID_NUMBER); 169 170 /* no negative values */ 171 if (strchr(scaledin, '-')) 172 return (SCALED_INVALID_NUMBER); 173 if (value < 0.0) 174 return (SCALED_INVALID_NUMBER); 175 176 177 /* compute width of number string */ 178 if (widthout) 179 *widthout = (int)(endptr - scaledin); 180 181 /* check possible modifier */ 182 if (*endptr != '\0') { 183 index = 0; 184 while (modifiers[index] != NULL) { 185 if (flags & SCALED_MODIFIER_CASE_INSENSITIVE_FLAG) 186 cmp = strncasecmp(modifiers[index], endptr, 187 strlen(modifiers[index])); 188 else 189 cmp = strncmp(modifiers[index], endptr, 190 strlen(modifiers[index])); 191 192 if (cmp == 0) { 193 if (modifierout) 194 *modifierout = modifiers[index]; 195 endptr += strlen(modifiers[index]); 196 multiplier = scales[index]; 197 result = value * multiplier; 198 if (result > UINT64_MAX) 199 return (SCALED_OVERFLOW); 200 201 *uint64out = (uint64_t)result; 202 hasmodifier = 1; 203 break; 204 } 205 index++; 206 } 207 } 208 /* if there is no modifier, value must be an integer */ 209 if (!hasmodifier) { 210 errno = 0; 211 *uint64out = strtoull(scaledin, &endptr, 0); 212 if (errno) { 213 if (errno == ERANGE) 214 return (SCALED_OVERFLOW); 215 else 216 return (SCALED_INVALID_NUMBER); 217 } 218 if (endptr == scaledin) 219 return (SCALED_INVALID_NUMBER); 220 } 221 222 /* if unit is present when no unit is allowed, fail */ 223 if ((unit == NULL || *unit == '\0') && (*endptr != '\0')) 224 return (SCALED_INVALID_UNIT); 225 226 /* check for missing unit when unit is required */ 227 if ((unit && *unit != '\0') && 228 !(flags & SCALED_UNIT_OPTIONAL_FLAG) && 229 (*endptr == '\0')) 230 return (SCALED_INVALID_UNIT); 231 232 /* validate unit */ 233 if (unit && *unit != '\0') { 234 235 /* allow for missing unit if it is optional */ 236 if ((flags & SCALED_UNIT_OPTIONAL_FLAG) && 237 (*endptr == '\0')) 238 return (0); 239 240 if (flags & SCALED_UNIT_CASE_INSENSITIVE_FLAG) 241 cmp = strncasecmp(unit, endptr, strlen(unit)); 242 else 243 cmp = strncmp(unit, endptr, strlen(unit)); 244 245 if (cmp != 0) 246 return (SCALED_INVALID_UNIT); 247 248 if (*(endptr + strlen(unit)) != '\0') 249 return (SCALED_INVALID_UNIT); 250 251 if (unitout) 252 *unitout = unit; 253 } 254 return (0); 255 } 256 257 258 int 259 uint64toscaled(uint64_t uint64in, int widthin, char *maxmodifierin, 260 char *scaledout, int *widthout, char **modifierout, 261 scale_t *scale, char *unit, int flags) { 262 263 int index = 0; 264 int count; 265 int width; 266 int decimals = 0; 267 char string[SCALED_STRLEN]; 268 double value; 269 char **modifiers = scale->modifers; 270 uint64_t *scales = scale->scales; 271 272 /* don't scale if there is no reason to */ 273 if (uint64in < scales[0] || maxmodifierin == NULL) { 274 if (flags & SCALED_PAD_WIDTH_FLAG) 275 width = widthin; 276 else 277 width = 0; 278 279 (void) snprintf(string, SCALED_STRLEN, "%%%dllu", width); 280 /* LINTED */ 281 count = snprintf(scaledout, SCALED_STRLEN, string, uint64in); 282 if (unit && *unit != '\0') 283 (void) strcat(scaledout, unit); 284 285 if (widthout) 286 *widthout = count; 287 288 if (modifierout) 289 *modifierout = NULL; 290 291 return (0); 292 } 293 294 for (index = 0; modifiers[index + 1] != NULL; index++) { 295 296 if (uint64in >= scales[index] && 297 uint64in < scales[index + 1]) 298 break; 299 300 if ((strncmp(modifiers[index], maxmodifierin, 301 strlen(modifiers[index])) == 0) && 302 (strlen(modifiers[index]) == strlen(maxmodifierin))) 303 break; 304 305 } 306 307 value = ((double)(uint64in)) / scales[index]; 308 if (modifierout) 309 *modifierout = modifiers[index]; 310 311 count = snprintf(string, SCALED_STRLEN, "%0.0lf", value); 312 while (count < widthin) { 313 decimals++; 314 (void) snprintf(string, SCALED_STRLEN, "%%0.%dlf", decimals); 315 /* LINTED */ 316 count = snprintf(scaledout, SCALED_STRLEN, string, value); 317 318 /* reduce decimal places if we've overshot the desired width */ 319 if (count > widthin) { 320 decimals--; 321 break; 322 } 323 } 324 325 if (flags & SCALED_PAD_WIDTH_FLAG) 326 width = widthin; 327 else 328 width = 0; 329 330 (void) snprintf(string, SCALED_STRLEN, "%%%d.%dlf", width, decimals); 331 /* LINTED */ 332 count = snprintf(scaledout, SCALED_STRLEN, string, value); 333 334 (void) strcat(scaledout, modifiers[index]); 335 336 if (unit && *unit != '\0') 337 (void) strcat(scaledout, unit); 338 339 if (widthout) 340 *widthout = count; 341 342 return (0); 343 } 344 345 int 346 scaledtoscaled(char *scaledin, int widthin, char *maxmodifierin, 347 char *scaledout, int *widthout, char **modifierout, 348 scale_t *scale, char *unit, int flags) { 349 350 int ret; 351 uint64_t val; 352 353 ret = scaledtouint64(scaledin, &val, NULL, NULL, NULL, 354 scale, unit, flags); 355 if (ret) 356 return (ret); 357 358 ret = uint64toscaled(val, widthin, maxmodifierin, 359 scaledout, widthout, modifierout, 360 scale, unit, flags); 361 362 return (ret); 363 } 364 365 int 366 scaledeqscaled(char *scaled1, char *scaled2, 367 scale_t *scale, char *unit, int flags) { 368 369 int ret; 370 uint64_t uint64; 371 char *modifier1; 372 char *modifier2; 373 char *modifier = NULL; 374 int i; 375 int width; 376 int width1; 377 int width2; 378 char scaledA[SCALED_STRLEN]; 379 char scaledB[SCALED_STRLEN]; 380 char **modifiers = scale->modifers; 381 382 /* 383 * remove padding flag, so strings to compare will not have 384 * whitespace 385 */ 386 flags = flags & (~SCALED_PAD_WIDTH_FLAG); 387 388 /* determine each number's width and modifier */ 389 ret = scaledtouint64(scaled1, &uint64, &width1, &modifier1, NULL, 390 scale, unit, flags); 391 if (ret) 392 return (0); 393 394 ret = scaledtouint64(scaled2, &uint64, &width2, &modifier2, NULL, 395 scale, unit, flags); 396 if (ret) 397 return (0); 398 399 /* 400 * determine the width and modifier to use for comparison. 401 * Use widest width and smallest modifier. 402 * Rescale to new width and modifier 403 */ 404 405 if (modifier1 == NULL || modifier2 == NULL) 406 modifier = NULL; 407 else { 408 for (i = 0; modifiers[i] != NULL; i++) { 409 410 if (strcmp(modifier1, modifiers[i]) == 0) { 411 modifier = modifiers[i]; 412 break; 413 } 414 if (strcmp(modifier2, modifiers[i]) == 0) { 415 modifier = modifiers[i]; 416 break; 417 } 418 } 419 } 420 width = 0; 421 if (width1 > width) 422 width = width1; 423 if (width2 > width) 424 width = width2; 425 426 /* 427 * Convert first number to width and modifier. 428 * This is done for the following reasons: 429 * 1. In case first number is hecadecimal. This will convert 430 * it to decimal 431 * 2. In case the first number has < the minimum number of 432 * columns. 433 * 3. The first number is missing an optional unit string. 434 * 4. Fix casing of modifier and unit. 435 */ 436 437 ret = scaledtoscaled(scaled1, width, modifier, 438 scaledA, NULL, NULL, scale, unit, flags); 439 if (ret) 440 return (0); 441 442 /* convert second number to width and modifier matching first number */ 443 ret = scaledtoscaled(scaled2, width, modifier, 444 scaledB, NULL, NULL, scale, unit, flags); 445 if (ret) 446 return (0); 447 448 /* numbers are equal if strings match */ 449 return ((strncmp(scaledA, scaledB, strlen(scaledA)) == 0) && 450 (strlen(scaledA) == strlen(scaledB))); 451 452 } 453 454 int 455 scaledequint64(char *scaled, uint64_t uint64, int minwidth, 456 scale_t *scale, char *unit, int flags) { 457 458 int ret; 459 uint64_t tmpuint64; 460 char *modifier; 461 int width; 462 463 char scaledA[SCALED_STRLEN]; 464 char scaledB[SCALED_STRLEN]; 465 466 /* determine for number's width and modifier */ 467 ret = scaledtouint64(scaled, &tmpuint64, &width, &modifier, NULL, 468 scale, unit, flags); 469 if (ret) 470 return (0); 471 472 if (width < minwidth) 473 width = minwidth; 474 475 /* 476 * Convert first number to width and modifier. 477 * This is done for the following reasons: 478 * 1. In case first number is hecadecimal. This will convert 479 * it to decimal 480 * 2. In case the first number has < the minimum number of 481 * columns. 482 * 3. The first number is missing an optional unit string. 483 * 4. Fix casing of modifier and unit. 484 */ 485 486 ret = scaledtoscaled(scaled, width, modifier, 487 scaledA, NULL, NULL, scale, unit, flags); 488 if (ret) 489 return (0); 490 491 /* convert second number to width and modifier matching first number */ 492 ret = uint64toscaled(uint64, width, modifier, 493 scaledB, NULL, NULL, scale, unit, flags); 494 if (ret) 495 return (0); 496 497 /* numbers are equal if strings match */ 498 return ((strncmp(scaledA, scaledB, strlen(scaledA)) == 0) && 499 (strlen(scaledA) == strlen(scaledB))); 500 } 501