1 1.8 andvar /* $NetBSD: t_sprintf.c,v 1.8 2021/08/02 17:41:07 andvar Exp $ */ 2 1.1 perseant 3 1.1 perseant /*- 4 1.1 perseant * Copyright (c) 2017 The NetBSD Foundation, Inc. 5 1.1 perseant * All rights reserved. 6 1.1 perseant * 7 1.1 perseant * This code is derived from software contributed to The NetBSD Foundation 8 1.2 perseant * by Konrad Schroder. 9 1.1 perseant * 10 1.1 perseant * Redistribution and use in source and binary forms, with or without 11 1.1 perseant * modification, are permitted provided that the following conditions 12 1.1 perseant * are met: 13 1.1 perseant * 1. Redistributions of source code must retain the above copyright 14 1.1 perseant * notice, this list of conditions and the following disclaimer. 15 1.1 perseant * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 perseant * notice, this list of conditions and the following disclaimer in the 17 1.1 perseant * documentation and/or other materials provided with the distribution. 18 1.1 perseant * 19 1.1 perseant * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 perseant * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 perseant * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 perseant * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 perseant * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 perseant * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 perseant * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 perseant * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 perseant * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 perseant * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 perseant * POSSIBILITY OF SUCH DAMAGE. 30 1.1 perseant */ 31 1.1 perseant 32 1.1 perseant #include <sys/cdefs.h> 33 1.1 perseant __COPYRIGHT("@(#) Copyright (c) 2017\ 34 1.1 perseant The NetBSD Foundation, inc. All rights reserved."); 35 1.8 andvar __RCSID("$NetBSD: t_sprintf.c,v 1.8 2021/08/02 17:41:07 andvar Exp $"); 36 1.1 perseant 37 1.1 perseant #include <locale.h> 38 1.5 kre #include <math.h> 39 1.1 perseant #include <stdio.h> 40 1.1 perseant #include <stdlib.h> 41 1.1 perseant #include <string.h> 42 1.1 perseant #include <limits.h> 43 1.1 perseant #include <ctype.h> 44 1.1 perseant 45 1.1 perseant #include <atf-c.h> 46 1.1 perseant 47 1.1 perseant static struct test { 48 1.1 perseant const char *locale; 49 1.1 perseant const int int_value; 50 1.1 perseant const char *int_result; 51 1.1 perseant const char *int_input; 52 1.1 perseant const double double_value; 53 1.1 perseant const char *double_result; 54 1.1 perseant const char *double_input; 55 1.1 perseant } tests[] = { 56 1.1 perseant { 57 1.2 perseant "en_US.UTF-8", 58 1.1 perseant -12345, 59 1.1 perseant "-12,345", 60 1.1 perseant "-12345", 61 1.1 perseant -12345.6789, 62 1.1 perseant "-12,345.678900", 63 1.1 perseant "-12345.678900", 64 1.1 perseant }, { 65 1.2 perseant "fr_FR.ISO8859-1", 66 1.1 perseant -12345, 67 1.2 perseant "-12\240345", 68 1.1 perseant "-12345", 69 1.1 perseant -12345.6789, 70 1.2 perseant "-12\240345,678900", 71 1.2 perseant "-12345,678900", 72 1.1 perseant }, { 73 1.2 perseant "it_IT.ISO8859-1", 74 1.1 perseant -12345, 75 1.2 perseant "-12.345", 76 1.1 perseant "-12345", 77 1.1 perseant -12345.6789, 78 1.2 perseant "-12.345,678900", 79 1.1 perseant "-12345,678900", 80 1.1 perseant }, { 81 1.2 perseant "POSIX", 82 1.2 perseant /* 83 1.2 perseant * POSIX-1.2008 specifies that the C and POSIX 84 1.2 perseant * locales shall be identical (section 7.2) and 85 1.2 perseant * that the POSIX locale shall have an empty 86 1.2 perseant * thousands separator and "<period>" as its 87 1.2 perseant * decimal point (section 7.3.4). *printf 88 1.2 perseant * ought to honor these settings. 89 1.2 perseant */ 90 1.2 perseant -12345, 91 1.2 perseant "-12345", 92 1.2 perseant "-12345", 93 1.2 perseant -12345.6789, 94 1.2 perseant "-12345.678900", 95 1.2 perseant "-12345.678900", 96 1.2 perseant }, { 97 1.1 perseant NULL, 98 1.1 perseant 0, 99 1.1 perseant NULL, 100 1.1 perseant NULL, 101 1.1 perseant 0.0, 102 1.1 perseant NULL, 103 1.1 perseant NULL, 104 1.1 perseant } 105 1.1 perseant }; 106 1.1 perseant 107 1.1 perseant static void 108 1.1 perseant h_sprintf(const struct test *t) 109 1.1 perseant { 110 1.1 perseant char buf[1024]; 111 1.1 perseant 112 1.1 perseant ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); 113 1.1 perseant printf("Trying locale %s...\n", t->locale); 114 1.1 perseant ATF_REQUIRE(setlocale(LC_NUMERIC, t->locale) != NULL); 115 1.2 perseant printf("Using locale: %s\n", setlocale(LC_ALL, NULL)); 116 1.2 perseant 117 1.1 perseant sprintf(buf, "%'f", t->double_value); 118 1.1 perseant ATF_REQUIRE_STREQ(buf, t->double_result); 119 1.1 perseant 120 1.1 perseant sprintf(buf, "%'d", t->int_value); 121 1.1 perseant ATF_REQUIRE_STREQ(buf, t->int_result); 122 1.2 perseant 123 1.2 perseant atf_tc_expect_pass(); 124 1.1 perseant } 125 1.1 perseant 126 1.1 perseant static void 127 1.1 perseant h_strto(const struct test *t) 128 1.1 perseant { 129 1.5 kre double d, diff; 130 1.4 kre 131 1.1 perseant ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); 132 1.1 perseant printf("Trying locale %s...\n", t->locale); 133 1.1 perseant ATF_REQUIRE(setlocale(LC_NUMERIC, t->locale) != NULL); 134 1.1 perseant 135 1.1 perseant ATF_REQUIRE_EQ((int)strtol(t->int_input, NULL, 10), t->int_value); 136 1.7 kre 137 1.7 kre /* 138 1.7 kre * Note that the C standard permits function values to be 139 1.7 kre * returned with more precision than is expected by (floating) 140 1.7 kre * data types, and on i386 (and potentially other implementations) 141 1.7 kre * that is exactly what happens, meaning that the result from 142 1.7 kre * strtod() is not identical to the expected value - it turns out 143 1.7 kre * that it is the same if the value is constrained to the number 144 1.7 kre * of mantissa bits in a double (so the %a values printed below 145 1.7 kre * show the exact same bit patterns) and on i386 -ffloat-store 146 1.7 kre * will cause gcc to constrain the result that way, but nothing 147 1.7 kre * demands that be true, so instead, we simply test that the 148 1.7 kre * value returned is very very close to that expected. 149 1.7 kre * 150 1.7 kre * 1e-12 is chosen as the allowable delta, as we know (from 151 1.7 kre * the data in the "struct test" earlier in this file) that 152 1.7 kre * its magnitude is ~ 10^5, with values of that magnitude, 153 1.7 kre * 10^-12 difference is a 10^-17 relative difference, and 154 1.7 kre * with a 56 bit mantissa (standard IEEE "double") a difference 155 1.7 kre * that small vanishes (requires at least 57 mantissa bits to 156 1.7 kre * be representable). If the data values were to change, then 157 1.7 kre * so might this delta (if they were not all the same, we would 158 1.7 kre * move the delta into the struct rather than having it a constant 159 1.7 kre * here.). 160 1.7 kre * 161 1.7 kre * Finally, note that our purpose here is not to test floating 162 1.7 kre * point arithmetic, we're testing locale dependent string to 163 1.7 kre * binary conversions. 164 1.7 kre */ 165 1.7 kre 166 1.7 kre d = (double)strtod(t->double_input, NULL); 167 1.6 kre diff = fabs(d - t->double_value); 168 1.7 kre if (diff >= 1e-12) 169 1.7 kre ATF_REQUIRE_EQ_MSG(d, t->double_value, "In %s: " 170 1.7 kre "d=strtod(t->double_input[%s], NULL)[%.12g = %a] != " 171 1.7 kre "t->double_value[%.12g = %a]: diff=%g", t->locale, 172 1.7 kre t->double_input, d, d, t->double_value, t->double_value, 173 1.7 kre diff); 174 1.1 perseant } 175 1.1 perseant 176 1.1 perseant static void 177 1.1 perseant h_sscanf(const struct test *t) 178 1.1 perseant { 179 1.1 perseant int int_reported; 180 1.1 perseant double double_reported; 181 1.1 perseant 182 1.1 perseant ATF_REQUIRE_STREQ(setlocale(LC_ALL, "C"), "C"); 183 1.1 perseant printf("Trying locale %s...\n", t->locale); 184 1.1 perseant ATF_REQUIRE(setlocale(LC_NUMERIC, t->locale) != NULL); 185 1.1 perseant 186 1.1 perseant sscanf(t->int_input, "%d", &int_reported); 187 1.1 perseant ATF_REQUIRE_EQ(int_reported, t->int_value); 188 1.1 perseant sscanf(t->double_input, "%lf", &double_reported); 189 1.1 perseant ATF_REQUIRE_EQ(double_reported, t->double_value); 190 1.1 perseant } 191 1.1 perseant 192 1.1 perseant ATF_TC(sprintf); 193 1.1 perseant ATF_TC_HEAD(sprintf, tc) 194 1.1 perseant { 195 1.1 perseant atf_tc_set_md_var(tc, "descr", 196 1.8 andvar "Checks sprintf %%'d and %%'f under different locales"); 197 1.1 perseant } 198 1.1 perseant ATF_TC_BODY(sprintf, tc) 199 1.1 perseant { 200 1.1 perseant struct test *t; 201 1.1 perseant 202 1.1 perseant for (t = &tests[0]; t->locale != NULL; ++t) 203 1.1 perseant h_sprintf(t); 204 1.1 perseant } 205 1.1 perseant 206 1.1 perseant ATF_TC(strto); 207 1.1 perseant ATF_TC_HEAD(strto, tc) 208 1.1 perseant { 209 1.1 perseant atf_tc_set_md_var(tc, "descr", 210 1.8 andvar "Checks strtol and strtod under different locales"); 211 1.1 perseant } 212 1.1 perseant ATF_TC_BODY(strto, tc) 213 1.1 perseant { 214 1.1 perseant struct test *t; 215 1.1 perseant 216 1.1 perseant for (t = &tests[0]; t->locale != NULL; ++t) 217 1.1 perseant h_strto(t); 218 1.1 perseant } 219 1.1 perseant 220 1.1 perseant ATF_TC(sscanf); 221 1.1 perseant ATF_TC_HEAD(sscanf, tc) 222 1.1 perseant { 223 1.1 perseant atf_tc_set_md_var(tc, "descr", 224 1.8 andvar "Checks sscanf under different locales"); 225 1.1 perseant } 226 1.1 perseant ATF_TC_BODY(sscanf, tc) 227 1.1 perseant { 228 1.1 perseant struct test *t; 229 1.1 perseant 230 1.1 perseant for (t = &tests[0]; t->locale != NULL; ++t) 231 1.1 perseant h_sscanf(t); 232 1.1 perseant } 233 1.1 perseant 234 1.1 perseant ATF_TP_ADD_TCS(tp) 235 1.1 perseant { 236 1.1 perseant 237 1.1 perseant ATF_TP_ADD_TC(tp, sprintf); 238 1.1 perseant ATF_TP_ADD_TC(tp, sscanf); 239 1.1 perseant ATF_TP_ADD_TC(tp, strto); 240 1.1 perseant 241 1.1 perseant return atf_no_error(); 242 1.1 perseant } 243