| /* | |
| * 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 ); | |
| FILE_SECBOOT ( PERMITTED ); | |
| #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, or NUL terminator | |
| * | |
| * @v src String | |
| * @v character Character to find | |
| * @ret found Found character, or terminating NUL if not found | |
| */ | |
| char * strchrnul ( const char *src, int character ) { | |
| const uint8_t *src_bytes = ( ( const uint8_t * ) src ); | |
| for ( ; ; src_bytes++ ) { | |
| if ( ( *src_bytes == character ) || ( ! *src_bytes ) ) | |
| return ( ( char * ) src_bytes ); | |
| } | |
| } | |
| /** | |
| * 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 ) { | |
| char *found; | |
| found = strchrnul ( src, character ); | |
| if ( character != *( ( uint8_t * ) found ) ) | |
| return NULL; | |
| return found; | |
| } | |
| /** | |
| * 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; | |
| } | |
| /** | |
| * Find case-insensitive substring | |
| * | |
| * @v haystack String | |
| * @v needle Substring | |
| * @ret found Found substring, or NULL if not found | |
| */ | |
| char * strcasestr ( const char *haystack, const char *needle ) { | |
| size_t len = strlen ( needle ); | |
| for ( ; *haystack ; haystack++ ) { | |
| if ( strncasecmp ( 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; | |
| } |