blob: 364c4cf0edf3e592c50f2f210d9d381cbe0a6dbe [file] [log] [blame]
/*
* 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;
}