Reimplement strtol/strtoul/atoi (#5400)

newlibc version is pulling in (part of) locale support

Adapted from uClibc source code
Addded support for 0b.... prefix for binary numbers
This commit is contained in:
Petr Ledvina 2018-03-12 21:24:15 +01:00 committed by Michael Keller
parent 0a2e5a5878
commit 2abcaebd81
2 changed files with 132 additions and 0 deletions

View File

@ -15,6 +15,7 @@ COMMON_SRC = \
common/printf.c \
common/streambuf.c \
common/string_light.c \
common/strtol.c \
common/time.c \
common/typeconversion.c \
config/config_eeprom.c \

131
src/main/common/strtol.c Normal file
View File

@ -0,0 +1,131 @@
/* Copyright (C) 2002 Manuel Novoa III
* From my (incomplete) stdlib library for linux and (soon) elks.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, see
* <http://www.gnu.org/licenses/>.
* Adapted for Betaflight by Petr Ledvina, 2018
*/
#include <ctype.h>
#include <limits.h>
#include "common/utils.h"
#define _STRTO_ENDPTR 1
unsigned long _strto_l(const char * str, char ** endptr, int base, int sflag)
{
unsigned long number, cutoff;
#if _STRTO_ENDPTR
const char *fail_char;
#define SET_FAIL(X) fail_char = (X)
#else
#define SET_FAIL(X) ((void)(X)) /* Keep side effects. */
#endif
unsigned char negative, digit, cutoff_digit;
SET_FAIL(str);
while (isspace(*str)) { /* Skip leading whitespace. */
++str;
}
/* Handle optional sign. */
negative = 0;
switch (*str) {
case '-':
negative = 1; /* Fall through to increment str. */
FALLTHROUGH;
case '+':
++str;
}
if (!base || base == 16 || base == 2) { /* Either dynamic (base = 0) or base with 0[xb] prefix. */
if (*str == '0') {
SET_FAIL(++str);
if ((!base || base == 16) && tolower(*str) == 'x') {
++str;
base = 16;
} else if ((!base || base == 2) && tolower(*str) == 'b') {
++str;
base = 2;
} else if(!base) {
base = 8;
}
}
}
number = 0;
if (((unsigned)(base - 2)) < 35) { /* Legal base. */
cutoff_digit = ULONG_MAX % base;
cutoff = ULONG_MAX / base;
do {
digit = ( (*str - '0') <= 9)
? /* 0..9 */ (*str - '0')
: /* else */ (((0x20 | *str) >= 'a') /* WARNING: assumes ascii. */
? /* >= A/a */ ((0x20 | *str) - ('a' - 10))
: /* else */ 40 /* bad value */);
if (digit >= base) {
break;
}
SET_FAIL(++str);
if ((number > cutoff)
|| ((number == cutoff) && (digit > cutoff_digit))) {
number = ULONG_MAX;
negative &= sflag;
} else {
number = number * base + digit;
}
} while (1);
}
#if _STRTO_ENDPTR
if (endptr) {
*endptr = (char *) fail_char;
}
#endif
{
unsigned long tmp = (negative
? ((unsigned long)(-(1+LONG_MIN)))+1
: LONG_MAX);
if (sflag && (number > tmp)) {
number = tmp;
}
}
return negative ? (unsigned long)(-((long)number)) : number;
}
long strtol(const char * str, char ** endptr, int base)
{
return _strto_l(str, endptr, base, 1);
}
unsigned long strtoul(const char * str, char ** endptr, int base)
{
return _strto_l(str, endptr, base, 0);
}
int atoi(const char *str)
{
return strtol(str, NULL, 10);
}