/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/param.h>
#include <libintl.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
#include <errno.h>
#include <strings.h>
#include <sys/types.h>
#include <limits.h>
#include "utils.h"
static char PNAME_FMT[] = "%s: ";
static char ERRNO_FMT[] = ": %s\n";
static char EOL_FMT[] = "\n";
static char *pname;
char *
setpname(char *arg0)
{
char *p = strrchr(arg0, '/');
if (p == NULL)
p = arg0;
else
p++;
pname = p;
return (pname);
}
/*PRINTFLIKE1*/
void
warn(const char *format, ...)
{
int err = errno;
va_list alist;
if (pname != NULL)
(void) fprintf(stderr, gettext(PNAME_FMT), pname);
va_start(alist, format);
(void) vfprintf(stderr, format, alist);
va_end(alist);
if (strchr(format, '\n') == NULL)
if (err)
(void) fprintf(stderr,
gettext(ERRNO_FMT), strerror(err));
else
(void) fprintf(stderr, gettext(EOL_FMT));
}
static char *__metric_modifiers[] = { "K", "M", "G", "T", "P", "E", NULL };
static uint64_t __metric_scales[] = {
1000LLU,
1000LLU * 1000,
1000LLU * 1000 * 1000,
1000LLU * 1000 * 1000 * 1000,
1000LLU * 1000 * 1000 * 1000 * 1000,
1000LLU * 1000 * 1000 * 1000 * 1000 * 1000
};
static scale_t __metric_scale = { __metric_modifiers, __metric_scales };
static char *__binary_modifiers[] = {"K", "M", "G", "T", "P", "E", NULL};
static uint64_t __binary_scales[] = {
1024LLU,
1024LLU * 1024,
1024LLU * 1024 * 1024,
1024LLU * 1024 * 1024 * 1024,
1024LLU * 1024 * 1024 * 1024 * 1024,
1024LLU * 1024 * 1024 * 1024 * 1024 * 1024
};
static scale_t __binary_scale = { __binary_modifiers, __binary_scales };
scale_t *scale_metric = &__metric_scale;
scale_t *scale_binary = &__binary_scale;
int
scaledtouint64(char *scaledin,
uint64_t *uint64out,
int *widthout, char **modifierout, char **unitout,
scale_t *scale, char *unit, int flags) {
double result;
double value;
int index = 0;
uint64_t multiplier = 1;
char string[SCALED_STRLEN];
char *endptr;
int cmp;
int hasmodifier = 0;
char **modifiers = scale->modifers;
uint64_t *scales = scale->scales;
if (modifierout)
*modifierout = NULL;
if (unitout)
*unitout = NULL;
/*
* first check for hex value, which cannot be scaled, as
* hex letters cannot be disserned from modifier or unit letters
*/
if ((strncmp("0x", scaledin, 2) == 0) ||
(strncmp("0X", scaledin, 2) == 0)) {
/* unit cannot be required on hex values */
if ((unit && *unit != '\0') &&
!(flags & SCALED_UNIT_OPTIONAL_FLAG))
return (SCALED_INVALID_UNIT);
errno = 0;
*uint64out = strtoull(scaledin, &endptr, 16);
if (errno) {
if (errno == ERANGE)
return (SCALED_OVERFLOW);
else
return (SCALED_INVALID_NUMBER);
}
if (*endptr != '\0')
return (SCALED_INVALID_NUMBER);
/* compute width of decimal equivalent */
if (widthout) {
(void) snprintf(
string, SCALED_STRLEN, "%llu", *uint64out);
*widthout = strlen(string);
}
return (0);
}
/* scan out numeric value */
errno = 0;
value = strtod(scaledin, &endptr);
if (errno) {
if (errno == ERANGE)
return (SCALED_OVERFLOW);
else
return (SCALED_INVALID_NUMBER);
}
if (endptr == scaledin)
return (SCALED_INVALID_NUMBER);
/* no negative values */
if (strchr(scaledin, '-'))
return (SCALED_INVALID_NUMBER);
if (value < 0.0)
return (SCALED_INVALID_NUMBER);
/* compute width of number string */
if (widthout)
*widthout = (int)(endptr - scaledin);
/* check possible modifier */
if (*endptr != '\0') {
index = 0;
while (modifiers[index] != NULL) {
if (flags & SCALED_MODIFIER_CASE_INSENSITIVE_FLAG)
cmp = strncasecmp(modifiers[index], endptr,
strlen(modifiers[index]));
else
cmp = strncmp(modifiers[index], endptr,
strlen(modifiers[index]));
if (cmp == 0) {
if (modifierout)
*modifierout = modifiers[index];
endptr += strlen(modifiers[index]);
multiplier = scales[index];
result = value * multiplier;
if (result > UINT64_MAX)
return (SCALED_OVERFLOW);
*uint64out = (uint64_t)result;
hasmodifier = 1;
break;
}
index++;
}
}
/* if there is no modifier, value must be an integer */
if (!hasmodifier) {
errno = 0;
*uint64out = strtoull(scaledin, &endptr, 0);
if (errno) {
if (errno == ERANGE)
return (SCALED_OVERFLOW);
else
return (SCALED_INVALID_NUMBER);
}
if (endptr == scaledin)
return (SCALED_INVALID_NUMBER);
}
/* if unit is present when no unit is allowed, fail */
if ((unit == NULL || *unit == '\0') && (*endptr != '\0'))
return (SCALED_INVALID_UNIT);
/* check for missing unit when unit is required */
if ((unit && *unit != '\0') &&
!(flags & SCALED_UNIT_OPTIONAL_FLAG) &&
(*endptr == '\0'))
return (SCALED_INVALID_UNIT);
/* validate unit */
if (unit && *unit != '\0') {
/* allow for missing unit if it is optional */
if ((flags & SCALED_UNIT_OPTIONAL_FLAG) &&
(*endptr == '\0'))
return (0);
if (flags & SCALED_UNIT_CASE_INSENSITIVE_FLAG)
cmp = strncasecmp(unit, endptr, strlen(unit));
else
cmp = strncmp(unit, endptr, strlen(unit));
if (cmp != 0)
return (SCALED_INVALID_UNIT);
if (*(endptr + strlen(unit)) != '\0')
return (SCALED_INVALID_UNIT);
if (unitout)
*unitout = unit;
}
return (0);
}
int
uint64toscaled(uint64_t uint64in, int widthin, char *maxmodifierin,
char *scaledout, int *widthout, char **modifierout,
scale_t *scale, char *unit, int flags) {
int index = 0;
int count;
int width;
int decimals = 0;
char string[SCALED_STRLEN];
double value;
char **modifiers = scale->modifers;
uint64_t *scales = scale->scales;
/* don't scale if there is no reason to */
if (uint64in < scales[0] || maxmodifierin == NULL) {
if (flags & SCALED_PAD_WIDTH_FLAG)
width = widthin;
else
width = 0;
(void) snprintf(string, SCALED_STRLEN, "%%%dllu", width);
/* LINTED */
count = snprintf(scaledout, SCALED_STRLEN, string, uint64in);
if (unit && *unit != '\0')
(void) strcat(scaledout, unit);
if (widthout)
*widthout = count;
if (modifierout)
*modifierout = NULL;
return (0);
}
for (index = 0; modifiers[index + 1] != NULL; index++) {
if (uint64in >= scales[index] &&
uint64in < scales[index + 1])
break;
if ((strncmp(modifiers[index], maxmodifierin,
strlen(modifiers[index])) == 0) &&
(strlen(modifiers[index]) == strlen(maxmodifierin)))
break;
}
value = ((double)(uint64in)) / scales[index];
if (modifierout)
*modifierout = modifiers[index];
count = snprintf(string, SCALED_STRLEN, "%0.0lf", value);
while (count < widthin) {
decimals++;
(void) snprintf(string, SCALED_STRLEN, "%%0.%dlf", decimals);
/* LINTED */
count = snprintf(scaledout, SCALED_STRLEN, string, value);
/* reduce decimal places if we've overshot the desired width */
if (count > widthin) {
decimals--;
break;
}
}
if (flags & SCALED_PAD_WIDTH_FLAG)
width = widthin;
else
width = 0;
(void) snprintf(string, SCALED_STRLEN, "%%%d.%dlf", width, decimals);
/* LINTED */
count = snprintf(scaledout, SCALED_STRLEN, string, value);
(void) strcat(scaledout, modifiers[index]);
if (unit && *unit != '\0')
(void) strcat(scaledout, unit);
if (widthout)
*widthout = count;
return (0);
}
int
scaledtoscaled(char *scaledin, int widthin, char *maxmodifierin,
char *scaledout, int *widthout, char **modifierout,
scale_t *scale, char *unit, int flags) {
int ret;
uint64_t val;
ret = scaledtouint64(scaledin, &val, NULL, NULL, NULL,
scale, unit, flags);
if (ret)
return (ret);
ret = uint64toscaled(val, widthin, maxmodifierin,
scaledout, widthout, modifierout,
scale, unit, flags);
return (ret);
}
int
scaledeqscaled(char *scaled1, char *scaled2,
scale_t *scale, char *unit, int flags) {
int ret;
uint64_t uint64;
char *modifier1;
char *modifier2;
char *modifier = NULL;
int i;
int width;
int width1;
int width2;
char scaledA[SCALED_STRLEN];
char scaledB[SCALED_STRLEN];
char **modifiers = scale->modifers;
/*
* remove padding flag, so strings to compare will not have
* whitespace
*/
flags = flags & (~SCALED_PAD_WIDTH_FLAG);
/* determine each number's width and modifier */
ret = scaledtouint64(scaled1, &uint64, &width1, &modifier1, NULL,
scale, unit, flags);
if (ret)
return (0);
ret = scaledtouint64(scaled2, &uint64, &width2, &modifier2, NULL,
scale, unit, flags);
if (ret)
return (0);
/*
* determine the width and modifier to use for comparison.
* Use widest width and smallest modifier.
* Rescale to new width and modifier
*/
if (modifier1 == NULL || modifier2 == NULL)
modifier = NULL;
else {
for (i = 0; modifiers[i] != NULL; i++) {
if (strcmp(modifier1, modifiers[i]) == 0) {
modifier = modifiers[i];
break;
}
if (strcmp(modifier2, modifiers[i]) == 0) {
modifier = modifiers[i];
break;
}
}
}
width = 0;
if (width1 > width)
width = width1;
if (width2 > width)
width = width2;
/*
* Convert first number to width and modifier.
* This is done for the following reasons:
* 1. In case first number is hecadecimal. This will convert
* it to decimal
* 2. In case the first number has < the minimum number of
* columns.
* 3. The first number is missing an optional unit string.
* 4. Fix casing of modifier and unit.
*/
ret = scaledtoscaled(scaled1, width, modifier,
scaledA, NULL, NULL, scale, unit, flags);
if (ret)
return (0);
/* convert second number to width and modifier matching first number */
ret = scaledtoscaled(scaled2, width, modifier,
scaledB, NULL, NULL, scale, unit, flags);
if (ret)
return (0);
/* numbers are equal if strings match */
return ((strncmp(scaledA, scaledB, strlen(scaledA)) == 0) &&
(strlen(scaledA) == strlen(scaledB)));
}
int
scaledequint64(char *scaled, uint64_t uint64, int minwidth,
scale_t *scale, char *unit, int flags) {
int ret;
uint64_t tmpuint64;
char *modifier;
int width;
char scaledA[SCALED_STRLEN];
char scaledB[SCALED_STRLEN];
/* determine for number's width and modifier */
ret = scaledtouint64(scaled, &tmpuint64, &width, &modifier, NULL,
scale, unit, flags);
if (ret)
return (0);
if (width < minwidth)
width = minwidth;
/*
* Convert first number to width and modifier.
* This is done for the following reasons:
* 1. In case first number is hecadecimal. This will convert
* it to decimal
* 2. In case the first number has < the minimum number of
* columns.
* 3. The first number is missing an optional unit string.
* 4. Fix casing of modifier and unit.
*/
ret = scaledtoscaled(scaled, width, modifier,
scaledA, NULL, NULL, scale, unit, flags);
if (ret)
return (0);
/* convert second number to width and modifier matching first number */
ret = uint64toscaled(uint64, width, modifier,
scaledB, NULL, NULL, scale, unit, flags);
if (ret)
return (0);
/* numbers are equal if strings match */
return ((strncmp(scaledA, scaledB, strlen(scaledA)) == 0) &&
(strlen(scaledA) == strlen(scaledB)));
}