(root)/
glibc-2.38/
stdlib/
grouping.c
       1  /* Internal header for proving correct grouping in strings of numbers.
       2     Copyright (C) 1995-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       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.
       9  
      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.
      14  
      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/>.  */
      18  
      19  #include <limits.h>
      20  #include <stddef.h>
      21  #include <string.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  #ifdef USE_WIDE_CHAR
      29  # include <wctype.h>
      30  # define L_(Ch) L##Ch
      31  # define UCHAR_TYPE wint_t
      32  # define STRING_TYPE wchar_t
      33  #else
      34  # define L_(Ch) Ch
      35  # define UCHAR_TYPE unsigned char
      36  # define STRING_TYPE char
      37  #endif
      38  
      39  #include "grouping.h"
      40  
      41  /* Find the maximum prefix of the string between BEGIN and END which
      42     satisfies the grouping rules.  It is assumed that at least one digit
      43     follows BEGIN directly.  */
      44  
      45  const STRING_TYPE *
      46  #ifdef USE_WIDE_CHAR
      47  __correctly_grouped_prefixwc (const STRING_TYPE *begin, const STRING_TYPE *end,
      48  			      wchar_t thousands,
      49  #else
      50  __correctly_grouped_prefixmb (const STRING_TYPE *begin, const STRING_TYPE *end,
      51  			      const char *thousands,
      52  #endif
      53  			      const char *grouping)
      54  {
      55    if (grouping == NULL)
      56      return end;
      57  
      58  #ifdef USE_WIDE_CHAR
      59    size_t thousands_len = 1;
      60  #else
      61    size_t thousands_len = strlen (thousands);
      62    int cnt;
      63  #endif
      64  
      65    while (end - begin >= thousands_len)
      66      {
      67        const STRING_TYPE *cp = end - thousands_len;
      68        const char *gp = grouping;
      69  
      70        /* Check first group.  */
      71        while (cp >= begin)
      72  	{
      73  #ifdef USE_WIDE_CHAR
      74  	  if (*cp == thousands)
      75  	    break;
      76  #else
      77  	  if (cp[thousands_len - 1] == *thousands)
      78  	    {
      79  	      for (cnt = 1; thousands[cnt] != '\0'; ++cnt)
      80  		if (thousands[cnt] != cp[thousands_len - 1 - cnt])
      81  		  break;
      82  	      if (thousands[cnt] == '\0')
      83  		break;
      84  	    }
      85  #endif
      86  	  --cp;
      87  	}
      88  
      89        /* We allow the representation to contain no grouping at all even if
      90  	 the locale specifies we can have grouping.  */
      91        if (cp < begin)
      92  	return end;
      93  
      94        if (end - cp == (int) *gp + 1)
      95  	{
      96  	  /* This group matches the specification.  */
      97  
      98  	  const STRING_TYPE *new_end;
      99  
     100  	  if (cp < begin)
     101  	    /* There is just one complete group.  We are done.  */
     102  	    return end;
     103  
     104  	  /* CP points to a thousands separator character.  The preceding
     105  	     remainder of the string from BEGIN to NEW_END is the part we
     106  	     will consider if there is a grouping error in this trailing
     107  	     portion from CP to END.  */
     108  	  new_end = cp - 1;
     109  
     110  	  /* Loop while the grouping is correct.  */
     111  	  while (1)
     112  	    {
     113  	      /* Get the next grouping rule.  */
     114  	      ++gp;
     115  	      if (*gp == 0)
     116  		/* If end is reached use last rule.  */
     117  	        --gp;
     118  
     119  	      /* Skip the thousands separator.  */
     120  	      --cp;
     121  
     122  	      if (*gp == CHAR_MAX
     123  #if CHAR_MIN < 0
     124  		  || *gp < 0
     125  #endif
     126  		  )
     127  	        {
     128  	          /* No more thousands separators are allowed to follow.  */
     129  	          while (cp >= begin)
     130  		    {
     131  #ifdef USE_WIDE_CHAR
     132  		      if (*cp == thousands)
     133  			break;
     134  #else
     135  		      for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
     136  			if (thousands[cnt] != cp[thousands_len - cnt - 1])
     137  			  break;
     138  		      if (thousands[cnt] == '\0')
     139  			break;
     140  #endif
     141  		      --cp;
     142  		    }
     143  
     144  	          if (cp < begin)
     145  		    /* OK, only digits followed.  */
     146  		    return end;
     147  	        }
     148  	      else
     149  	        {
     150  		  /* Check the next group.  */
     151  	          const STRING_TYPE *group_end = cp;
     152  
     153  		  while (cp >= begin)
     154  		    {
     155  #ifdef USE_WIDE_CHAR
     156  		      if (*cp == thousands)
     157  			break;
     158  #else
     159  		      for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
     160  			if (thousands[cnt] != cp[thousands_len - cnt - 1])
     161  			  break;
     162  		      if (thousands[cnt] == '\0')
     163  			break;
     164  #endif
     165  		      --cp;
     166  		    }
     167  
     168  		  if (cp < begin && group_end - cp <= (int) *gp)
     169  		    /* Final group is correct.  */
     170  		    return end;
     171  
     172  		  if (cp < begin || group_end - cp != (int) *gp)
     173  		    /* Incorrect group.  Punt.  */
     174  		    break;
     175  		}
     176  	    }
     177  
     178  	  /* The trailing portion of the string starting at NEW_END
     179  	     contains a grouping error.  So we will look for a correctly
     180  	     grouped number in the preceding portion instead.  */
     181  	  end = new_end;
     182  	}
     183        else
     184  	{
     185  	  /* Even the first group was wrong; determine maximum shift.  */
     186  	  if (end - cp > (int) *gp + 1)
     187  	    end = cp + (int) *gp + 1;
     188  	  else if (cp < begin)
     189  	    /* This number does not fill the first group, but is correct.  */
     190  	    return end;
     191  	  else
     192  	    /* CP points to a thousands separator character.  */
     193  	    end = cp;
     194  	}
     195      }
     196  
     197    return MAX (begin, end);
     198  }