summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2015-02-19 17:00:01 +0100
committerMichael Brown2015-02-19 17:00:01 +0100
commita32b1e9e3557393d60fb4805cd74d8ba357b66cb (patch)
treeacd24bea0b4b0fc300d9b2d613dab573ca49b21f
parent[ipv4] Rewrite inet_aton() (diff)
downloadipxe-a32b1e9e3557393d60fb4805cd74d8ba357b66cb.tar.gz
ipxe-a32b1e9e3557393d60fb4805cd74d8ba357b66cb.tar.xz
ipxe-a32b1e9e3557393d60fb4805cd74d8ba357b66cb.zip
[libc] Rewrite strtoul()
The implementation of strtoul() has a partially unknown provenance. Rewrite this code to avoid potential licensing uncertainty. Since we now use -ffunction-sections, there is no need to place strtoull() in a separate file from strtoul(). Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/core/base16.c9
-rw-r--r--src/core/misc.c62
-rw-r--r--src/core/string.c129
-rw-r--r--src/core/strtoull.c60
-rw-r--r--src/include/ipxe/string.h14
-rw-r--r--src/include/stdlib.h28
-rw-r--r--src/tests/string_test.c48
7 files changed, 199 insertions, 151 deletions
diff --git a/src/core/base16.c b/src/core/base16.c
index bf9cc21b..d2977bde 100644
--- a/src/core/base16.c
+++ b/src/core/base16.c
@@ -20,9 +20,10 @@
FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
-#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
+#include <assert.h>
+#include <ipxe/string.h>
#include <ipxe/base16.h>
/** @file
@@ -87,13 +88,13 @@ int hex_decode ( const char *encoded, char separator, void *data, size_t len ) {
/* Extract digits. Note that either digit may be NUL,
* which would be interpreted as an invalid value by
- * strtoul_charval(); there is therefore no need for an
+ * digit_value(); there is therefore no need for an
* explicit end-of-string check.
*/
- sixteens = strtoul_charval ( *(encoded++) );
+ sixteens = digit_value ( *(encoded++) );
if ( sixteens >= 16 )
return -EINVAL;
- units = strtoul_charval ( *(encoded++) );
+ units = digit_value ( *(encoded++) );
if ( units >= 16 )
return -EINVAL;
diff --git a/src/core/misc.c b/src/core/misc.c
deleted file mode 100644
index 84cfcd80..00000000
--- a/src/core/misc.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/**************************************************************************
-MISC Support Routines
-**************************************************************************/
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdlib.h>
-#include <ctype.h>
-#include <byteswap.h>
-#include <ipxe/in.h>
-#include <ipxe/timer.h>
-
-unsigned int strtoul_charval ( unsigned int charval ) {
-
- if ( charval >= 'a' ) {
- charval = ( charval - 'a' + 10 );
- } else if ( charval >= 'A' ) {
- charval = ( charval - 'A' + 10 );
- } else if ( charval <= '9' ) {
- charval = ( charval - '0' );
- }
-
- return charval;
-}
-
-unsigned long strtoul ( const char *p, char **endp, int base ) {
- unsigned long ret = 0;
- int negative = 0;
- unsigned int charval;
-
- while ( isspace ( *p ) )
- p++;
-
- if ( *p == '-' ) {
- negative = 1;
- p++;
- }
-
- base = strtoul_base ( &p, base );
-
- while ( 1 ) {
- charval = strtoul_charval ( *p );
- if ( charval >= ( unsigned int ) base )
- break;
- ret = ( ( ret * base ) + charval );
- p++;
- }
-
- if ( negative )
- ret = -ret;
-
- if ( endp )
- *endp = ( char * ) p;
-
- return ( ret );
-}
-
-/*
- * Local variables:
- * c-basic-offset: 8
- * End:
- */
diff --git a/src/core/string.c b/src/core/string.c
index db80d612..8000efcd 100644
--- a/src/core/string.c
+++ b/src/core/string.c
@@ -366,3 +366,132 @@ char * strndup ( const char *src, size_t max ) {
}
return dup;
}
+
+/**
+ * Calculate digit value
+ *
+ * @v character Digit character
+ * @ret digit Digit value
+ *
+ * Invalid digits will be returned as a value greater than or equal to
+ * the numeric base.
+ */
+unsigned int digit_value ( unsigned int character ) {
+
+ if ( character >= 'a' )
+ return ( character - ( 'a' - 10 ) );
+ if ( character >= 'A' )
+ return ( character - ( 'A' - 10 ) );
+ if ( character <= '9' )
+ return ( character - '0' );
+ return character;
+}
+
+/**
+ * Preprocess string for strtoul() or strtoull()
+ *
+ * @v string String
+ * @v negate Final value should be negated
+ * @v base Numeric base
+ * @ret string Remaining string
+ */
+static const char * strtoul_pre ( const char *string, int *negate, int *base ) {
+
+ /* Skip any leading whitespace */
+ while ( isspace ( *string ) )
+ string++;
+
+ /* Process arithmetic sign, if present */
+ *negate = 0;
+ if ( *string == '-' ) {
+ string++;
+ *negate = 1;
+ } else if ( *string == '+' ) {
+ string++;
+ }
+
+ /* Process base, if present */
+ if ( *base == 0 ) {
+ *base = 10;
+ if ( *string == '0' ) {
+ string++;
+ *base = 8;
+ if ( ( *string & ~0x20 ) == 'X' ) {
+ string++;
+ *base = 16;
+ }
+ }
+ }
+
+ return string;
+}
+
+/**
+ * Convert string to numeric value
+ *
+ * @v string String
+ * @v endp End pointer (or NULL)
+ * @v base Numeric base (or zero to autodetect)
+ * @ret value Numeric value
+ */
+unsigned long strtoul ( const char *string, char **endp, int base ) {
+ unsigned long value = 0;
+ unsigned int digit;
+ int negate;
+
+ /* Preprocess string */
+ string = strtoul_pre ( string, &negate, &base );
+
+ /* Process digits */
+ for ( ; ; string++ ) {
+ digit = digit_value ( *string );
+ if ( digit >= ( unsigned int ) base )
+ break;
+ value = ( ( value * base ) + digit );
+ }
+
+ /* Negate value if, applicable */
+ if ( negate )
+ value = -value;
+
+ /* Fill in end pointer, if applicable */
+ if ( endp )
+ *endp = ( ( char * ) string );
+
+ return value;
+}
+
+/**
+ * Convert string to numeric value
+ *
+ * @v string String
+ * @v endp End pointer (or NULL)
+ * @v base Numeric base (or zero to autodetect)
+ * @ret value Numeric value
+ */
+unsigned long long strtoull ( const char *string, char **endp, int base ) {
+ unsigned long long value = 0;
+ unsigned int digit;
+ int negate;
+
+ /* Preprocess string */
+ string = strtoul_pre ( string, &negate, &base );
+
+ /* Process digits */
+ for ( ; ; string++ ) {
+ digit = digit_value ( *string );
+ if ( digit >= ( unsigned int ) base )
+ break;
+ value = ( ( value * base ) + digit );
+ }
+
+ /* Negate value if, applicable */
+ if ( negate )
+ value = -value;
+
+ /* Fill in end pointer, if applicable */
+ if ( endp )
+ *endp = ( ( char * ) string );
+
+ return value;
+}
diff --git a/src/core/strtoull.c b/src/core/strtoull.c
deleted file mode 100644
index 00986eef..00000000
--- a/src/core/strtoull.c
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>
- * Copyright (C) 2010 Piotr JaroszyƄski <p.jaroszynski@gmail.com>
- *
- * This program 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 2 of the
- * License, or any later version.
- *
- * This program 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 this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- */
-
-FILE_LICENCE ( GPL2_OR_LATER );
-
-#include <stdlib.h>
-#include <ctype.h>
-
-/*
- * Despite being exactly the same as strtoul() except the long long instead of
- * long it ends up being much bigger so provide a separate implementation in a
- * separate object so that it won't be linked in if not used.
- */
-unsigned long long strtoull ( const char *p, char **endp, int base ) {
- unsigned long long ret = 0;
- int negative = 0;
- unsigned int charval;
-
- while ( isspace ( *p ) )
- p++;
-
- if ( *p == '-' ) {
- negative = 1;
- p++;
- }
-
- base = strtoul_base ( &p, base );
-
- while ( 1 ) {
- charval = strtoul_charval ( *p );
- if ( charval >= ( unsigned int ) base )
- break;
- ret = ( ( ret * base ) + charval );
- p++;
- }
-
- if ( negative )
- ret = -ret;
-
- if ( endp )
- *endp = ( char * ) p;
-
- return ( ret );
-}
diff --git a/src/include/ipxe/string.h b/src/include/ipxe/string.h
new file mode 100644
index 00000000..954c7f16
--- /dev/null
+++ b/src/include/ipxe/string.h
@@ -0,0 +1,14 @@
+#ifndef _IPXE_STRING_H
+#define _IPXE_STRING_H
+
+/** @file
+ *
+ * String functions
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER );
+
+extern unsigned int digit_value ( unsigned int digit );
+
+#endif /* _IPXE_STRING_H */
diff --git a/src/include/stdlib.h b/src/include/stdlib.h
index 2951522b..819b87b3 100644
--- a/src/include/stdlib.h
+++ b/src/include/stdlib.h
@@ -13,31 +13,9 @@ FILE_LICENCE ( GPL2_OR_LATER );
****************************************************************************
*/
-static inline int strtoul_base ( const char **pp, int base )
-{
- const char *p = *pp;
-
- if ( base == 0 ) {
- base = 10;
- if ( *p == '0' ) {
- p++;
- base = 8;
- if ( ( *p | 0x20 ) == 'x' ) {
- p++;
- base = 16;
- }
- }
- }
-
- *pp = p;
-
- return base;
-}
-
-extern unsigned int strtoul_charval ( unsigned int charval );
-extern unsigned long strtoul ( const char *p, char **endp, int base );
-extern unsigned long long strtoull ( const char *p, char **endp, int base );
-
+extern unsigned long strtoul ( const char *string, char **endp, int base );
+extern unsigned long long strtoull ( const char *string, char **endp,
+ int base );
/*****************************************************************************
*
diff --git a/src/tests/string_test.c b/src/tests/string_test.c
index e5669f4a..541b7e78 100644
--- a/src/tests/string_test.c
+++ b/src/tests/string_test.c
@@ -31,8 +31,10 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <stdint.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
#include <strings.h>
+#include <ipxe/string.h>
#include <ipxe/test.h>
/**
@@ -242,6 +244,52 @@ static void string_test_exec ( void ) {
ok ( dest == buf );
ok ( strcmp ( buf, "append this" ) == 0 );
}
+
+ /* Test digit_value() */
+ {
+ unsigned int i;
+ char buf[2];
+ for ( i = 0 ; i < 16 ; i++ ) {
+ snprintf ( buf, sizeof ( buf ), "%x", i );
+ ok ( digit_value ( buf[0] ) == i );
+ snprintf ( buf, sizeof ( buf ), "%X", i );
+ ok ( digit_value ( buf[0] ) == i );
+ }
+ ok ( digit_value ( 0 ) >= 16 );
+ ok ( digit_value ( 9 ) >= 16 );
+ ok ( digit_value ( '0' - 1 ) >= 16 );
+ ok ( digit_value ( '9' + 1 ) >= 16 );
+ ok ( digit_value ( 'A' - 1 ) >= 16 );
+ ok ( digit_value ( 'F' + 1 ) >= 16 );
+ ok ( digit_value ( 'a' - 1 ) >= 16 );
+ ok ( digit_value ( 'f' + 1 ) >= 16 );
+ }
+
+ /* Test strtoul() */
+ ok ( strtoul ( "12345", NULL, 0 ) == 12345UL );
+ ok ( strtoul ( " 741", NULL, 10 ) == 741UL );
+ ok ( strtoul ( " 555a", NULL, 0 ) == 555UL );
+ ok ( strtoul ( " 555a", NULL, 16 ) == 0x555aUL );
+ ok ( strtoul ( "-12", NULL, 0 ) == -12UL );
+ ok ( strtoul ( "+3", NULL, 0 ) == 3UL );
+ ok ( strtoul ( "721", NULL, 0 ) == 721UL );
+ ok ( strtoul ( "721", NULL, 8 ) == 0721UL );
+ ok ( strtoul ( "0721", NULL, 0 ) == 0721UL );
+ ok ( strtoul ( "", NULL, 0 ) == 0UL );
+ ok ( strtoul ( "\t0xcAfe", NULL, 0 ) == 0xcafeUL );
+ ok ( strtoul ( "0xffffffff", NULL, 0 ) == 0xffffffffUL );
+ {
+ static const char string[] = "123aHa.world";
+ char *endp;
+ ok ( strtoul ( string, &endp, 0 ) == 123UL );
+ ok ( endp == &string[3] );
+ ok ( strtoul ( string, &endp, 16 ) == 0x123aUL );
+ ok ( endp == &string[4] );
+ ok ( strtoul ( string, &endp, 26 ) ==
+ ( ( ( ( ( 1 * 26 + 2 ) * 26 + 3 ) * 26 + 10 ) * 26
+ + 17 ) * 26 + 10 ) );
+ ok ( endp == &string[6] );
+ }
}
/** String self-test */