String stuff that should be in stdlib but isn't.
Dependents: X10Svr SSDP_Server
SW_String.cpp@2:c7a3039893cb, 2018-11-18 (annotated)
- Committer:
- WiredHome
- Date:
- Sun Nov 18 04:04:48 2018 +0000
- Revision:
- 2:c7a3039893cb
- Parent:
- 1:65bc379d8cd0
- Child:
- 3:bc30d348f5b4
Improve the diagnostic when debug is enabled.
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
WiredHome | 0:6d899ce93ea0 | 1 | |
WiredHome | 0:6d899ce93ea0 | 2 | #include "SW_String.h" |
WiredHome | 0:6d899ce93ea0 | 3 | |
WiredHome | 2:c7a3039893cb | 4 | #define DEBUG "SWst" |
WiredHome | 2:c7a3039893cb | 5 | #include <cstdio> |
WiredHome | 2:c7a3039893cb | 6 | #if (defined(DEBUG) && !defined(TARGET_LPC11U24)) |
WiredHome | 2:c7a3039893cb | 7 | #define DBG(x, ...) std::printf("[DBG %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 2:c7a3039893cb | 8 | #define WARN(x, ...) std::printf("[WRN %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 2:c7a3039893cb | 9 | #define ERR(x, ...) std::printf("[ERR %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 2:c7a3039893cb | 10 | #define INFO(x, ...) std::printf("[INF %s %4d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__); |
WiredHome | 2:c7a3039893cb | 11 | #else |
WiredHome | 2:c7a3039893cb | 12 | #define DBG(x, ...) |
WiredHome | 2:c7a3039893cb | 13 | #define WARN(x, ...) |
WiredHome | 2:c7a3039893cb | 14 | #define ERR(x, ...) |
WiredHome | 2:c7a3039893cb | 15 | #define INFO(x, ...) |
WiredHome | 2:c7a3039893cb | 16 | #endif |
WiredHome | 2:c7a3039893cb | 17 | |
WiredHome | 1:65bc379d8cd0 | 18 | /// A more secure version of strcat |
WiredHome | 1:65bc379d8cd0 | 19 | /// |
WiredHome | 1:65bc379d8cd0 | 20 | /// This function is like a wrapper on strcat, to first validate the concatination |
WiredHome | 1:65bc379d8cd0 | 21 | /// and then if all parameters appear good, it will call strcat. It will not |
WiredHome | 1:65bc379d8cd0 | 22 | /// permit overlapping source and destination. |
WiredHome | 1:65bc379d8cd0 | 23 | /// |
WiredHome | 1:65bc379d8cd0 | 24 | /// If there is an error, no concatination is performed. |
WiredHome | 1:65bc379d8cd0 | 25 | /// |
WiredHome | 1:65bc379d8cd0 | 26 | /// @note This has a different return value than the normal strcat. |
WiredHome | 1:65bc379d8cd0 | 27 | /// |
WiredHome | 1:65bc379d8cd0 | 28 | /// @param[out] dst is a pointer to the start of the destination buffer (not necessarily |
WiredHome | 1:65bc379d8cd0 | 29 | /// where the next string will appear). |
WiredHome | 1:65bc379d8cd0 | 30 | /// @param[in] dstSize defines the size of the destination buffer. |
WiredHome | 1:65bc379d8cd0 | 31 | /// @param[in] src is a pointer to the source. |
WiredHome | 1:65bc379d8cd0 | 32 | /// |
WiredHome | 1:65bc379d8cd0 | 33 | /// @returns |
WiredHome | 1:65bc379d8cd0 | 34 | /// - 0 = no error |
WiredHome | 1:65bc379d8cd0 | 35 | /// - -1 = destination pointer invalid |
WiredHome | 1:65bc379d8cd0 | 36 | /// - -2 = source is too big to append into the destination |
WiredHome | 1:65bc379d8cd0 | 37 | /// - -3 = overlap between src and dst |
WiredHome | 1:65bc379d8cd0 | 38 | /// |
WiredHome | 1:65bc379d8cd0 | 39 | int strcat_s(char * dst, size_t dstSize, const char * src) { |
WiredHome | 2:c7a3039893cb | 40 | if (dst == NULL) { |
WiredHome | 2:c7a3039893cb | 41 | ERR("strcat_s FAIL destination == NULL"); |
WiredHome | 1:65bc379d8cd0 | 42 | return -1; |
WiredHome | 2:c7a3039893cb | 43 | } |
WiredHome | 1:65bc379d8cd0 | 44 | if (src == NULL || *src == '\0') |
WiredHome | 1:65bc379d8cd0 | 45 | return 0; // done, that was easy. |
WiredHome | 2:c7a3039893cb | 46 | if (src >= dst && src <= dst + dstSize) { |
WiredHome | 2:c7a3039893cb | 47 | ERR("strcat_s FAIL source overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 48 | return -3; |
WiredHome | 2:c7a3039893cb | 49 | } |
WiredHome | 1:65bc379d8cd0 | 50 | int dstLen = strlen(dst); |
WiredHome | 1:65bc379d8cd0 | 51 | int srcLen = strlen(src); |
WiredHome | 2:c7a3039893cb | 52 | if (src + srcLen >= dst && src + srcLen <= dst + dstSize) { |
WiredHome | 2:c7a3039893cb | 53 | ERR("strcat_s FAIL source + length overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 54 | return -3; |
WiredHome | 2:c7a3039893cb | 55 | } |
WiredHome | 2:c7a3039893cb | 56 | if (dstLen + srcLen > dstSize) { |
WiredHome | 2:c7a3039893cb | 57 | ERR("strcat_s FAIL dstLen + srcLen > size"); |
WiredHome | 1:65bc379d8cd0 | 58 | return -2; |
WiredHome | 2:c7a3039893cb | 59 | } |
WiredHome | 1:65bc379d8cd0 | 60 | strcat(dst, src); |
WiredHome | 1:65bc379d8cd0 | 61 | return 0; |
WiredHome | 1:65bc379d8cd0 | 62 | } |
WiredHome | 1:65bc379d8cd0 | 63 | |
WiredHome | 1:65bc379d8cd0 | 64 | |
WiredHome | 1:65bc379d8cd0 | 65 | /// A more secure version of strcpy |
WiredHome | 1:65bc379d8cd0 | 66 | /// |
WiredHome | 1:65bc379d8cd0 | 67 | /// This function is like a wrapper on strcpy, to first validate the concatination |
WiredHome | 1:65bc379d8cd0 | 68 | /// and then if all parameters appear good, it will call strcpy. It will not |
WiredHome | 1:65bc379d8cd0 | 69 | /// permit overlapping source and destination. |
WiredHome | 1:65bc379d8cd0 | 70 | /// |
WiredHome | 1:65bc379d8cd0 | 71 | /// If there is an error, no copy is performed. |
WiredHome | 1:65bc379d8cd0 | 72 | /// |
WiredHome | 1:65bc379d8cd0 | 73 | /// @note This has a different return value than the normal strcpy. |
WiredHome | 1:65bc379d8cd0 | 74 | /// |
WiredHome | 1:65bc379d8cd0 | 75 | /// @param[out] dst is a pointer to the start of the destination buffer. |
WiredHome | 1:65bc379d8cd0 | 76 | /// @param[in] dstSize defines the size of the destination buffer. |
WiredHome | 1:65bc379d8cd0 | 77 | /// @param[in] src is a pointer to the source. |
WiredHome | 1:65bc379d8cd0 | 78 | /// |
WiredHome | 1:65bc379d8cd0 | 79 | /// @returns |
WiredHome | 1:65bc379d8cd0 | 80 | /// - 0 = no error |
WiredHome | 1:65bc379d8cd0 | 81 | /// - -1 = destination pointer invalid |
WiredHome | 1:65bc379d8cd0 | 82 | /// - -2 = source is too big to append into the destination |
WiredHome | 1:65bc379d8cd0 | 83 | /// - -3 = overlap between src and dst |
WiredHome | 1:65bc379d8cd0 | 84 | /// |
WiredHome | 1:65bc379d8cd0 | 85 | int strcpy_s(char * dst, size_t dstSize, const char * src) { |
WiredHome | 2:c7a3039893cb | 86 | if (dst == NULL) { |
WiredHome | 2:c7a3039893cb | 87 | ERR("strcpy_s FAIL destination == NULL"); |
WiredHome | 1:65bc379d8cd0 | 88 | return -1; |
WiredHome | 2:c7a3039893cb | 89 | } |
WiredHome | 1:65bc379d8cd0 | 90 | if (src == NULL || *src == '\0') { |
WiredHome | 1:65bc379d8cd0 | 91 | *dst = '\0'; |
WiredHome | 1:65bc379d8cd0 | 92 | return 0; // done, that was easy. |
WiredHome | 1:65bc379d8cd0 | 93 | } |
WiredHome | 2:c7a3039893cb | 94 | if (src >= dst && src <= dst + dstSize) { |
WiredHome | 2:c7a3039893cb | 95 | ERR("strcpy_s FAIL source overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 96 | return -3; |
WiredHome | 2:c7a3039893cb | 97 | } |
WiredHome | 1:65bc379d8cd0 | 98 | int srcLen = strlen(src); |
WiredHome | 2:c7a3039893cb | 99 | if (src + srcLen >= dst && src + srcLen <= dst + dstSize) { |
WiredHome | 2:c7a3039893cb | 100 | ERR("strcpy_s FAIL source + length overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 101 | return -3; |
WiredHome | 2:c7a3039893cb | 102 | } |
WiredHome | 2:c7a3039893cb | 103 | if (srcLen > dstSize) { |
WiredHome | 2:c7a3039893cb | 104 | ERR("strcpy_s FAIL dstLen + srcLen > size"); |
WiredHome | 1:65bc379d8cd0 | 105 | return -2; |
WiredHome | 2:c7a3039893cb | 106 | } |
WiredHome | 1:65bc379d8cd0 | 107 | strcpy(dst, src); |
WiredHome | 1:65bc379d8cd0 | 108 | return 0; |
WiredHome | 1:65bc379d8cd0 | 109 | } |
WiredHome | 1:65bc379d8cd0 | 110 | |
WiredHome | 1:65bc379d8cd0 | 111 | |
WiredHome | 0:6d899ce93ea0 | 112 | /// sw_tolower exists because not all compiler libraries have this function |
WiredHome | 0:6d899ce93ea0 | 113 | /// |
WiredHome | 0:6d899ce93ea0 | 114 | /// This takes a character and if it is upper-case, it converts it to |
WiredHome | 0:6d899ce93ea0 | 115 | /// lower-case and returns it. |
WiredHome | 0:6d899ce93ea0 | 116 | /// |
WiredHome | 0:6d899ce93ea0 | 117 | /// @note an alternate means would be a 256-entry lookup table. Very fast... |
WiredHome | 0:6d899ce93ea0 | 118 | /// |
WiredHome | 0:6d899ce93ea0 | 119 | /// @param a is the character to convert |
WiredHome | 0:6d899ce93ea0 | 120 | /// @returns the lower case equivalent to a |
WiredHome | 0:6d899ce93ea0 | 121 | /// |
WiredHome | 0:6d899ce93ea0 | 122 | char sw_tolower(char a) { |
WiredHome | 0:6d899ce93ea0 | 123 | if (a >= 'A' && a <= 'Z') |
WiredHome | 0:6d899ce93ea0 | 124 | return (a - 'A' + 'a'); |
WiredHome | 0:6d899ce93ea0 | 125 | else |
WiredHome | 0:6d899ce93ea0 | 126 | return a; |
WiredHome | 0:6d899ce93ea0 | 127 | } |
WiredHome | 0:6d899ce93ea0 | 128 | |
WiredHome | 0:6d899ce93ea0 | 129 | /// sw_strnicmp exists because not all compiler libraries have this function. |
WiredHome | 0:6d899ce93ea0 | 130 | /// |
WiredHome | 0:6d899ce93ea0 | 131 | /// In a case-insensitive compare, evaluate 'n' characters of the left and |
WiredHome | 0:6d899ce93ea0 | 132 | /// right referenced strings. |
WiredHome | 0:6d899ce93ea0 | 133 | /// |
WiredHome | 0:6d899ce93ea0 | 134 | /// @note Some compilers have strnicmp, others _strnicmp, and others have C++ |
WiredHome | 0:6d899ce93ea0 | 135 | /// methods, which is outside the scope of this C-portable set of functions. |
WiredHome | 0:6d899ce93ea0 | 136 | /// |
WiredHome | 0:6d899ce93ea0 | 137 | /// @param l is a pointer to the string on the left |
WiredHome | 0:6d899ce93ea0 | 138 | /// @param r is a pointer to the string on the right |
WiredHome | 0:6d899ce93ea0 | 139 | /// @param n is the number of characters to compare |
WiredHome | 0:6d899ce93ea0 | 140 | /// @returns -1 if l < r |
WiredHome | 0:6d899ce93ea0 | 141 | /// @returns 0 if l == r |
WiredHome | 0:6d899ce93ea0 | 142 | /// @returns +1 if l > r |
WiredHome | 0:6d899ce93ea0 | 143 | /// |
WiredHome | 0:6d899ce93ea0 | 144 | int sw_strnicmp(const char *l, const char *r, size_t n) { |
WiredHome | 0:6d899ce93ea0 | 145 | int result = 0; |
WiredHome | 0:6d899ce93ea0 | 146 | |
WiredHome | 0:6d899ce93ea0 | 147 | if (n != 0) { |
WiredHome | 0:6d899ce93ea0 | 148 | do { |
WiredHome | 0:6d899ce93ea0 | 149 | result = sw_tolower(*l++) - sw_tolower(*r++); |
WiredHome | 0:6d899ce93ea0 | 150 | } while ((result == 0) && (*l != '\0') && (--n > 0)); |
WiredHome | 0:6d899ce93ea0 | 151 | } |
WiredHome | 0:6d899ce93ea0 | 152 | if (result < -1) |
WiredHome | 0:6d899ce93ea0 | 153 | result = -1; |
WiredHome | 0:6d899ce93ea0 | 154 | else if (result > 1) |
WiredHome | 0:6d899ce93ea0 | 155 | result = 1; |
WiredHome | 0:6d899ce93ea0 | 156 | return result; |
WiredHome | 0:6d899ce93ea0 | 157 | } |
WiredHome | 0:6d899ce93ea0 | 158 | |
WiredHome | 0:6d899ce93ea0 | 159 | |
WiredHome | 0:6d899ce93ea0 | 160 | /// sw_stristr exists because not all compiler libraries have this function. |
WiredHome | 0:6d899ce93ea0 | 161 | /// |
WiredHome | 0:6d899ce93ea0 | 162 | /// In a case-insenstive search, try to find the needle in the haystack. |
WiredHome | 0:6d899ce93ea0 | 163 | /// |
WiredHome | 0:6d899ce93ea0 | 164 | /// @param haystack is a pointer to string being searched |
WiredHome | 0:6d899ce93ea0 | 165 | /// @param needle is a pointer to a string to find |
WiredHome | 0:6d899ce93ea0 | 166 | /// @returns a pointer to the found needle in the haystack, or NULL |
WiredHome | 0:6d899ce93ea0 | 167 | /// |
WiredHome | 0:6d899ce93ea0 | 168 | const char * sw_stristr(const char * haystack, const char * needle) { |
WiredHome | 0:6d899ce93ea0 | 169 | while (*haystack) { |
WiredHome | 0:6d899ce93ea0 | 170 | if (sw_tolower(*haystack) == sw_tolower(*needle)) { |
WiredHome | 0:6d899ce93ea0 | 171 | if (sw_strnicmp(haystack, needle, strlen(needle)) == 0) { |
WiredHome | 0:6d899ce93ea0 | 172 | return (char *)haystack; |
WiredHome | 0:6d899ce93ea0 | 173 | } |
WiredHome | 0:6d899ce93ea0 | 174 | } |
WiredHome | 0:6d899ce93ea0 | 175 | haystack++; |
WiredHome | 0:6d899ce93ea0 | 176 | } |
WiredHome | 0:6d899ce93ea0 | 177 | return NULL; |
WiredHome | 0:6d899ce93ea0 | 178 | } |
WiredHome | 0:6d899ce93ea0 | 179 | |
WiredHome | 0:6d899ce93ea0 | 180 | /// sw_stristr exists because not all compiler libraries have this function. |
WiredHome | 0:6d899ce93ea0 | 181 | /// |
WiredHome | 0:6d899ce93ea0 | 182 | /// In a case-insenstive search, try to find the needle in the haystack. |
WiredHome | 0:6d899ce93ea0 | 183 | /// |
WiredHome | 0:6d899ce93ea0 | 184 | /// @param haystack is a pointer to string being searched |
WiredHome | 0:6d899ce93ea0 | 185 | /// @param needle is a pointer to a string to find |
WiredHome | 0:6d899ce93ea0 | 186 | /// @returns a pointer to the found needle in the haystack, or NULL |
WiredHome | 0:6d899ce93ea0 | 187 | /// |
WiredHome | 0:6d899ce93ea0 | 188 | char * sw_stristr(char * haystack, const char * needle) { |
WiredHome | 0:6d899ce93ea0 | 189 | while (*haystack) { |
WiredHome | 0:6d899ce93ea0 | 190 | if (sw_tolower(*haystack) == sw_tolower(*needle)) { |
WiredHome | 0:6d899ce93ea0 | 191 | if (sw_strnicmp(haystack, needle, strlen(needle)) == 0) { |
WiredHome | 0:6d899ce93ea0 | 192 | return (char *)haystack; |
WiredHome | 0:6d899ce93ea0 | 193 | } |
WiredHome | 0:6d899ce93ea0 | 194 | } |
WiredHome | 0:6d899ce93ea0 | 195 | haystack++; |
WiredHome | 0:6d899ce93ea0 | 196 | } |
WiredHome | 0:6d899ce93ea0 | 197 | return NULL; |
WiredHome | 0:6d899ce93ea0 | 198 | } |