elf: Add a way to check if tunable is set (BZ 27069)
[glibc.git] / stdio-common / grouping_iterator.c
blobfe845db37ea2db01fcbffa491cb841ebb064f5eb
1 /* Iterator for inserting thousands separators into numbers.
2 Copyright (C) 2022-2023 Free Software Foundation, Inc.
3 This file is part of the GNU C Library.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; if not, see
17 <https://www.gnu.org/licenses/>. */
19 #include <grouping_iterator.h>
21 #include <assert.h>
22 #include <limits.h>
23 #include <locale/localeinfo.h>
24 #include <stdint.h>
25 #include <string.h>
27 /* Initializes *IT with no grouping information for a string of length
28 DIGITS, and return false to indicate no grouping. */
29 bool
30 __grouping_iterator_init_none (struct grouping_iterator *it,
31 unsigned int digits)
33 memset (it, 0, sizeof (*it));
34 it->remaining_in_current_group = digits;
35 it->remaining = digits;
36 return false;
39 static bool
40 grouping_iterator_setup (struct grouping_iterator *it, unsigned int digits,
41 const char *grouping)
43 /* We treat all negative values like CHAR_MAX. */
45 if (*grouping == CHAR_MAX || *grouping <= 0)
46 /* No grouping should be done. */
47 return __grouping_iterator_init_none (it, digits);
49 unsigned int remaining_to_group = digits;
50 unsigned int non_repeating_groups = 0;
51 unsigned int groups = 0;
52 while (true)
54 non_repeating_groups += *grouping;
55 if (remaining_to_group <= (unsigned int) *grouping)
56 break;
58 ++groups;
59 remaining_to_group -= *grouping++;
61 if (*grouping == CHAR_MAX
62 #if CHAR_MIN < 0
63 || *grouping < 0
64 #endif
66 /* No more grouping should be done. */
67 break;
68 else if (*grouping == 0)
70 /* Same grouping repeats. */
71 --grouping;
72 non_repeating_groups -= *grouping; /* Over-counted. */
73 unsigned int repeats = (remaining_to_group - 1) / *grouping;
74 groups += repeats;
75 remaining_to_group -= repeats * *grouping;
76 break;
80 it->remaining_in_current_group = remaining_to_group;
81 it->remaining = digits;
82 it->groupings = grouping;
83 it->non_repeating_groups = non_repeating_groups;
84 it->separators = groups;
85 return it->separators > 0;
88 /* Returns the appropriate grouping item in LOC depending on CATEGORY
89 (which must be LC_MONETARY or LC_NUMERIC). */
90 static const char *
91 get_grouping (int category, locale_t loc)
93 return _nl_lookup (loc, category,
94 category == LC_MONETARY ? MON_GROUPING : GROUPING);
98 bool
99 __grouping_iterator_init (struct grouping_iterator *it,
100 int category, locale_t loc, unsigned int digits)
102 if (digits <= 1)
103 return __grouping_iterator_init_none (it, digits);
104 else
105 return grouping_iterator_setup (it, digits, get_grouping (category, loc));
108 bool
109 __grouping_iterator_next (struct grouping_iterator *it)
111 assert (it->remaining > 0);
112 --it->remaining;
114 if (it->remaining_in_current_group > 0)
116 --it->remaining_in_current_group;
117 return false;
120 /* If we are in the non-repeating part, switch group. */
121 if (it->remaining < it->non_repeating_groups)
122 --it->groupings;
124 it->remaining_in_current_group = *it->groupings - 1;
125 return true;