david/ipxe
david
/
ipxe
Archived
1
0
Fork 0

[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:
Michael Brown 2015-02-19 16:00:01 +00:00
parent bb1abb2b21
commit a32b1e9e35
7 changed files with 199 additions and 151 deletions

View File

@ -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;

View File

@ -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:
*/

View 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;
}

View File

@ -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 );
}

14
src/include/ipxe/string.h Normal file
View File

@ -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 */

View 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 );
/*****************************************************************************
*

View 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 */