xref: /illumos-gate/usr/src/cmd/prctl/utils.c (revision 6a634c9d)
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 *
setpname(char * arg0)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
warn(const char * format,...)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
scaledtouint64(char * scaledin,uint64_t * uint64out,int * widthout,char ** modifierout,char ** unitout,scale_t * scale,char * unit,int flags)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
uint64toscaled(uint64_t uint64in,int widthin,char * maxmodifierin,char * scaledout,int * widthout,char ** modifierout,scale_t * scale,char * unit,int flags)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
scaledtoscaled(char * scaledin,int widthin,char * maxmodifierin,char * scaledout,int * widthout,char ** modifierout,scale_t * scale,char * unit,int flags)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
scaledeqscaled(char * scaled1,char * scaled2,scale_t * scale,char * unit,int flags)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
scaledequint64(char * scaled,uint64_t uint64,int minwidth,scale_t * scale,char * unit,int flags)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