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