xref: /illumos-gate/usr/src/cmd/prctl/utils.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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