String stuff that should be in stdlib but isn't.

Dependents:   X10Svr SSDP_Server

SW_String.cpp

Committer:
WiredHome
Date:
2019-02-27
Revision:
3:bc30d348f5b4
Parent:
2:c7a3039893cb

File content as of revision 3:bc30d348f5b4:


#include "SW_String.h"

#define DEBUG "SWst"
#include <cstdio>
#if (defined(DEBUG) && !defined(TARGET_LPC11U24))
#define DBG(x, ...)  std::printf("[DBG %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#define ERR(x, ...)  std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
#else
#define DBG(x, ...)
#define WARN(x, ...)
#define ERR(x, ...)
#define INFO(x, ...)
#endif

/// A more secure version of strcat
///
/// This function is like a wrapper on strcat, to first validate the concatination
/// and then if all parameters appear good, it will call strcat. It will not
/// permit overlapping source and destination.
///
/// If there is an error, no concatination is performed.
///
/// @note This has a different return value than the normal strcat.
///
/// @param[out] dst is a pointer to the start of the destination buffer (not necessarily
///             where the next string will appear).
/// @param[in] dstSize defines the size of the destination buffer.
/// @param[in] src is a pointer to the source.
///
/// @returns 
///     - 0 = no error
///     - -1 = destination pointer invalid
///     - -2 = source is too big to append into the destination
///     - -3 = overlap between src and dst
///
int strcat_s(char * dst, size_t dstSize, const char * src) {
    if (dst == NULL) {
        ERR("strcat_s FAIL destination == NULL");
        return -1;
    }
    if (src == NULL || *src == '\0')
        return 0;       // done, that was easy.
    //            [Source .... 
    //  [Destination  ... ]
    if (src >= dst && src < (dst + dstSize)) {
        ERR("strcat_s FAIL source overlaps destination");
        return -3;
    }
    int dstLen = strlen(dst);
    int srcLen = strlen(src);
    //          [Source ..... +srcLen]
    //  [Destination                  + dstSize]
    if (src + srcLen >= dst && src + srcLen <= dst + dstSize) {
        ERR("strcat_s FAIL source + length overlaps destination");
        return -3;
    }
    //                                   [Source length > freespace]
    //  [Destination   ... \0[freespace]]
    if (dstLen + srcLen > dstSize) {
        ERR("strcat_s FAIL dstLen + srcLen > size");
        return -2;
    }
    strcat(dst, src);
    return 0;
}


/// A more secure version of strcpy
///
/// This function is like a wrapper on strcpy, to first validate the concatination
/// and then if all parameters appear good, it will call strcpy. It will not
/// permit overlapping source and destination.
///
/// If there is an error, no copy is performed.
///
/// @note This has a different return value than the normal strcpy.
///
/// @param[out] dst is a pointer to the start of the destination buffer.
/// @param[in] dstSize defines the size of the destination buffer.
/// @param[in] src is a pointer to the source.
///
/// @returns 
///     - 0 = no error
///     - -1 = destination pointer invalid
///     - -2 = source is too big to append into the destination
///     - -3 = overlap between src and dst
///
int strcpy_s(char * dst, size_t dstSize, const char * src) {
    if (dst == NULL) {
        ERR("strcpy_s FAIL destination == NULL");
        return -1;
    }
    if (src == NULL || *src == '\0') {
        *dst = '\0';
        return 0;       // done, that was easy.
    }
    if (src >= dst && src < dst + dstSize) {
        ERR("strcpy_s FAIL source overlaps destination");
        return -3;
    }
    int srcLen = strlen(src);
    if (src + srcLen >= dst && src + srcLen <= dst + dstSize) {
        ERR("strcpy_s FAIL source + length overlaps destination");
        return -3;
    }
    if (srcLen > dstSize) {
        ERR("strcpy_s FAIL dstLen + srcLen > size");
        return -2;
    }
    strcpy(dst, src);
    return 0;    
}


/// sw_tolower exists because not all compiler libraries have this function
///
/// This takes a character and if it is upper-case, it converts it to
/// lower-case and returns it.
///
/// @note an alternate means would be a 256-entry lookup table. Very fast...
///
/// @param a is the character to convert
/// @returns the lower case equivalent to a
///
char sw_tolower(char a) {
    if (a >= 'A' && a <= 'Z')
        return (a - 'A' + 'a');
    else
        return a;
}

/// sw_strnicmp exists because not all compiler libraries have this function.
///
/// In a case-insensitive compare, evaluate 'n' characters of the left and
/// right referenced strings.
///
/// @note Some compilers have strnicmp, others _strnicmp, and others have C++ 
/// methods, which is outside the scope of this C-portable set of functions.
///
/// @param l is a pointer to the string on the left
/// @param r is a pointer to the string on the right
/// @param n is the number of characters to compare
/// @returns -1 if l < r
/// @returns 0 if l == r
/// @returns +1 if l > r
///
int sw_strnicmp(const char *l, const char *r, size_t n) {
    int result = 0;

    if (n != 0) {
        do {
            result = sw_tolower(*l++) - sw_tolower(*r++);
        } while ((result == 0) && (*l != '\0') && (--n > 0));
    }
    if (result < -1)
        result = -1;
    else if (result > 1)
        result = 1;
    return result;
}


/// sw_stristr exists because not all compiler libraries have this function.
///
/// In a case-insenstive search, try to find the needle in the haystack.
///
/// @param haystack is a pointer to string being searched
/// @param needle is a pointer to a string to find
/// @returns a pointer to the found needle in the haystack, or NULL
///
const char * sw_stristr(const char * haystack, const char * needle) {
    while (*haystack) {
        if (sw_tolower(*haystack) == sw_tolower(*needle)) {
            if (sw_strnicmp(haystack, needle, strlen(needle)) == 0) {
                return (char *)haystack;
            }
        } 
        haystack++;
    }
    return NULL;
}

/// sw_stristr exists because not all compiler libraries have this function.
///
/// In a case-insenstive search, try to find the needle in the haystack.
///
/// @param haystack is a pointer to string being searched
/// @param needle is a pointer to a string to find
/// @returns a pointer to the found needle in the haystack, or NULL
///
char * sw_stristr(char * haystack, const char * needle) {
    while (*haystack) {
        if (sw_tolower(*haystack) == sw_tolower(*needle)) {
            if (sw_strnicmp(haystack, needle, strlen(needle)) == 0) {
                return (char *)haystack;
            }
        } 
        haystack++;
    }
    return NULL;
}