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