Back to index

glibc  2.9
grouping.c
Go to the documentation of this file.
00001 /* Internal header for proving correct grouping in strings of numbers.
00002    Copyright (C) 1995,1996,1997,1998,2000,2003 Free Software Foundation, Inc.
00003    This file is part of the GNU C Library.
00004    Contributed by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
00005 
00006    The GNU C Library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Lesser General Public
00008    License as published by the Free Software Foundation; either
00009    version 2.1 of the License, or (at your option) any later version.
00010 
00011    The GNU C Library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Lesser General Public License for more details.
00015 
00016    You should have received a copy of the GNU Lesser General Public
00017    License along with the GNU C Library; if not, write to the Free
00018    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00019    02111-1307 USA.  */
00020 
00021 #include <limits.h>
00022 #include <stddef.h>
00023 #include <string.h>
00024 
00025 #ifndef MAX
00026 #define MAX(a,b)     ({ typeof(a) _a = (a); typeof(b) _b = (b); \
00027                         _a > _b ? _a : _b; })
00028 #endif
00029 
00030 #ifdef USE_WIDE_CHAR
00031 # include <wctype.h>
00032 # define L_(Ch) L##Ch
00033 # define UCHAR_TYPE wint_t
00034 # define STRING_TYPE wchar_t
00035 #else
00036 # define L_(Ch) Ch
00037 # define UCHAR_TYPE unsigned char
00038 # define STRING_TYPE char
00039 #endif
00040 
00041 #include "grouping.h"
00042 
00043 /* Find the maximum prefix of the string between BEGIN and END which
00044    satisfies the grouping rules.  It is assumed that at least one digit
00045    follows BEGIN directly.  */
00046 
00047 const STRING_TYPE *
00048 #ifdef USE_WIDE_CHAR
00049 __correctly_grouped_prefixwc (const STRING_TYPE *begin, const STRING_TYPE *end,
00050                            wchar_t thousands,
00051 #else
00052 __correctly_grouped_prefixmb (const STRING_TYPE *begin, const STRING_TYPE *end,
00053                            const char *thousands,
00054 #endif
00055                            const char *grouping)
00056 {
00057 #ifndef USE_WIDE_CHAR
00058   size_t thousands_len;
00059   int cnt;
00060 #endif
00061 
00062   if (grouping == NULL)
00063     return end;
00064 
00065 #ifndef USE_WIDE_CHAR
00066   thousands_len = strlen (thousands);
00067 #endif
00068 
00069   while (end > begin)
00070     {
00071       const STRING_TYPE *cp = end - 1;
00072       const char *gp = grouping;
00073 
00074       /* Check first group.  */
00075       while (cp >= begin)
00076        {
00077 #ifdef USE_WIDE_CHAR
00078          if (*cp == thousands)
00079            break;
00080 #else
00081          if (cp[thousands_len - 1] == *thousands)
00082            {
00083              for (cnt = 1; thousands[cnt] != '\0'; ++cnt)
00084               if (thousands[cnt] != cp[thousands_len - 1 - cnt])
00085                 break;
00086              if (thousands[cnt] == '\0')
00087               break;
00088            }
00089 #endif
00090          --cp;
00091        }
00092 
00093       /* We allow the representation to contain no grouping at all even if
00094         the locale specifies we can have grouping.  */
00095       if (cp < begin)
00096        return end;
00097 
00098       if (end - cp == (int) *gp + 1)
00099        {
00100          /* This group matches the specification.  */
00101 
00102          const STRING_TYPE *new_end;
00103 
00104          if (cp < begin)
00105            /* There is just one complete group.  We are done.  */
00106            return end;
00107 
00108          /* CP points to a thousands separator character.  The preceding
00109             remainder of the string from BEGIN to NEW_END is the part we
00110             will consider if there is a grouping error in this trailing
00111             portion from CP to END.  */
00112          new_end = cp - 1;
00113 
00114          /* Loop while the grouping is correct.  */
00115          while (1)
00116            {
00117              /* Get the next grouping rule.  */
00118              ++gp;
00119              if (*gp == 0)
00120               /* If end is reached use last rule.  */
00121                --gp;
00122 
00123              /* Skip the thousands separator.  */
00124              --cp;
00125 
00126              if (*gp == CHAR_MAX
00127 #if CHAR_MIN < 0
00128                 || *gp < 0
00129 #endif
00130                 )
00131                {
00132                  /* No more thousands separators are allowed to follow.  */
00133                  while (cp >= begin)
00134                   {
00135 #ifdef USE_WIDE_CHAR
00136                     if (*cp == thousands)
00137                      break;
00138 #else
00139                     for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
00140                      if (thousands[cnt] != cp[thousands_len - cnt - 1])
00141                        break;
00142                     if (thousands[cnt] == '\0')
00143                      break;
00144 #endif
00145                     --cp;
00146                   }
00147 
00148                  if (cp < begin)
00149                   /* OK, only digits followed.  */
00150                   return end;
00151                }
00152              else
00153                {
00154                 /* Check the next group.  */
00155                  const STRING_TYPE *group_end = cp;
00156 
00157                 while (cp >= begin)
00158                   {
00159 #ifdef USE_WIDE_CHAR
00160                     if (*cp == thousands)
00161                      break;
00162 #else
00163                     for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
00164                      if (thousands[cnt] != cp[thousands_len - cnt - 1])
00165                        break;
00166                     if (thousands[cnt] == '\0')
00167                      break;
00168 #endif
00169                     --cp;
00170                   }
00171 
00172                 if (cp < begin && group_end - cp <= (int) *gp)
00173                   /* Final group is correct.  */
00174                   return end;
00175 
00176                 if (cp < begin || group_end - cp != (int) *gp)
00177                   /* Incorrect group.  Punt.  */
00178                   break;
00179               }
00180            }
00181 
00182          /* The trailing portion of the string starting at NEW_END
00183             contains a grouping error.  So we will look for a correctly
00184             grouped number in the preceding portion instead.  */
00185          end = new_end;
00186        }
00187       else
00188        {
00189          /* Even the first group was wrong; determine maximum shift.  */
00190          if (end - cp > (int) *gp + 1)
00191            end = cp + (int) *gp + 1;
00192          else if (cp < begin)
00193            /* This number does not fill the first group, but is correct.  */
00194            return end;
00195          else
00196            /* CP points to a thousands separator character.  */
00197            end = cp;
00198        }
00199     }
00200 
00201   return MAX (begin, end);
00202 }