//***************************************************************************** // //! @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) 2020, 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 2.4.2 of the AmbiqSuite Development Package. // //***************************************************************************** #include #include #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 str1str2. // //***************************************************************************** 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 str1str2. // //***************************************************************************** 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 str1str2. // //***************************************************************************** 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 str1str2. // //***************************************************************************** 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; }