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