summaryrefslogtreecommitdiff
path: root/libc/stdlib/stdlib.c
diff options
context:
space:
mode:
authorManuel Novoa III <mjn3@codepoet.org>2002-03-13 23:28:57 +0000
committerManuel Novoa III <mjn3@codepoet.org>2002-03-13 23:28:57 +0000
commit29d3e23bab8e53ed8653aafb8af9d2999769f17f (patch)
treeac0e55fc8f2894d00686e2048df3b0fadf38980d /libc/stdlib/stdlib.c
parent347b1fc3b66b3fcffdc9da6f928b0e6caaf9de9d (diff)
New versions of the various string to int functions which are smaller
than the old ones, even with errno setting turned on now. Also, at least on i386, we no longer need the long long helper functions for division and mod from libgcc.a.
Diffstat (limited to 'libc/stdlib/stdlib.c')
-rw-r--r--libc/stdlib/stdlib.c448
1 files changed, 448 insertions, 0 deletions
diff --git a/libc/stdlib/stdlib.c b/libc/stdlib/stdlib.c
new file mode 100644
index 000000000..40286f0e5
--- /dev/null
+++ b/libc/stdlib/stdlib.c
@@ -0,0 +1,448 @@
+/* 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, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION!
+ *
+ * This code is currently under development. Also, I plan to port
+ * it to elks which is a 16-bit environment with a fairly limited
+ * compiler. Therefore, please refrain from modifying this code
+ * and, instead, pass any bug-fixes, etc. to me. Thanks. Manuel
+ *
+ * ATTENTION! ATTENTION! ATTENTION! ATTENTION! ATTENTION! */
+
+#define _ISOC99_SOURCE /* for ULLONG primarily... */
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <limits.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+
+extern unsigned long
+_stdlib_strto_l(register const char * __restrict str,
+ char ** __restrict endptr, int base, int sflag);
+extern unsigned long long
+_stdlib_strto_ll(register const char * __restrict str,
+ char ** __restrict endptr, int base, int sflag);
+
+/* TODO: gcc reports an error due to prototype conflicts. Don't include
+ * the header for the problem cases? */
+#define HEADER_ALIAS_PROBLEM
+
+/**********************************************************************/
+#ifdef L_abs
+
+#ifdef HEADER_ALIAS_PROBLEM
+/* #if UINT_MAX < ULONG_MAX */
+
+int abs(int j)
+{
+ return (j >= 0) ? j : -j;
+}
+
+#endif /* UINT_MAX < ULONG_MAX */
+
+#endif
+/**********************************************************************/
+#ifdef L_labs
+
+#ifndef HEADER_ALIAS_PROBLEM
+/* #if UINT_MAX == ULONG_MAX */
+strong_alias(labs,abs)
+#endif
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX)
+strong_alias(labs,llabs)
+#endif
+
+#if ULONG_MAX == UINTMAX_MAX
+strong_alias(labs,imaxabs)
+#endif
+
+long int labs(long int j)
+{
+ return (j >= 0) ? j : -j;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_llabs
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX)
+
+#if (ULLONG_MAX == UINTMAX_MAX)
+strong_alias(llabs,imaxabs)
+#endif
+
+long long int llabs(long long int j)
+{
+ return (j >= 0) ? j : -j;
+}
+
+#endif /* defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX) */
+
+#endif
+/**********************************************************************/
+#ifdef L_atoi
+
+#ifdef HEADER_ALIAS_PROBLEM
+/* #if UINT_MAX < ULONG_MAX */
+
+int atoi(const char *nptr)
+{
+ return (int) strtol(nptr, (char **) NULL, 10);
+}
+
+#endif /* UINT_MAX < ULONG_MAX */
+
+#endif
+/**********************************************************************/
+#ifdef L_atol
+
+#ifndef HEADER_ALIAS_PROBLEM
+/* #if UINT_MAX == ULONG_MAX */
+strong_alias(atol,atoi)
+#endif
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX)
+strong_alias(atol,atoll)
+#endif
+
+long atol(const char *nptr)
+{
+ return strtol(nptr, (char **) NULL, 10);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_atoll
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX)
+
+long long atoll(const char *nptr)
+{
+ return strtoll(nptr, (char **) NULL, 10);
+}
+
+#endif /* defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX) */
+
+#endif
+/**********************************************************************/
+#ifdef L_strtol
+
+#if ULONG_MAX == UINTMAX_MAX
+strong_alias(strtol,strtoimax)
+#endif
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX)
+strong_alias(strtol,strtoll)
+#endif
+
+long strtol(const char * __restrict str, char ** __restrict endptr, int base)
+{
+ return _stdlib_strto_l(str, endptr, base, 1);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_strtoll
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX)
+
+#if (ULLONG_MAX == UINTMAX_MAX)
+strong_alias(strtoll,strtoimax)
+#endif
+
+long long strtoll(const char * __restrict str,
+ char ** __restrict endptr, int base)
+{
+ return (long long) _stdlib_strto_ll(str, endptr, base, 1);
+}
+
+#endif /* defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX) */
+
+#endif
+/**********************************************************************/
+#ifdef L_strtoul
+
+#if ULONG_MAX == UINTMAX_MAX
+strong_alias(strtoul,strtoumax)
+#endif
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX == ULONG_MAX)
+strong_alias(strtoul,strtoull)
+#endif
+
+unsigned long strtoul(const char * __restrict str,
+ char ** __restrict endptr, int base)
+{
+ return _stdlib_strto_l(str, endptr, base, 0);
+}
+
+#endif
+/**********************************************************************/
+#ifdef L_strtoull
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX)
+
+#if (ULLONG_MAX == UINTMAX_MAX)
+strong_alias(strtoull,strtoumax)
+#endif
+
+unsigned long long strtoull(const char * __restrict str,
+ char ** __restrict endptr, int base)
+{
+ return _stdlib_strto_ll(str, endptr, base, 0);
+}
+
+#endif /* defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX) */
+
+#endif
+/**********************************************************************/
+/* Support routines follow */
+/**********************************************************************/
+/* Set if we want errno set appropriately. */
+/* NOTE: Implies _STRTO_ENDPTR below */
+#define _STRTO_ERRNO 1
+
+/* Set if we want support for the endptr arg. */
+/* Implied by _STRTO_ERRNO. */
+#define _STRTO_ENDPTR 1
+
+#if _STRTO_ERRNO
+#undef _STRTO_ENDPTR
+#define _STRTO_ENDPTR 1
+#define SET_ERRNO(X) __set_errno(X)
+#else
+#define SET_ERRNO(X) ((void)(X)) /* keep side effects */
+#endif
+
+/**********************************************************************/
+#ifdef L__stdlib_strto_l
+
+/* This is the main work fuction which handles both strtol (sflag = 1) and
+ * strtoul (sflag = 0). */
+
+unsigned long _stdlib_strto_l(register const char * __restrict str,
+ char ** __restrict 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;
+
+ assert((sflag == 0) || (sflag == 1));
+
+ 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. */
+ case '+': ++str;
+ }
+
+ if (!(base & ~0x10)) { /* Either dynamic (base = 0) or base 16. */
+ base += 10; /* Default is 10 (26). */
+ if (*str == '0') {
+ SET_FAIL(++str);
+ base -= 2; /* Now base is 8 or 16 (24). */
+ if ((0x20|(*str)) == 'x') { /* WARNING: assumes ascii. */
+ ++str;
+ base += base; /* Base is 16 (16 or 48). */
+ }
+ }
+
+ if (base > 16) { /* Adjust in case base wasn't dynamic. */
+ base = 16;
+ }
+ }
+
+ number = 0;
+
+ if (((unsigned)(base - 2)) < 35) { /* Legal base. */
+ cutoff_digit = ULONG_MAX % base;
+ cutoff = ULONG_MAX / base;
+ do {
+ digit = (((unsigned char)(*str - '0')) <= 9)
+ ? (*str - '0')
+ : ((*str >= 'A')
+ ? (((0x20|(*str)) - 'a' + 10)) /* WARNING: assumes ascii. */
+ : 40);
+
+ if (digit >= base) {
+ break;
+ }
+
+ SET_FAIL(++str);
+
+ if ((number > cutoff)
+ || ((number == cutoff) && (digit > cutoff_digit))) {
+ number = ULONG_MAX;
+ negative &= sflag;
+ SET_ERRNO(ERANGE);
+ } 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;
+ SET_ERRNO(ERANGE);
+ }
+ }
+
+ return negative ? (unsigned long)(-((long)number)) : number;
+}
+
+#endif
+/**********************************************************************/
+#ifdef L__stdlib_strto_ll
+
+#if defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX)
+
+/* This is the main work fuction which handles both strtoll (sflag = 1) and
+ * strtoull (sflag = 0). */
+
+unsigned long long _stdlib_strto_ll(register const char * __restrict str,
+ char ** __restrict endptr, int base,
+ int sflag)
+{
+ unsigned long long number;
+#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 int n1;
+ unsigned char negative, digit;
+
+ assert((sflag == 0) || (sflag == 1));
+
+ 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. */
+ case '+': ++str;
+ }
+
+ if (!(base & ~0x10)) { /* Either dynamic (base = 0) or base 16. */
+ base += 10; /* Default is 10 (26). */
+ if (*str == '0') {
+ SET_FAIL(++str);
+ base -= 2; /* Now base is 8 or 16 (24). */
+ if ((0x20|(*str)) == 'x') { /* WARNING: assumes ascii. */
+ ++str;
+ base += base; /* Base is 16 (16 or 48). */
+ }
+ }
+
+ if (base > 16) { /* Adjust in case base wasn't dynamic. */
+ base = 16;
+ }
+ }
+
+ number = 0;
+
+ if (((unsigned)(base - 2)) < 35) { /* Legal base. */
+ do {
+ digit = (((unsigned char)(*str - '0')) <= 9)
+ ? (*str - '0')
+ : ((*str >= 'A')
+ ? (((0x20|(*str)) - 'a' + 10)) /* WARNING: assumes ascii. */
+ : 40);
+
+ if (digit >= base) {
+ break;
+ }
+
+ SET_FAIL(++str);
+
+#if 1
+ /* Optional, but speeds things up in the usual case. */
+ if (number <= (ULLONG_MAX >> 6)) {
+ number = number * base + digit;
+ } else
+#endif
+ {
+ n1 = ((unsigned char) number) * base + digit;
+ number = (number >> CHAR_BIT) * base;
+
+ if (number + (n1 >> CHAR_BIT) <= (ULLONG_MAX >> CHAR_BIT)) {
+ number = (number << CHAR_BIT) + n1;
+ } else { /* Overflow. */
+ number = ULLONG_MAX;
+ negative &= sflag;
+ SET_ERRNO(ERANGE);
+ }
+ }
+
+ } while (1);
+ }
+
+#if _STRTO_ENDPTR
+ if (endptr) {
+ *endptr = (char *) fail_char;
+ }
+#endif
+
+ {
+ unsigned long long tmp = ((negative)
+ ? ((unsigned long long)(-(1+LLONG_MIN)))+1
+ : LLONG_MAX);
+ if (sflag && (number > tmp)) {
+ number = tmp;
+ SET_ERRNO(ERANGE);
+ }
+ }
+
+ return negative ? (unsigned long long)(-((long long)number)) : number;
+}
+
+#endif /* defined(ULLONG_MAX) && (ULLONG_MAX > ULONG_MAX) */
+
+#endif
+/**********************************************************************/