2003-06-12 07:22:36 +08:00
|
|
|
/* Internal header for proving correct grouping in strings of numbers.
|
2023-01-07 05:08:04 +08:00
|
|
|
Copyright (C) 1995-2023 Free Software Foundation, Inc.
|
2003-06-12 07:22:36 +08:00
|
|
|
This file is part of the GNU C Library.
|
|
|
|
|
|
|
|
The GNU C Library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Lesser General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
The GNU C Library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Lesser General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2012-02-10 07:18:22 +08:00
|
|
|
License along with the GNU C Library; if not, see
|
Prefer https to http for gnu.org and fsf.org URLs
Also, change sources.redhat.com to sourceware.org.
This patch was automatically generated by running the following shell
script, which uses GNU sed, and which avoids modifying files imported
from upstream:
sed -ri '
s,(http|ftp)(://(.*\.)?(gnu|fsf|sourceware)\.org($|[^.]|\.[^a-z])),https\2,g
s,(http|ftp)(://(.*\.)?)sources\.redhat\.com($|[^.]|\.[^a-z]),https\2sourceware.org\4,g
' \
$(find $(git ls-files) -prune -type f \
! -name '*.po' \
! -name 'ChangeLog*' \
! -path COPYING ! -path COPYING.LIB \
! -path manual/fdl-1.3.texi ! -path manual/lgpl-2.1.texi \
! -path manual/texinfo.tex ! -path scripts/config.guess \
! -path scripts/config.sub ! -path scripts/install-sh \
! -path scripts/mkinstalldirs ! -path scripts/move-if-change \
! -path INSTALL ! -path locale/programs/charmap-kw.h \
! -path po/libc.pot ! -path sysdeps/gnu/errlist.c \
! '(' -name configure \
-execdir test -f configure.ac -o -f configure.in ';' ')' \
! '(' -name preconfigure \
-execdir test -f preconfigure.ac ';' ')' \
-print)
and then by running 'make dist-prepare' to regenerate files built
from the altered files, and then executing the following to cleanup:
chmod a+x sysdeps/unix/sysv/linux/riscv/configure
# Omit irrelevant whitespace and comment-only changes,
# perhaps from a slightly-different Autoconf version.
git checkout -f \
sysdeps/csky/configure \
sysdeps/hppa/configure \
sysdeps/riscv/configure \
sysdeps/unix/sysv/linux/csky/configure
# Omit changes that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/powerpc/powerpc64/ppc-mcount.S: trailing lines
git checkout -f \
sysdeps/powerpc/powerpc64/ppc-mcount.S \
sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
# Omit change that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S: last line does not end in newline
git checkout -f sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S
2019-09-07 13:40:42 +08:00
|
|
|
<https://www.gnu.org/licenses/>. */
|
2003-06-12 07:22:36 +08:00
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#ifndef MAX
|
|
|
|
#define MAX(a,b) ({ typeof(a) _a = (a); typeof(b) _b = (b); \
|
|
|
|
_a > _b ? _a : _b; })
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef USE_WIDE_CHAR
|
|
|
|
# include <wctype.h>
|
|
|
|
# define L_(Ch) L##Ch
|
|
|
|
# define UCHAR_TYPE wint_t
|
|
|
|
# define STRING_TYPE wchar_t
|
|
|
|
#else
|
|
|
|
# define L_(Ch) Ch
|
|
|
|
# define UCHAR_TYPE unsigned char
|
|
|
|
# define STRING_TYPE char
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "grouping.h"
|
|
|
|
|
|
|
|
/* Find the maximum prefix of the string between BEGIN and END which
|
|
|
|
satisfies the grouping rules. It is assumed that at least one digit
|
|
|
|
follows BEGIN directly. */
|
|
|
|
|
|
|
|
const STRING_TYPE *
|
|
|
|
#ifdef USE_WIDE_CHAR
|
|
|
|
__correctly_grouped_prefixwc (const STRING_TYPE *begin, const STRING_TYPE *end,
|
|
|
|
wchar_t thousands,
|
|
|
|
#else
|
|
|
|
__correctly_grouped_prefixmb (const STRING_TYPE *begin, const STRING_TYPE *end,
|
|
|
|
const char *thousands,
|
|
|
|
#endif
|
|
|
|
const char *grouping)
|
|
|
|
{
|
|
|
|
if (grouping == NULL)
|
|
|
|
return end;
|
|
|
|
|
2022-10-11 22:24:41 +08:00
|
|
|
#ifdef USE_WIDE_CHAR
|
|
|
|
size_t thousands_len = 1;
|
|
|
|
#else
|
|
|
|
size_t thousands_len = strlen (thousands);
|
|
|
|
int cnt;
|
2003-06-12 07:22:36 +08:00
|
|
|
#endif
|
|
|
|
|
2022-10-11 22:24:41 +08:00
|
|
|
while (end - begin >= thousands_len)
|
2003-06-12 07:22:36 +08:00
|
|
|
{
|
2022-10-11 22:24:41 +08:00
|
|
|
const STRING_TYPE *cp = end - thousands_len;
|
2003-06-12 07:22:36 +08:00
|
|
|
const char *gp = grouping;
|
|
|
|
|
|
|
|
/* Check first group. */
|
|
|
|
while (cp >= begin)
|
|
|
|
{
|
|
|
|
#ifdef USE_WIDE_CHAR
|
|
|
|
if (*cp == thousands)
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
if (cp[thousands_len - 1] == *thousands)
|
|
|
|
{
|
|
|
|
for (cnt = 1; thousands[cnt] != '\0'; ++cnt)
|
|
|
|
if (thousands[cnt] != cp[thousands_len - 1 - cnt])
|
|
|
|
break;
|
|
|
|
if (thousands[cnt] == '\0')
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
--cp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We allow the representation to contain no grouping at all even if
|
|
|
|
the locale specifies we can have grouping. */
|
|
|
|
if (cp < begin)
|
|
|
|
return end;
|
|
|
|
|
|
|
|
if (end - cp == (int) *gp + 1)
|
|
|
|
{
|
|
|
|
/* This group matches the specification. */
|
|
|
|
|
|
|
|
const STRING_TYPE *new_end;
|
|
|
|
|
|
|
|
if (cp < begin)
|
|
|
|
/* There is just one complete group. We are done. */
|
|
|
|
return end;
|
|
|
|
|
|
|
|
/* CP points to a thousands separator character. The preceding
|
|
|
|
remainder of the string from BEGIN to NEW_END is the part we
|
|
|
|
will consider if there is a grouping error in this trailing
|
|
|
|
portion from CP to END. */
|
|
|
|
new_end = cp - 1;
|
|
|
|
|
|
|
|
/* Loop while the grouping is correct. */
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
/* Get the next grouping rule. */
|
|
|
|
++gp;
|
|
|
|
if (*gp == 0)
|
|
|
|
/* If end is reached use last rule. */
|
|
|
|
--gp;
|
|
|
|
|
|
|
|
/* Skip the thousands separator. */
|
|
|
|
--cp;
|
|
|
|
|
|
|
|
if (*gp == CHAR_MAX
|
|
|
|
#if CHAR_MIN < 0
|
|
|
|
|| *gp < 0
|
|
|
|
#endif
|
|
|
|
)
|
|
|
|
{
|
|
|
|
/* No more thousands separators are allowed to follow. */
|
|
|
|
while (cp >= begin)
|
|
|
|
{
|
|
|
|
#ifdef USE_WIDE_CHAR
|
|
|
|
if (*cp == thousands)
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
|
|
|
|
if (thousands[cnt] != cp[thousands_len - cnt - 1])
|
|
|
|
break;
|
|
|
|
if (thousands[cnt] == '\0')
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
--cp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cp < begin)
|
|
|
|
/* OK, only digits followed. */
|
|
|
|
return end;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Check the next group. */
|
|
|
|
const STRING_TYPE *group_end = cp;
|
|
|
|
|
|
|
|
while (cp >= begin)
|
|
|
|
{
|
|
|
|
#ifdef USE_WIDE_CHAR
|
|
|
|
if (*cp == thousands)
|
|
|
|
break;
|
|
|
|
#else
|
|
|
|
for (cnt = 0; thousands[cnt] != '\0'; ++cnt)
|
|
|
|
if (thousands[cnt] != cp[thousands_len - cnt - 1])
|
|
|
|
break;
|
|
|
|
if (thousands[cnt] == '\0')
|
|
|
|
break;
|
|
|
|
#endif
|
|
|
|
--cp;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cp < begin && group_end - cp <= (int) *gp)
|
|
|
|
/* Final group is correct. */
|
|
|
|
return end;
|
|
|
|
|
|
|
|
if (cp < begin || group_end - cp != (int) *gp)
|
|
|
|
/* Incorrect group. Punt. */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The trailing portion of the string starting at NEW_END
|
|
|
|
contains a grouping error. So we will look for a correctly
|
|
|
|
grouped number in the preceding portion instead. */
|
|
|
|
end = new_end;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Even the first group was wrong; determine maximum shift. */
|
|
|
|
if (end - cp > (int) *gp + 1)
|
|
|
|
end = cp + (int) *gp + 1;
|
|
|
|
else if (cp < begin)
|
|
|
|
/* This number does not fill the first group, but is correct. */
|
|
|
|
return end;
|
|
|
|
else
|
|
|
|
/* CP points to a thousands separator character. */
|
|
|
|
end = cp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return MAX (begin, end);
|
|
|
|
}
|