xref: /haiku/src/system/libroot/posix/glibc/stdlib/grouping.h (revision c90684742e7361651849be4116d0e5de3a817194)
1 /* Internal header for proving correct grouping in strings of numbers.
2    Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
5 
6    The GNU C Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Lesser General Public
8    License as published by the Free Software Foundation; either
9    version 2.1 of the License, or (at your option) any later version.
10 
11    The GNU C Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Lesser General Public License for more details.
15 
16    You should have received a copy of the GNU Lesser General Public
17    License along with the GNU C Library; if not, write to the Free
18    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19    02111-1307 USA.  */
20 
21 #include <limits.h>
22 
23 #ifndef MAX
24 #define MAX(a,b)	({ typeof(a) _a = (a); typeof(b) _b = (b); \
25 			   _a > _b ? _a : _b; })
26 #endif
27 
28 /* Find the maximum prefix of the string between BEGIN and END which
29    satisfies the grouping rules.  It is assumed that at least one digit
30    follows BEGIN directly.  */
31 
32 static inline const STRING_TYPE *
33 correctly_grouped_prefix (const STRING_TYPE *begin, const STRING_TYPE *end,
34 #ifdef USE_WIDE_CHAR
35 			  wchar_t thousands,
36 #else
37 			  const char *thousands,
38 #endif
39 			  const char *grouping)
40 {
41 #ifndef USE_WIDE_CHAR
42   size_t thousands_len;
43   int cnt;
44 #endif
45 
46   if (grouping == NULL)
47     return end;
48 
49 #ifndef USE_WIDE_CHAR
50   thousands_len = strlen (thousands);
51 #endif
52 
53   while (end > begin)
54     {
55       const STRING_TYPE *cp = end - 1;
56       const char *gp = grouping;
57 
58       /* Check first group.  */
59       while (cp >= begin)
60 	{
61 #ifdef USE_WIDE_CHAR
62 	  if (*cp == thousands)
63 	    break;
64 #else
65 	  if (cp[thousands_len - 1] == *thousands)
66 	    {
67 	      for (cnt = 1; thousands[cnt] != '\0'; ++cnt)
68 		if (thousands[cnt] != cp[thousands_len - 1 - cnt])
69 		  break;
70 	      if (thousands[cnt] == '\0')
71 		break;
72 	    }
73 #endif
74 	  --cp;
75 	}
76 
77       /* We allow the representation to contain no grouping at all even if
78 	 the locale specifies we can have grouping.  */
79       if (cp < begin)
80 	return end;
81 
82       if (end - cp == (int) *gp + 1)
83 	{
84 	  /* This group matches the specification.  */
85 
86 	  const STRING_TYPE *new_end;
87 
88 	  if (cp < begin)
89 	    /* There is just one complete group.  We are done.  */
90 	    return end;
91 
92 	  /* CP points to a thousands separator character.  The preceding
93 	     remainder of the string from BEGIN to NEW_END is the part we
94 	     will consider if there is a grouping error in this trailing
95 	     portion from CP to END.  */
96 	  new_end = cp - 1;
97 
98 	  /* Loop while the grouping is correct.  */
99 	  while (1)
100 	    {
101 	      /* Get the next grouping rule.  */
102 	      ++gp;
103 	      if (*gp == 0)
104 		/* If end is reached use last rule.  */
105 	        --gp;
106 
107 	      /* Skip the thousands separator.  */
108 	      --cp;
109 
110 	      if (*gp == CHAR_MAX
111 #if CHAR_MIN < 0
112 		  || *gp < 0
113 #endif
114 		  )
115 	        {
116 	          /* No more thousands separators are allowed to follow.  */
117 	          while (cp >= begin)
118 		    {
119 #ifdef USE_WIDE_CHAR
120 		      if (*cp == thousands)
121 			break;
122 #else
123 		      for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
124 			if (thousands[cnt] != cp[thousands_len - cnt - 1])
125 			  break;
126 		      if (thousands[cnt] == '\0')
127 			break;
128 #endif
129 		      --cp;
130 		    }
131 
132 	          if (cp < begin)
133 		    /* OK, only digits followed.  */
134 		    return end;
135 	        }
136 	      else
137 	        {
138 		  /* Check the next group.  */
139 	          const STRING_TYPE *group_end = cp;
140 
141 		  while (cp >= begin)
142 		    {
143 #ifdef USE_WIDE_CHAR
144 		      if (*cp == thousands)
145 			break;
146 #else
147 		      for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
148 			if (thousands[cnt] != cp[thousands_len - cnt - 1])
149 			  break;
150 		      if (thousands[cnt] == '\0')
151 			break;
152 #endif
153 		      --cp;
154 		    }
155 
156 		  if (cp < begin && group_end - cp <= (int) *gp)
157 		    /* Final group is correct.  */
158 		    return end;
159 
160 		  if (cp < begin || group_end - cp != (int) *gp)
161 		    /* Incorrect group.  Punt.  */
162 		    break;
163 		}
164 	    }
165 
166 	  /* The trailing portion of the string starting at NEW_END
167 	     contains a grouping error.  So we will look for a correctly
168 	     grouped number in the preceding portion instead.  */
169 	  end = new_end;
170 	}
171       else
172 	{
173 	  /* Even the first group was wrong; determine maximum shift.  */
174 	  if (end - cp > (int) *gp + 1)
175 	    end = cp + (int) *gp + 1;
176 	  else if (cp < begin)
177 	    /* This number does not fill the first group, but is correct.  */
178 	    return end;
179 	  else
180 	    /* CP points to a thousands separator character.  */
181 	    end = cp;
182 	}
183     }
184 
185   return MAX (begin, end);
186 }
187