]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[libc] Rewrite strtoul()
authorMichael Brown <mcb30@ipxe.org>
Thu, 19 Feb 2015 16:00:01 +0000 (16:00 +0000)
committerMichael Brown <mcb30@ipxe.org>
Thu, 19 Feb 2015 16:00:01 +0000 (16:00 +0000)
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>
src/core/base16.c
src/core/misc.c [deleted file]
src/core/string.c
src/core/strtoull.c [deleted file]
src/include/ipxe/string.h [new file with mode: 0644]
src/include/stdlib.h
src/tests/string_test.c

index bf9cc21bb24a815822acb298838d85fd9ba94b9f..d2977bde1ca7643ae4c106f5fa9c8a0bdcf7d525 100644 (file)
 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 (file)
index 84cfcd8..0000000
+++ /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:
- */
index db80d6120c4db3f193317bb55bb2e2493717532e..8000efcde35ec312fbf1740874c6f3166e018d88 100644 (file)
@@ -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 (file)
index 00986ee..0000000
+++ /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 (file)
index 0000000..954c7f1
--- /dev/null
@@ -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 */
index 2951522b8a4581eb78eaab135b901b03c7cab223..819b87b39b355c36c1934a8fe6d6673fb9ba11ae 100644 (file)
@@ -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 );
 
 /*****************************************************************************
  *
index e5669f4a730bfb3655ea3eb41a398faeea6b59de..541b7e78978d1c244fe76e9dc7e2c4b8e50c1cd5 100644 (file)
@@ -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 */