mirror of https://github.com/ipxe/ipxe.git
545 lines
12 KiB
C
545 lines
12 KiB
C
/*
|
|
* Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
|
|
*
|
|
* 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 (at your option) 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 Street, Fifth Floor, Boston, MA
|
|
* 02110-1301, USA.
|
|
*
|
|
* You can also choose to distribute this program under the terms of
|
|
* the Unmodified Binary Distribution Licence (as given in the file
|
|
* COPYING.UBDL), provided that you have satisfied its requirements.
|
|
*/
|
|
|
|
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
#include <ctype.h>
|
|
|
|
/** @file
|
|
*
|
|
* String functions
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Fill memory region
|
|
*
|
|
* @v dest Destination region
|
|
* @v character Fill character
|
|
* @v len Length
|
|
* @ret dest Destination region
|
|
*/
|
|
void * generic_memset ( void *dest, int character, size_t len ) {
|
|
uint8_t *dest_bytes = dest;
|
|
|
|
while ( len-- )
|
|
*(dest_bytes++) = character;
|
|
return dest;
|
|
}
|
|
|
|
/**
|
|
* Copy memory region (forwards)
|
|
*
|
|
* @v dest Destination region
|
|
* @v src Source region
|
|
* @v len Length
|
|
* @ret dest Destination region
|
|
*/
|
|
void * generic_memcpy ( void *dest, const void *src, size_t len ) {
|
|
const uint8_t *src_bytes = src;
|
|
uint8_t *dest_bytes = dest;
|
|
|
|
while ( len-- )
|
|
*(dest_bytes++) = *(src_bytes++);
|
|
return dest;
|
|
}
|
|
|
|
/**
|
|
* Copy memory region (backwards)
|
|
*
|
|
* @v dest Destination region
|
|
* @v src Source region
|
|
* @v len Length
|
|
* @ret dest Destination region
|
|
*/
|
|
void * generic_memcpy_reverse ( void *dest, const void *src, size_t len ) {
|
|
const uint8_t *src_bytes = ( src + len );
|
|
uint8_t *dest_bytes = ( dest + len );
|
|
|
|
while ( len-- )
|
|
*(--dest_bytes) = *(--src_bytes);
|
|
return dest;
|
|
}
|
|
|
|
/**
|
|
* Copy (possibly overlapping) memory region
|
|
*
|
|
* @v dest Destination region
|
|
* @v src Source region
|
|
* @v len Length
|
|
* @ret dest Destination region
|
|
*/
|
|
void * generic_memmove ( void *dest, const void *src, size_t len ) {
|
|
|
|
if ( dest < src ) {
|
|
return generic_memcpy ( dest, src, len );
|
|
} else {
|
|
return generic_memcpy_reverse ( dest, src, len );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compare memory regions
|
|
*
|
|
* @v first First region
|
|
* @v second Second region
|
|
* @v len Length
|
|
* @ret diff Difference
|
|
*/
|
|
int memcmp ( const void *first, const void *second, size_t len ) {
|
|
const uint8_t *first_bytes = first;
|
|
const uint8_t *second_bytes = second;
|
|
int diff;
|
|
|
|
while ( len-- ) {
|
|
diff = ( *(first_bytes++) - *(second_bytes++) );
|
|
if ( diff )
|
|
return diff;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Find character within a memory region
|
|
*
|
|
* @v src Source region
|
|
* @v character Character to find
|
|
* @v len Length
|
|
* @ret found Found character, or NULL if not found
|
|
*/
|
|
void * memchr ( const void *src, int character, size_t len ) {
|
|
const uint8_t *src_bytes = src;
|
|
|
|
for ( ; len-- ; src_bytes++ ) {
|
|
if ( *src_bytes == character )
|
|
return ( ( void * ) src_bytes );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Swap memory regions
|
|
*
|
|
* @v first First region
|
|
* @v second Second region
|
|
* @v len Length
|
|
* @ret first First region
|
|
*/
|
|
void * memswap ( void *first, void *second, size_t len ) {
|
|
uint8_t *first_bytes = first;
|
|
uint8_t *second_bytes = second;
|
|
uint8_t temp;
|
|
|
|
for ( ; len-- ; first_bytes++, second_bytes++ ) {
|
|
temp = *first_bytes;
|
|
*first_bytes = *second_bytes;
|
|
*second_bytes = temp;
|
|
}
|
|
return first;
|
|
}
|
|
|
|
/**
|
|
* Compare strings
|
|
*
|
|
* @v first First string
|
|
* @v second Second string
|
|
* @ret diff Difference
|
|
*/
|
|
int strcmp ( const char *first, const char *second ) {
|
|
|
|
return strncmp ( first, second, ~( ( size_t ) 0 ) );
|
|
}
|
|
|
|
/**
|
|
* Compare strings
|
|
*
|
|
* @v first First string
|
|
* @v second Second string
|
|
* @v max Maximum length to compare
|
|
* @ret diff Difference
|
|
*/
|
|
int strncmp ( const char *first, const char *second, size_t max ) {
|
|
const uint8_t *first_bytes = ( ( const uint8_t * ) first );
|
|
const uint8_t *second_bytes = ( ( const uint8_t * ) second );
|
|
int diff;
|
|
|
|
for ( ; max-- ; first_bytes++, second_bytes++ ) {
|
|
diff = ( *first_bytes - *second_bytes );
|
|
if ( diff )
|
|
return diff;
|
|
if ( ! *first_bytes )
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Compare case-insensitive strings
|
|
*
|
|
* @v first First string
|
|
* @v second Second string
|
|
* @ret diff Difference
|
|
*/
|
|
int strcasecmp ( const char *first, const char *second ) {
|
|
|
|
return strncasecmp ( first, second, ~( ( size_t ) 0 ) );
|
|
}
|
|
|
|
/**
|
|
* Compare case-insensitive strings
|
|
*
|
|
* @v first First string
|
|
* @v second Second string
|
|
* @v max Maximum length to compare
|
|
* @ret diff Difference
|
|
*/
|
|
int strncasecmp ( const char *first, const char *second, size_t max ) {
|
|
const uint8_t *first_bytes = ( ( const uint8_t * ) first );
|
|
const uint8_t *second_bytes = ( ( const uint8_t * ) second );
|
|
int diff;
|
|
|
|
for ( ; max-- ; first_bytes++, second_bytes++ ) {
|
|
diff = ( toupper ( *first_bytes ) -
|
|
toupper ( *second_bytes ) );
|
|
if ( diff )
|
|
return diff;
|
|
if ( ! *first_bytes )
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Get length of string
|
|
*
|
|
* @v src String
|
|
* @ret len Length
|
|
*/
|
|
size_t strlen ( const char *src ) {
|
|
|
|
return strnlen ( src, ~( ( size_t ) 0 ) );
|
|
}
|
|
|
|
/**
|
|
* Get length of string
|
|
*
|
|
* @v src String
|
|
* @v max Maximum length
|
|
* @ret len Length
|
|
*/
|
|
size_t strnlen ( const char *src, size_t max ) {
|
|
const uint8_t *src_bytes = ( ( const uint8_t * ) src );
|
|
size_t len = 0;
|
|
|
|
while ( max-- && *(src_bytes++) )
|
|
len++;
|
|
return len;
|
|
}
|
|
|
|
/**
|
|
* Find character within a string
|
|
*
|
|
* @v src String
|
|
* @v character Character to find
|
|
* @ret found Found character, or NULL if not found
|
|
*/
|
|
char * strchr ( const char *src, int character ) {
|
|
const uint8_t *src_bytes = ( ( const uint8_t * ) src );
|
|
|
|
for ( ; ; src_bytes++ ) {
|
|
if ( *src_bytes == character )
|
|
return ( ( char * ) src_bytes );
|
|
if ( ! *src_bytes )
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Find rightmost character within a string
|
|
*
|
|
* @v src String
|
|
* @v character Character to find
|
|
* @ret found Found character, or NULL if not found
|
|
*/
|
|
char * strrchr ( const char *src, int character ) {
|
|
const uint8_t *src_bytes = ( ( const uint8_t * ) src );
|
|
const uint8_t *start = src_bytes;
|
|
|
|
while ( *src_bytes )
|
|
src_bytes++;
|
|
for ( src_bytes-- ; src_bytes >= start ; src_bytes-- ) {
|
|
if ( *src_bytes == character )
|
|
return ( ( char * ) src_bytes );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Find substring
|
|
*
|
|
* @v haystack String
|
|
* @v needle Substring
|
|
* @ret found Found substring, or NULL if not found
|
|
*/
|
|
char * strstr ( const char *haystack, const char *needle ) {
|
|
size_t len = strlen ( needle );
|
|
|
|
for ( ; *haystack ; haystack++ ) {
|
|
if ( memcmp ( haystack, needle, len ) == 0 )
|
|
return ( ( char * ) haystack );
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Copy string
|
|
*
|
|
* @v dest Destination string
|
|
* @v src Source string
|
|
* @ret dnul Terminating NUL of destination string
|
|
*/
|
|
char * stpcpy ( char *dest, const char *src ) {
|
|
const uint8_t *src_bytes = ( ( const uint8_t * ) src );
|
|
uint8_t *dest_bytes = ( ( uint8_t * ) dest );
|
|
|
|
/* We cannot use strncpy(), since that would pad the destination */
|
|
for ( ; ; src_bytes++, dest_bytes++ ) {
|
|
*dest_bytes = *src_bytes;
|
|
if ( ! *dest_bytes )
|
|
break;
|
|
}
|
|
return ( ( char * ) dest_bytes );
|
|
}
|
|
|
|
/**
|
|
* Copy string
|
|
*
|
|
* @v dest Destination string
|
|
* @v src Source string
|
|
* @ret dest Destination string
|
|
*/
|
|
char * strcpy ( char *dest, const char *src ) {
|
|
|
|
stpcpy ( dest, src );
|
|
return dest;
|
|
}
|
|
|
|
/**
|
|
* Copy string
|
|
*
|
|
* @v dest Destination string
|
|
* @v src Source string
|
|
* @v max Maximum length
|
|
* @ret dest Destination string
|
|
*/
|
|
char * strncpy ( char *dest, const char *src, size_t max ) {
|
|
const uint8_t *src_bytes = ( ( const uint8_t * ) src );
|
|
uint8_t *dest_bytes = ( ( uint8_t * ) dest );
|
|
|
|
for ( ; max ; max--, src_bytes++, dest_bytes++ ) {
|
|
*dest_bytes = *src_bytes;
|
|
if ( ! *dest_bytes )
|
|
break;
|
|
}
|
|
while ( max-- )
|
|
*(dest_bytes++) = '\0';
|
|
return dest;
|
|
}
|
|
|
|
/**
|
|
* Concatenate string
|
|
*
|
|
* @v dest Destination string
|
|
* @v src Source string
|
|
* @ret dest Destination string
|
|
*/
|
|
char * strcat ( char *dest, const char *src ) {
|
|
|
|
strcpy ( ( dest + strlen ( dest ) ), src );
|
|
return dest;
|
|
}
|
|
|
|
/**
|
|
* Duplicate string
|
|
*
|
|
* @v src Source string
|
|
* @ret dup Duplicated string, or NULL if allocation failed
|
|
*/
|
|
char * strdup ( const char *src ) {
|
|
|
|
return strndup ( src, ~( ( size_t ) 0 ) );
|
|
}
|
|
|
|
/**
|
|
* Duplicate string
|
|
*
|
|
* @v src Source string
|
|
* @v max Maximum length
|
|
* @ret dup Duplicated string, or NULL if allocation failed
|
|
*/
|
|
char * strndup ( const char *src, size_t max ) {
|
|
size_t len = strnlen ( src, max );
|
|
char *dup;
|
|
|
|
dup = malloc ( len + 1 /* NUL */ );
|
|
if ( dup ) {
|
|
memcpy ( dup, src, len );
|
|
dup[len] = '\0';
|
|
}
|
|
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;
|
|
}
|