1 /* $NetBSD: t_strpct.c,v 1.2 2025/05/03 07:22:52 rillig Exp $ */
2 
3 /*
4  * Copyright (c) 2025 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code was contributed to The NetBSD Foundation by Roland Illig.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <sys/cdefs.h>
32 __COPYRIGHT("@(#) Copyright (c) 2025\
33  The NetBSD Foundation, inc. All rights reserved.");
34 __RCSID("$NetBSD: t_strpct.c,v 1.2 2025/05/03 07:22:52 rillig Exp $");
35 
36 #include <stdbool.h>
37 #include <stdint.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <util.h>
41 
42 #include <atf-c.h>
43 
44 static void
check_strspct(const char * file,unsigned line,size_t bufsiz,intmax_t num,intmax_t den,size_t digits,const char * want)45 check_strspct(const char *file, unsigned line,
46     size_t bufsiz, intmax_t num, intmax_t den, size_t digits,
47     const char *want)
48 {
49           char buf[128];
50 
51           ATF_REQUIRE_MSG(bufsiz < sizeof(buf) - 2, "bufsiz too large");
52           memset(buf, '>', sizeof(buf));
53           buf[0] = '<';
54           buf[sizeof(buf) - 1] = '\0';
55 
56           const char *have = strspct(buf + 1, bufsiz, num, den, digits);
57 
58           ATF_REQUIRE_MSG(buf[0] == '<',
59               "out-of-bounds write before");
60           ATF_REQUIRE_MSG(buf[1 + bufsiz] == '>',
61               "out-of-bounds write after");
62           ATF_REQUIRE_MSG(have == buf + 1,
63               "have != buf");
64           ATF_CHECK_MSG(bufsiz > 0 ? strcmp(have, want) == 0 : true,
65               "%s:%u: want \"%s\", have \"%s\"",
66               file, line, want, have);
67 }
68 
69 #define h_strspct(bufsiz, num, den, digits, want) \
70           check_strspct(__FILE__, __LINE__, bufsiz, num, den, digits, want)
71 
72 static void
check_strpct(const char * file,unsigned line,size_t bufsiz,uintmax_t num,uintmax_t den,size_t digits,const char * want)73 check_strpct(const char *file, unsigned line,
74     size_t bufsiz, uintmax_t num, uintmax_t den, size_t digits,
75     const char *want)
76 {
77           char buf[128];
78 
79           ATF_REQUIRE_MSG(bufsiz < sizeof(buf) - 2, "bufsiz too large");
80           memset(buf, '>', sizeof(buf));
81           buf[0] = '<';
82           buf[sizeof(buf) - 1] = '\0';
83 
84           const char *have = strpct(buf + 1, bufsiz, num, den, digits);
85 
86           ATF_REQUIRE_MSG(buf[0] == '<',
87               "out-of-bounds write before");
88           ATF_REQUIRE_MSG(buf[1 + bufsiz] == '>',
89               "out-of-bounds write after");
90           ATF_REQUIRE_MSG(have == buf + 1,
91               "have != buf");
92           ATF_CHECK_MSG(bufsiz > 0 ? strcmp(have, want) == 0 : true,
93               "%s:%u: want \"%s\", have \"%s\"",
94               file, line, want, have);
95 }
96 
97 #define h_strpct(bufsiz, num, den, digits, want) \
98           check_strpct(__FILE__, __LINE__, bufsiz, num, den, digits, want)
99 
100 ATF_TC(strspct);
ATF_TC_HEAD(strspct,tc)101 ATF_TC_HEAD(strspct, tc)
102 {
103           atf_tc_set_md_var(tc, "descr", "Checks strspct(3)");
104 }
ATF_TC_BODY(strspct,tc)105 ATF_TC_BODY(strspct, tc)
106 {
107 
108           // Very small buffers.
109           h_strspct(0, 0, 0, 0, "");
110           h_strspct(1, 0, 0, 0, "");
111 
112           // Small buffers.
113           h_strspct(2, 1, 40, 0, "2");
114           h_strspct(3, 1, 40, 0, "2");
115           h_strspct(3, 1, 40, 1, "2.");
116           h_strspct(4, 1, 40, 1, "2.5");
117           h_strspct(4, 8, 40, 1, "20.");
118           h_strspct(6, 1,  5, 1, "20.0");
119           h_strspct(100, 1, 5, 5, "20.00000");
120           h_strspct( 5, 11223344, 100, 10, "1122");
121           h_strspct(10, 11223344, 100, 10, "11223344.");
122           h_strspct(11, 11223344, 100, 10, "11223344.0");
123 
124           // Small buffers with negative numbers.
125           h_strspct(1, -1, 40, 0, "");
126           h_strspct(2, -1, 40, 0, "-");
127           h_strspct(3, -1, 40, 0, "-2");
128           h_strspct(3, -1, 40, 1, "-2");
129           h_strspct(4, -1, 40, 1, "-2.");
130           h_strspct(5, -1, 40, 1, "-2.5");
131           h_strspct(4, -8, 40, 1, "-20");
132           h_strspct(5, -8, 40, 1, "-20.");
133           h_strspct(6, -1,  5, 1, "-20.0");
134           h_strspct(100, -1, 5, 5, "-20.00000");
135           h_strspct( 5, -11223344, 100, 10, "-112");
136           h_strspct(10, -11223344, 100, 10, "-11223344");
137           h_strspct(11, -11223344, 100, 10, "-11223344.");
138           h_strspct(12, -11223344, 100, 10, "-11223344.0");
139 
140           // Percentages are always rounded towards zero.
141           h_strspct(6, 1, 6, 1, "16.6");
142           h_strspct(7, -1, 6, 1, "-16.6");
143           h_strspct(7, 1, -6, 1, "-16.6");
144           h_strspct(7, -1, -6, 1, "16.6");
145           h_strspct(100, 1, 7, 20, "14.28571428571428571428");
146 
147           // Big numbers.
148           h_strspct(100, INTMAX_MAX, INTMAX_MAX,  0, "100");
149           h_strspct(100, INTMAX_MIN, INTMAX_MIN, 25, "100.0000000000000000000000000");
150           h_strspct(100, INTMAX_MIN, INTMAX_MAX, 25, "-100.0000000000000000108420217");
151           h_strspct(100, INTMAX_MAX, INTMAX_MIN, 25, "-99.9999999999999999891579782");
152           h_strspct(100, INTMAX_MAX, INTMAX_MAX, 25, "100.0000000000000000000000000");
153 }
154 
155 ATF_TC(strpct);
ATF_TC_HEAD(strpct,tc)156 ATF_TC_HEAD(strpct, tc)
157 {
158           atf_tc_set_md_var(tc, "descr", "Checks strpct(3)");
159 }
ATF_TC_BODY(strpct,tc)160 ATF_TC_BODY(strpct, tc)
161 {
162 
163           // Small buffers.
164           h_strpct(0, 0, 0, 0, "");
165           h_strpct(1, 0, 0, 0, "");
166           h_strpct(2, 0, 0, 0, "0");
167           h_strpct(3, 0, 0, 0, "0");
168           h_strpct(3, 0, 0, 1, "0.");
169           h_strpct(4, 0, 0, 1, "0.0");
170           h_strpct(4, 1, 5, 1, "20.");
171           h_strpct(6, 1, 5, 1, "20.0");
172           h_strpct(100, 1, 5, 5, "20.00000");
173 
174           h_strpct(100, 1, 7, 20, "14.28571428571428571428");
175 
176           h_strpct( 5, 11223344, 100, 10, "1122");
177           h_strpct(10, 11223344, 100, 10, "11223344.");
178           h_strpct(11, 11223344, 100, 10, "11223344.0");
179 
180           h_strpct(100, UINTMAX_MAX, UINTMAX_MAX,  0, "100");
181           h_strpct(100, UINTMAX_MAX, UINTMAX_MAX,  1, "100.0");
182           h_strpct(100, UINTMAX_MAX, UINTMAX_MAX,  5, "100.00000");
183           h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 10, "100.0000000000");
184           h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 15, "100.000000000000000");
185           h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 20, "100.00000000000000000000");
186           h_strpct(100, UINTMAX_MAX, UINTMAX_MAX, 25, "100.0000000000000000000000000");
187 
188           h_strpct(100, UINTMAX_MAX - 1, UINTMAX_MAX, 25, "99.9999999999999999945789891");
189           h_strpct(100, 1, (UINTMAX_MAX >> 1) + 1, 70,
190               "0.0000000000000000108420217248550443400745280086994171142578125000000000");
191           h_strpct(100, UINTMAX_MAX, 1, 10, "1844674407370955161500.0000000000");
192           h_strpct(100, 1, UINTMAX_MAX, 30, "0.000000000000000005421010862427");
193 }
194 
ATF_TP_ADD_TCS(tp)195 ATF_TP_ADD_TCS(tp)
196 {
197 
198           ATF_TP_ADD_TC(tp, strspct);
199           ATF_TP_ADD_TC(tp, strpct);
200 
201           return atf_no_error();
202 }
203