[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>
This commit is contained in:
parent
bb1abb2b21
commit
a32b1e9e35
|
@ -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;
|
||||
|
||||
|
|
|
@ -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:
|
||||
*/
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
|
@ -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 */
|
|
@ -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 );
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
|
|
@ -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 */
|
||||
|
|
Reference in New Issue