xref: /netbsd-src/lib/libutil/strpct.c (revision 89cf253562e7a7ef571825450e7c5381f69edc6c)
1*89cf2535Schristos /* $NetBSD: strpct.c,v 1.3 2012/01/07 18:40:56 christos Exp $ */
2e7295d23Schristos 
3e7295d23Schristos /*-
4e7295d23Schristos  * Copyright (c) 1998 The NetBSD Foundation, Inc.
5e7295d23Schristos  * All rights reserved.
6e7295d23Schristos  *
7e7295d23Schristos  * This code is derived from software contributed to The NetBSD Foundation
8e7295d23Schristos  * by Erik E. Fair
9e7295d23Schristos  *
10e7295d23Schristos  * Redistribution and use in source and binary forms, with or without
11e7295d23Schristos  * modification, are permitted provided that the following conditions
12e7295d23Schristos  * are met:
13e7295d23Schristos  * 1. Redistributions of source code must retain the above copyright
14e7295d23Schristos  *    notice, this list of conditions and the following disclaimer.
15e7295d23Schristos  * 2. Redistributions in binary form must reproduce the above copyright
16e7295d23Schristos  *    notice, this list of conditions and the following disclaimer in the
17e7295d23Schristos  *    documentation and/or other materials provided with the distribution.
18e7295d23Schristos  *
19e7295d23Schristos  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20e7295d23Schristos  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21e7295d23Schristos  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22e7295d23Schristos  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23e7295d23Schristos  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24e7295d23Schristos  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25e7295d23Schristos  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26e7295d23Schristos  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27e7295d23Schristos  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28e7295d23Schristos  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29e7295d23Schristos  * POSSIBILITY OF SUCH DAMAGE.
30e7295d23Schristos  */
31e7295d23Schristos 
32e7295d23Schristos /*
33e7295d23Schristos  * Calculate a percentage without resorting to floating point
34e7295d23Schristos  * and return a pointer to a string
35e7295d23Schristos  *
36e7295d23Schristos  * "digits" is the number of digits past the decimal place you want
37e7295d23Schristos  * (zero being the straight percentage with no decimals)
38e7295d23Schristos  *
39e7295d23Schristos  * Erik E. Fair <fair@clock.org>, May 8, 1997
40e7295d23Schristos  */
41e7295d23Schristos 
42e7295d23Schristos #include <sys/cdefs.h>
43*89cf2535Schristos __RCSID("$NetBSD: strpct.c,v 1.3 2012/01/07 18:40:56 christos Exp $");
44e7295d23Schristos 
45e7295d23Schristos #include <stdint.h>
46e7295d23Schristos #include <locale.h>
47e7295d23Schristos #include <limits.h>
48e7295d23Schristos #include <stdio.h>
49e7295d23Schristos #include <errno.h>
50e7295d23Schristos #include <util.h>
51e7295d23Schristos 
52e7295d23Schristos char *
strspct(char * buf,size_t bufsiz,intmax_t numerator,intmax_t denominator,size_t digits)53*89cf2535Schristos strspct(char *buf, size_t bufsiz, intmax_t numerator, intmax_t denominator,
54*89cf2535Schristos     size_t digits)
55*89cf2535Schristos {
56*89cf2535Schristos 	int sign;
57*89cf2535Schristos 
58*89cf2535Schristos 	switch (bufsiz) {
59*89cf2535Schristos 	case 1:
60*89cf2535Schristos 		*buf = '\0';
61*89cf2535Schristos 		/*FALLTHROUGH*/
62*89cf2535Schristos 	case 0:
63*89cf2535Schristos 		return buf;
64*89cf2535Schristos 	default:
65*89cf2535Schristos 		break;
66*89cf2535Schristos 	}
67*89cf2535Schristos 
68*89cf2535Schristos 	if (denominator < 0) {
69*89cf2535Schristos 		denominator = -denominator;
70*89cf2535Schristos 		sign = 1;
71*89cf2535Schristos 	} else
72*89cf2535Schristos 		sign = 0;
73*89cf2535Schristos 
74*89cf2535Schristos 	if (numerator < 0) {
75*89cf2535Schristos 		numerator = -numerator;
76*89cf2535Schristos 		sign++;
77*89cf2535Schristos 	}
78*89cf2535Schristos 
79*89cf2535Schristos 	sign &= 1;
80*89cf2535Schristos 	(void)strpct(buf + sign, bufsiz - sign, (uintmax_t)numerator,
81*89cf2535Schristos 	    (uintmax_t)denominator, digits);
82*89cf2535Schristos 	if (sign)
83*89cf2535Schristos 		*buf = '-';
84*89cf2535Schristos 	return buf;
85*89cf2535Schristos }
86*89cf2535Schristos 
87*89cf2535Schristos char *
strpct(char * buf,size_t bufsiz,uintmax_t numerator,uintmax_t denominator,size_t digits)88e7295d23Schristos strpct(char *buf, size_t bufsiz, uintmax_t numerator, uintmax_t denominator,
89e7295d23Schristos     size_t digits)
90e7295d23Schristos {
91e7295d23Schristos 	uintmax_t factor, result;
92e7295d23Schristos 	size_t u;
93e7295d23Schristos 
94e7295d23Schristos 	factor = 100;
95e7295d23Schristos 	for (u = 0; u < digits; u++) {
96e7295d23Schristos 		/* watch out for overflow! */
97e7295d23Schristos 		if (factor < (UINTMAX_MAX / 10))
98e7295d23Schristos 			factor *= 10;
99e7295d23Schristos 		else
100e7295d23Schristos 			break;
101e7295d23Schristos 	}
102e7295d23Schristos 
103e7295d23Schristos 	/* watch out for overflow! */
104e7295d23Schristos 	if (numerator < (UINTMAX_MAX / factor))
105e7295d23Schristos 		numerator *= factor;
106e7295d23Schristos 	else {
107e7295d23Schristos 		/* toss some of the bits of lesser significance */
108e7295d23Schristos 		denominator /= factor;
109e7295d23Schristos 	}
110e7295d23Schristos 
111e7295d23Schristos 	if (denominator == 0)
112e7295d23Schristos 		denominator = 1;
113e7295d23Schristos 
114e7295d23Schristos 	result = numerator / denominator;
115e7295d23Schristos 
116e7295d23Schristos 	if (digits == 0)
117e7295d23Schristos 		(void)snprintf(buf, bufsiz, "%ju", result);
118e7295d23Schristos 	else {
119e7295d23Schristos 		factor /= 100;		/* undo initialization */
120e7295d23Schristos 
121352dad7eSchristos 		(void)snprintf(buf, bufsiz, "%ju%s%0*ju",
122e7295d23Schristos 		    result / factor, localeconv()->decimal_point, (int)u,
123e7295d23Schristos 		    result % factor);
124e7295d23Schristos 	}
125e7295d23Schristos 
126e7295d23Schristos 	return buf;
127e7295d23Schristos }
128