//*****************************************************************************
//
//! @file am_util_string.c
//!
//! @brief A subset of the functions provided in the C standard string library.
//!
//! The functions here are reimplementation of some of the standard "string"
//! functions.
//
//*****************************************************************************

//*****************************************************************************
//
// Copyright (c) 2019, Ambiq Micro
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// 
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 
// 3. Neither the name of the copyright holder nor the names of its
// contributors may be used to endorse or promote products derived from this
// software without specific prior written permission.
// 
// Third party software included in this distribution is subject to the
// additional license terms as defined in the /docs/licenses directory.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// This is part of revision v2.2.0-7-g63f7c2ba1 of the AmbiqSuite Development Package.
//
//*****************************************************************************

#include <stdint.h>
#include <stdbool.h>
#include "am_util_string.h"


//*****************************************************************************
//
//! @brief Table for quick lookup of character attributes.
//
//*****************************************************************************
#if MINIMIZE_CATTR_TABLE
#define CATTR_TBL_SIZE  128
#else
#define CATTR_TBL_SIZE  256
#endif

const uint8_t am_cattr[CATTR_TBL_SIZE] =
{
    AM_CATTR_NONE,                                                          /* 0x00 */
    AM_CATTR_NONE,                                                          /* 0x01 */
    AM_CATTR_NONE,                                                          /* 0x02 */
    AM_CATTR_NONE,                                                          /* 0x03 */
    AM_CATTR_NONE,                                                          /* 0x04 */
    AM_CATTR_NONE,                                                          /* 0x05 */
    AM_CATTR_NONE,                                                          /* 0x06 */
    AM_CATTR_NONE,                                                          /* 0x07 */
    AM_CATTR_NONE,                                                          /* 0x08 */
    AM_CATTR_WHSPACE,                                                       /* 0x09 */
    AM_CATTR_WHSPACE,                                                       /* 0x0A */
    AM_CATTR_WHSPACE,                                                       /* 0x0B */
    AM_CATTR_WHSPACE,                                                       /* 0x0C */
    AM_CATTR_WHSPACE,                                                       /* 0x0D */
    AM_CATTR_NONE,                                                          /* 0x0E */
    AM_CATTR_NONE,                                                          /* 0x0F */
    AM_CATTR_NONE,                                                          /* 0x00 */
    AM_CATTR_NONE,                                                          /* 0x11 */
    AM_CATTR_NONE,                                                          /* 0x12 */
    AM_CATTR_NONE,                                                          /* 0x13 */
    AM_CATTR_NONE,                                                          /* 0x14 */
    AM_CATTR_NONE,                                                          /* 0x15 */
    AM_CATTR_NONE,                                                          /* 0x16 */
    AM_CATTR_NONE,                                                          /* 0x17 */
    AM_CATTR_NONE,                                                          /* 0x18 */
    AM_CATTR_NONE,                                                          /* 0x19 */
    AM_CATTR_NONE,                                                          /* 0x1A */
    AM_CATTR_NONE,                                                          /* 0x1B */
    AM_CATTR_NONE,                                                          /* 0x1C */
    AM_CATTR_NONE,                                                          /* 0x1D */
    AM_CATTR_NONE,                                                          /* 0x1E */
    AM_CATTR_NONE,                                                          /* 0x1F */
    AM_CATTR_WHSPACE,                                                       /* 0x20, space */
    AM_CATTR_FILENM83,                                                      /* 0x21, ! */
    AM_CATTR_NONE,                                                          /* 0x22, " */
    AM_CATTR_FILENM83,                                                      /* 0x23, # */
    AM_CATTR_FILENM83,                                                      /* 0x24, $ */
    AM_CATTR_FILENM83,                                                      /* 0x25, % */
    AM_CATTR_FILENM83,                                                      /* 0x26, & */
    AM_CATTR_FILENM83,                                                      /* 0x27, ' */
    AM_CATTR_FILENM83,                                                      /* 0x28, ( */
    AM_CATTR_FILENM83,                                                      /* 0x29, ) */
    AM_CATTR_NONE,                                                          /* 0x2A, * */
    AM_CATTR_NONE,                                                          /* 0x2B, + */
    AM_CATTR_NONE,                                                          /* 0x2C, , */
    AM_CATTR_FILENM83,                                                      /* 0x2D, - */
    AM_CATTR_FILENM83,                                                      /* 0x2E, . */
    AM_CATTR_NONE,                                                          /* 0x2F, / */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x30, 0 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x31, 1 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x32, 2 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x33, 3 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x34, 4 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x35, 5 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x36, 6 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x37, 7 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x38, 8 */
    AM_CATTR_DIGIT | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,                   /* 0x39, 9 */
    AM_CATTR_NONE,                                                          /* 0x3A, : */
    AM_CATTR_NONE,                                                          /* 0x3B, ; */
    AM_CATTR_NONE,                                                          /* 0x3C, < */
    AM_CATTR_NONE,                                                          /* 0x3D, = */
    AM_CATTR_NONE,                                                          /* 0x3E, > */
    AM_CATTR_NONE,                                                          /* 0x3F, ? */
    AM_CATTR_FILENM83,                                                      /* 0x40, @ */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x41, A */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x42, B */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x43, C */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x44, D */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x45, E */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x46, F */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x47, G */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x48, H */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x49, I */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x4A, J */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x4B, K */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x4C, L */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x4D, M */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x4E, N */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x4F, O */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x50, P */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x51, Q */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x52, R */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x53, S */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x54, T */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x55, U */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x56, V */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x57, W */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x58, X */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x59, Y */
    AM_CATTR_ALPHA | AM_CATTR_UPPER | AM_CATTR_FILENM83,                    /* 0x5A, Z */
    AM_CATTR_NONE,                                                          /* 0x5B, [ */
    AM_CATTR_NONE,                                                          /* 0x5C, \ */
    AM_CATTR_NONE,                                                          /* 0x5D, ] */
    AM_CATTR_FILENM83,                                                      /* 0x5E, ^ */
    AM_CATTR_FILENM83,                                                      /* 0x5F, _ */
    AM_CATTR_FILENM83,                                                      /* 0x60, ` */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x61, a */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x62, b */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x63, c */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x64, d */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x65, e */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_XDIGIT | AM_CATTR_FILENM83,  /* 0x66, f */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x67, g */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x68, h */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x69, i */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x6A, j */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x6B, k */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x6C, l */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x6D, m */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x6E, n */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x6F, o */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x70, p */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x71, q */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x72, r */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x73, s */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x74, t */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x75, u */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x76, v */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x77, w */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x78, x */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x79, y */
    AM_CATTR_ALPHA | AM_CATTR_LOWER | AM_CATTR_FILENM83,                    /* 0x7A, z */
    AM_CATTR_FILENM83,                                                      /* 0x7B, { */
    AM_CATTR_NONE,                                                          /* 0x7C, | */
    AM_CATTR_FILENM83,                                                      /* 0x7D, } */
    AM_CATTR_FILENM83,                                                      /* 0x7E, ~ */
    AM_CATTR_NONE                                                           /* 0x7F, delete */

    //
    // All bit7 chars are AM_CATTR_NONE.
    //
};

