(root)/
util-linux-2.39/
lib/
c_strtod.c
       1  /*
       2   * Locale-independent strtod().
       3   *
       4   * This file may be redistributed under the terms of the
       5   * GNU Lesser General Public License.
       6   *
       7   * Copyright (C) 2021 Karel Zak <kzak@redhat.com>
       8   */
       9  #include "c.h"
      10  
      11  #include <locale.h>
      12  #include <stdlib.h>
      13  #include <string.h>
      14  
      15  #include "c_strtod.h"
      16  
      17  #ifdef __APPLE__
      18  # include <xlocale.h>
      19  #endif
      20  
      21  #if defined(HAVE_NEWLOCALE) && (defined(HAVE_STRTOD_L) || defined(HAVE_USELOCALE))
      22  # define USE_CLOCALE
      23  #endif
      24  
      25  #if defined(USE_CLOCALE)
      26  static volatile locale_t c_locale;
      27  
      28  static locale_t get_c_locale(void)
      29  {
      30  	if (!c_locale)
      31  		c_locale = newlocale(LC_ALL_MASK, "C", (locale_t) 0);
      32  	return c_locale;
      33  }
      34  #endif
      35  
      36  
      37  double c_strtod(char const *str, char **end)
      38  {
      39  	double res;
      40  	int errsv;
      41  
      42  #if defined(USE_CLOCALE)
      43  	locale_t cl = get_c_locale();
      44  
      45  #if defined(HAVE_STRTOD_L)
      46  	/*
      47  	 * A) try strtod_l() for "C" locale
      48  	 */
      49  	if (cl)
      50  		return strtod_l(str, end, cl);
      51  #elif defined(HAVE_USELOCALE)
      52  	/*
      53  	 * B) classic strtod(), but switch to "C" locale by uselocal()
      54  	 */
      55  	if (cl) {
      56  		locale_t org_cl = uselocale(locale);
      57  		if (!org_cl)
      58  			return 0;
      59  
      60  		res = strtod(str, end);
      61  		errsv = errno;
      62  
      63  		uselocale(org_cl);
      64  		errno = errsv;
      65  		return res;
      66  	}
      67  #endif /* HAVE_USELOCALE */
      68  #endif /* USE_CLOCALE */
      69  	/*
      70  	 * C) classic strtod(), but switch to "C" locale by setlocale()
      71  	 */
      72  	char *org_locale = setlocale(LC_NUMERIC, NULL);
      73  
      74  	if (org_locale) {
      75  		org_locale = strdup(org_locale);
      76  		if (!org_locale)
      77  			return 0;
      78  
      79  		setlocale(LC_NUMERIC, "C");
      80  	}
      81  	res = strtod(str, end);
      82  	errsv = errno;
      83  
      84  	if (org_locale) {
      85  		setlocale(LC_NUMERIC, org_locale);
      86  		free(org_locale);
      87  	}
      88  	errno = errsv;
      89  	return res;
      90  }
      91  
      92  #ifdef TEST_PROGRAM
      93  int main(int argc, char *argv[])
      94  {
      95  	double res;
      96  	char *end;
      97  
      98  	if (argc < 2) {
      99  		fprintf(stderr, "usage: %s decimal.number\n",
     100  				program_invocation_short_name);
     101  		return EXIT_FAILURE;
     102  	}
     103  
     104  	res = c_strtod(argv[1], &end);
     105  	printf("Result: %g, errno: %d, endptr: '%s'\n", res, errno, end);
     106  
     107  	return errno ? EXIT_FAILURE : EXIT_SUCCESS;
     108  }
     109  #endif