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