//*****************************************************************************
//
//! @brief Character "is" functions
//!
//! This family of functions tests a given integer value in order to determine
//!  whether the integer satisfies the test condition.
//! These functions are generally based on the C99 standard functions.
//!
//! By default all of the "is" functions are implemented as macros. To implement
//!  as functions rather than macros, use a global compiler command line (-D)
//!  option to define AM_UTIL_STRING_CTYPE_DISABLE_MACROS.
//!
//! Standard functions currently implemented include:
//!  isalnum(), isalpha(), islower(), isupper(), isdigit(), isxdigit(),
//!  isspace().
//!
//! Standard functions not currently implemented include:
//!  iscntrl(), isgraph(), isprint(), ispunct(), isblank() (new for C99).
//!
//! Non-standard functions currently implemented include:
//!  isfilenm83().
//!
//! @return Each function returns a nonzero value if the integer satisfies
//!  the test condition and 0 if it does not.
//
//*****************************************************************************

#ifdef AM_UTIL_STRING_CTYPE_DISABLE_MACROS
int
am_util_string_isalnum(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & (AM_CATTR_ALPHA | AM_CATTR_DIGIT)) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & (AM_CATTR_ALPHA | AM_CATTR_DIGIT)) ? 1 : 0;
#endif
}

int
am_util_string_isalpha(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & AM_CATTR_ALPHA) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_ALPHA) ? 1 : 0;
#endif
}

int
am_util_string_isdigit(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & AM_CATTR_DIGIT) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_DIGIT) ? 1 : 0;
#endif
}

int am_util_string_islower(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & AM_CATTR_LOWER) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_LOWER) ? 1 : 0;
#endif
}

