os-k/kaleid/libc/strtol.c

152 lines
4.5 KiB
C

//----------------------------------------------------------------------------//
// GNU GPL OS/K //
// //
// Desc: strto*l() family //
// //
// //
// Copyright © 2018-2019 The OS/K Team //
// //
// This file is part of OS/K. //
// //
// OS/K is free software: you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation, either version 3 of the License, or //
// any later version. //
// //
// OS/K 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 General Public License for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with OS/K. If not, see <https://www.gnu.org/licenses/>. //
//----------------------------------------------------------------------------//
#include <libc.h>
long strtol(const char *str, char **endp, int base) {
char c;
ulong n;
bool neg = 0;
const char *save, *start = str;
// Ignore leading spaces
do {
c = *str++;
} while (isspace(c));
// Accept any +/-'s, whatever the base
// In particular we accept things like "-0xF"
if (c == '+')
c = *str++;
else if (c == '-') {
c = *str++;
neg = 1;
}
// Accept 0/0b/0B/0x/0X and guess base if needed
if (c == '0') {
c = *str++;
if ((base == 0 || base == 16) && (c == 'x' || c == 'X')) {
c = *str++;
base = 16;
}
else if ((base == 0 || base == 2) && (c == 'b' || c == 'B')) {
c = *str++;
base = 2;
}
else if (base == 0)
base = 8;
}
// base==0 but no leading '0' - assume it's decimal
if (base == 0)
base = 10;
//
// Extract the number from the string
// We do not check whether the number actually fits
// That's why our accumulator 'n' needs to be unsigned, otherwise
// containing a number too large would cause a signed overflow and UB
//
save = str;
for (n = 0;; c = *str) {
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
else {
str++;
n = (n * base) + c;
}
}
// Save where we stopped in *endp (optional)
// If 'str' is still '== save', no digit were consummed
if (endp != NULL)
*endp = (char *)(str == save ? start : str);
return neg ? -(long)n : (long)n;
}
//
// Made from the code above by removing "neg"
//
ulong strtoul(const char *str, char **endp, int base) {
char c;
ulong n;
const char *save, *start = str;
do {
c = *str++;
} while (isspace(c));
if (c == '0') {
c = *str++;
if ((base == 0 || base == 16) && (c == 'x' || c == 'X')) {
c = *str++;
base = 16;
}
else if ((base == 0 || base == 2) && (c == 'b' || c == 'B')) {
c = *str++;
base = 2;
}
else if (base == 0)
base = 8;
}
if (base == 0)
base = 10;
save = str;
for (n = 0;; c = *str) {
if (isdigit(c))
c -= '0';
else if (isalpha(c))
c -= isupper(c) ? 'A' - 10 : 'a' - 10;
else
break;
if (c >= base)
break;
else {
str++;
n = (n * base) + c;
}
}
if (endp != NULL)
*endp = (char *)(str == save ? start : str);
return n;
}