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