int
am_util_string_isspace(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & AM_CATTR_WHSPACE) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_WHSPACE) ? 1 : 0;
#endif
}

int
am_util_string_isupper(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & AM_CATTR_UPPER) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_UPPER) ? 1 : 0;
#endif
}

int
am_util_string_isxdigit(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & AM_CATTR_XDIGIT) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_XDIGIT) ? 1 : 0;
#endif
}

int am_util_string_tolower(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (am_cattr[c & 0x7f] & AM_CATTR_UPPER) ? c | 0x20 : c;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_UPPER) ? c | 0x20 : c;
#endif
}

int am_util_string_toupper(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (am_cattr[c & 0x7f] & AM_CATTR_LOWER) ? c & ~0x20 : c;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_LOWER) ? c & ~0x20 : c;
#endif
}


//
// Non-standard "is" Functions
//
int
am_util_string_isfilenm83(int c)
{
#if MINIMIZE_CATTR_TABLE
    return (c & 0xffffff80) ? 0 : (am_cattr[c] & AM_CATTR_FILENM83) ? 1 : 0;
#else
    return (am_cattr[c & 0xff] & AM_CATTR_FILENM83) ? 1 : 0;
#endif
}
#endif // AM_UTIL_STRING_CTYPE_DISABLE_MACROS


//*****************************************************************************
//
//! @brief Compare two strings.
//!
//! @param str1 is the first string to compare.
//! @param str2 is the second string to compare
//!
//! This function steps through a pair of strings, character by character, to
//! determine if the strings contain the same characters. If the strings match,
//! this function will return a zero. If str1 is alphabetically earlier than
//! str2, the return value will be negative. Otherwise, the return value will
//! be positive.
//!
//! @return 0 for a perfect match, negative value if str1<str2, positive value
//!  if str1>str2.
//
//*****************************************************************************
int32_t
am_util_string_strcmp(const char *str1, const char *str2)
{
    return am_util_string_strncmp(str1, str2, 0xffffffff);
}

//*****************************************************************************
//
//! @brief Compare two strings with a specified count.
//!
//! @param str1 is the first string to compare.
//! @param str2 is the second string to compare
//! @param num is the maximum number of characters to compare.
//!
//! This function steps through a pair of strings, character by character, to
//! determine if the strings contain the same characters. If the strings match,
//! this function will return a zero. If str1 is alphabetically earlier than
//! str2, the return value will be negative. Otherwise, the return value will
//! be positive.
//!
//! @return 0 for a perfect match, negative value if str1<str2, positive value
//!  if str1>str2.
//
//*****************************************************************************
int32_t
am_util_string_strncmp(const char *str1, const char *str2, uint32_t num)
{
    while ( num-- )
    {
        // Check for inequality OR end of string
        if ( *str1 != *str2 || *str1 == '\0' )
        {
            return *str1 - *str2;
        }

        str1++;
        str2++;
    }

    //
    // Since we made it here, the strings must be equal to n characters.
    //
    return 0;
}

//*****************************************************************************
//
//! @brief Compare two strings with a specified count and without regard to
//! letter case in the strings.
//!
//! @param str1 is the first string to compare.
//! @param str2 is the second string to compare
//! @param num is the maximum number of characters to compare.
//!
//! This function steps through a pair of strings, character by character, to
//! determine if the strings contain the same characters. If the strings match,
//! this function will return a zero. If str1 is alphabetically earlier than
//! str2, the return value will be negative. Otherwise, the return value will
//! be positive.
//!
//! @return 0 for a perfect match, negative value if str1<str2, positive value
//!  if str1>str2.
//
//*****************************************************************************
int32_t
am_util_string_strnicmp(const char *str1, const char *str2, int num)
{
    uint8_t cChar1, cChar2;

    while ( *str1 && *str2 && num )
    {
        cChar1 = *str1;
        cChar2 = *str2;

        cChar1 |= ( am_cattr[cChar1] & AM_CATTR_UPPER ) ? 0x20 : 0x00;
        cChar2 |= ( am_cattr[cChar2] & AM_CATTR_UPPER ) ? 0x20 : 0x00;

        if ( cChar1 != cChar2 )
        {
            return cChar1 - cChar2;
        }

        str1++;
        str2++;
        num--;
    }

    //
    // Since we made it here, the strings must be equal to n characters.
    //
    return 0;
}

//*****************************************************************************
//
//! @brief Compare two strings with case-insensitivity.
//!
//! @param str1 is the first string to compare.
//! @param str2 is the second string to compare
//!
//! This function compares each character in the 2 strings, converting all
//! alpha characters to lower-case to make the comparison.
//!
//! To illustrate a possible unexpected outcome due to comparing the strings
//! as lower case, consider the example strings "AMBIQ_MICRO" and "AMBIQMICRO".
//! For these strings, stricmp() will return a negative value (indicating the
//! first as before the second), whereas strcmp() will return a positive value.
//!
//! @return 0 for a case-insensitive match, negative value if str1<str2,
//!  positive value if str1>str2.
//
//*****************************************************************************
int32_t
am_util_string_stricmp(const char *str1, const char *str2)
{
    uint8_t cChar1, cChar2;

    while ( *str1 && *str2 )
    {
        cChar1 = *str1++;
        cChar2 = *str2++;

        cChar1 |= ( am_cattr[cChar1] & AM_CATTR_UPPER ) ? 0x20 : 0x00;
        cChar2 |= ( am_cattr[cChar2] & AM_CATTR_UPPER ) ? 0x20 : 0x00;

        if ( cChar1 != cChar2 )
        {
            return cChar1 - cChar2;
        }
    }

    return *str1 - *str2;
}

//*****************************************************************************
//
//! @brief Return the length of a string.
//!
//! @param pcStr pointer to the string.
//!
//! This function returns the length of the string at pcStr.
//!
//! @return length of the string pcStr.
//
//*****************************************************************************
uint32_t
am_util_string_strlen(const char *pcStr)
{
    const char *pcS;

    //
    // Loop through the string.
    //
    for (pcS = pcStr; *pcS; ++pcS);

    //
    // Return the length.
    //
    return(pcS - pcStr);
}

//*****************************************************************************
//
//! @brief Copies a string.
//!
//! @param pcDst pointer to the destination string.
//! @param pcSrc pointer to the source string to be copied to pcDst.
//!
//! This function copies pcSrc to the location specified by pcDst.
//!
//! @return pcDst (the location of the destination string).
//
//*****************************************************************************
char *
am_util_string_strcpy(char *pcDst, const char *pcSrc)
{
    char *pcRet = pcDst;

    //
    // Blindly copy the string until we hit a terminating NULL char.
    //
    do
    {
        *pcDst++ = *pcSrc;
    } while ( *pcSrc++ );

    return pcRet;
}

//*****************************************************************************
//
//! @brief Copies a specified number of characters of a string.
//!
//! @param pcDst pointer to the destination string.
//! @param pcSrc pointer to the source string to be copied to pcDst.
//!
//! This function copies uNum characters of pcSrc to the location specified
//!  by pcDst.
//! If uNum is less than the length of pcSrc, a NULL terminating character
//!  is not appended to the copied string. Thus the resultant string will be
//!  exactly uNum chars in length and not terminated.
//! If uNum is greater than the length of pcSrc, then pcDst is padded with
//!  NULL characters up to the uNum length.
//! Behavior is undefined if the addresses ranges overlap.
//!
//! @return pcDst (the location of the destination string).
//
//*****************************************************************************
char *
am_util_string_strncpy(char *pcDst, const char *pcSrc, uint32_t uNum)
{
    char *pcRet = pcDst;

    while (uNum > 0)
    {
        if ( *pcSrc )
        {
            *pcDst++ = *pcSrc++;
        }
        else
        {
            *pcDst++ = 0x00;
        }
        uNum--;
    }

    return pcRet;
}

//*****************************************************************************
//
//! @brief Concatenate a string.
//!
//! @param pcDst pointer to the destination string.
//! @param pcSrc pointer to the source string to be copied to pcDst.
//!
//! This function concatenates the string at pcSrc to the existing string
//! at pcDst.
//!
//! Both strings, pcDst and pcSrc, must be NULL-terminated.
//! No overflow checking is performed.
//! pcDst and pcSrc shall not overlap.
//!
//! @return pcDst (the location of the destination string).
//
//*****************************************************************************
char *
am_util_string_strcat(char *pcDst, const char *pcSrc)
{
    char *pcRet = pcDst;

    //
    // Find the end of the existing string.
    //
    while ( *pcDst++ );
    pcDst--;

    //
    // Now, copy the new string.
    //
    am_util_string_strcpy(pcDst, pcSrc);

    return pcRet;
}
