String stuff that should be in stdlib but isn't.
Dependents: X10Svr SSDP_Server
SW_String.cpp@3:bc30d348f5b4, 2019-02-27 (annotated)
- Committer:
- WiredHome
- Date:
- Wed Feb 27 18:22:04 2019 +0000
- Revision:
- 3:bc30d348f5b4
- Parent:
- 2:c7a3039893cb
Fix the venerable off-by-one error; if the two strings are adjacent it was detecting a false overlap.
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 | 3:bc30d348f5b4 | 46 | // [Source .... |
WiredHome | 3:bc30d348f5b4 | 47 | // [Destination ... ] |
WiredHome | 3:bc30d348f5b4 | 48 | if (src >= dst && src < (dst + dstSize)) { |
WiredHome | 2:c7a3039893cb | 49 | ERR("strcat_s FAIL source overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 50 | return -3; |
WiredHome | 2:c7a3039893cb | 51 | } |
WiredHome | 1:65bc379d8cd0 | 52 | int dstLen = strlen(dst); |
WiredHome | 1:65bc379d8cd0 | 53 | int srcLen = strlen(src); |
WiredHome | 3:bc30d348f5b4 | 54 | // [Source ..... +srcLen] |
WiredHome | 3:bc30d348f5b4 | 55 | // [Destination + dstSize] |
WiredHome | 2:c7a3039893cb | 56 | if (src + srcLen >= dst && src + srcLen <= dst + dstSize) { |
WiredHome | 2:c7a3039893cb | 57 | ERR("strcat_s FAIL source + length overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 58 | return -3; |
WiredHome | 2:c7a3039893cb | 59 | } |
WiredHome | 3:bc30d348f5b4 | 60 | // [Source length > freespace] |
WiredHome | 3:bc30d348f5b4 | 61 | // [Destination ... \0[freespace]] |
WiredHome | 2:c7a3039893cb | 62 | if (dstLen + srcLen > dstSize) { |
WiredHome | 2:c7a3039893cb | 63 | ERR("strcat_s FAIL dstLen + srcLen > size"); |
WiredHome | 1:65bc379d8cd0 | 64 | return -2; |
WiredHome | 2:c7a3039893cb | 65 | } |
WiredHome | 1:65bc379d8cd0 | 66 | strcat(dst, src); |
WiredHome | 1:65bc379d8cd0 | 67 | return 0; |
WiredHome | 1:65bc379d8cd0 | 68 | } |
WiredHome | 1:65bc379d8cd0 | 69 | |
WiredHome | 1:65bc379d8cd0 | 70 | |
WiredHome | 1:65bc379d8cd0 | 71 | /// A more secure version of strcpy |
WiredHome | 1:65bc379d8cd0 | 72 | /// |
WiredHome | 1:65bc379d8cd0 | 73 | /// This function is like a wrapper on strcpy, to first validate the concatination |
WiredHome | 1:65bc379d8cd0 | 74 | /// and then if all parameters appear good, it will call strcpy. It will not |
WiredHome | 1:65bc379d8cd0 | 75 | /// permit overlapping source and destination. |
WiredHome | 1:65bc379d8cd0 | 76 | /// |
WiredHome | 1:65bc379d8cd0 | 77 | /// If there is an error, no copy is performed. |
WiredHome | 1:65bc379d8cd0 | 78 | /// |
WiredHome | 1:65bc379d8cd0 | 79 | /// @note This has a different return value than the normal strcpy. |
WiredHome | 1:65bc379d8cd0 | 80 | /// |
WiredHome | 1:65bc379d8cd0 | 81 | /// @param[out] dst is a pointer to the start of the destination buffer. |
WiredHome | 1:65bc379d8cd0 | 82 | /// @param[in] dstSize defines the size of the destination buffer. |
WiredHome | 1:65bc379d8cd0 | 83 | /// @param[in] src is a pointer to the source. |
WiredHome | 1:65bc379d8cd0 | 84 | /// |
WiredHome | 1:65bc379d8cd0 | 85 | /// @returns |
WiredHome | 1:65bc379d8cd0 | 86 | /// - 0 = no error |
WiredHome | 1:65bc379d8cd0 | 87 | /// - -1 = destination pointer invalid |
WiredHome | 1:65bc379d8cd0 | 88 | /// - -2 = source is too big to append into the destination |
WiredHome | 1:65bc379d8cd0 | 89 | /// - -3 = overlap between src and dst |
WiredHome | 1:65bc379d8cd0 | 90 | /// |
WiredHome | 1:65bc379d8cd0 | 91 | int strcpy_s(char * dst, size_t dstSize, const char * src) { |
WiredHome | 2:c7a3039893cb | 92 | if (dst == NULL) { |
WiredHome | 2:c7a3039893cb | 93 | ERR("strcpy_s FAIL destination == NULL"); |
WiredHome | 1:65bc379d8cd0 | 94 | return -1; |
WiredHome | 2:c7a3039893cb | 95 | } |
WiredHome | 1:65bc379d8cd0 | 96 | if (src == NULL || *src == '\0') { |
WiredHome | 1:65bc379d8cd0 | 97 | *dst = '\0'; |
WiredHome | 1:65bc379d8cd0 | 98 | return 0; // done, that was easy. |
WiredHome | 1:65bc379d8cd0 | 99 | } |
WiredHome | 3:bc30d348f5b4 | 100 | if (src >= dst && src < dst + dstSize) { |
WiredHome | 2:c7a3039893cb | 101 | ERR("strcpy_s FAIL source overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 102 | return -3; |
WiredHome | 2:c7a3039893cb | 103 | } |
WiredHome | 1:65bc379d8cd0 | 104 | int srcLen = strlen(src); |
WiredHome | 2:c7a3039893cb | 105 | if (src + srcLen >= dst && src + srcLen <= dst + dstSize) { |
WiredHome | 2:c7a3039893cb | 106 | ERR("strcpy_s FAIL source + length overlaps destination"); |
WiredHome | 1:65bc379d8cd0 | 107 | return -3; |
WiredHome | 2:c7a3039893cb | 108 | } |
WiredHome | 2:c7a3039893cb | 109 | if (srcLen > dstSize) { |
WiredHome | 2:c7a3039893cb | 110 | ERR("strcpy_s FAIL dstLen + srcLen > size"); |
WiredHome | 1:65bc379d8cd0 | 111 | return -2; |
WiredHome | 2:c7a3039893cb | 112 | } |
WiredHome | 1:65bc379d8cd0 | 113 | strcpy(dst, src); |
WiredHome | 1:65bc379d8cd0 | 114 | return 0; |
WiredHome | 1:65bc379d8cd0 | 115 | } |
WiredHome | 1:65bc379d8cd0 | 116 | |
WiredHome | 1:65bc379d8cd0 | 117 | |
WiredHome | 0:6d899ce93ea0 | 118 | /// sw_tolower exists because not all compiler libraries have this function |
WiredHome | 0:6d899ce93ea0 | 119 | /// |
WiredHome | 0:6d899ce93ea0 | 120 | /// This takes a character and if it is upper-case, it converts it to |
WiredHome | 0:6d899ce93ea0 | 121 | /// lower-case and returns it. |
WiredHome | 0:6d899ce93ea0 | 122 | /// |
WiredHome | 0:6d899ce93ea0 | 123 | /// @note an alternate means would be a 256-entry lookup table. Very fast... |
WiredHome | 0:6d899ce93ea0 | 124 | /// |
WiredHome | 0:6d899ce93ea0 | 125 | /// @param a is the character to convert |
WiredHome | 0:6d899ce93ea0 | 126 | /// @returns the lower case equivalent to a |
WiredHome | 0:6d899ce93ea0 | 127 | /// |
WiredHome | 0:6d899ce93ea0 | 128 | char sw_tolower(char a) { |
WiredHome | 0:6d899ce93ea0 | 129 | if (a >= 'A' && a <= 'Z') |
WiredHome | 0:6d899ce93ea0 | 130 | return (a - 'A' + 'a'); |
WiredHome | 0:6d899ce93ea0 | 131 | else |
WiredHome | 0:6d899ce93ea0 | 132 | return a; |
WiredHome | 0:6d899ce93ea0 | 133 | } |
WiredHome | 0:6d899ce93ea0 | 134 | |
WiredHome | 0:6d899ce93ea0 | 135 | /// sw_strnicmp exists because not all compiler libraries have this function. |
WiredHome | 0:6d899ce93ea0 | 136 | /// |
WiredHome | 0:6d899ce93ea0 | 137 | /// In a case-insensitive compare, evaluate 'n' characters of the left and |
WiredHome | 0:6d899ce93ea0 | 138 | /// right referenced strings. |
WiredHome | 0:6d899ce93ea0 | 139 | /// |
WiredHome | 0:6d899ce93ea0 | 140 | /// @note Some compilers have strnicmp, others _strnicmp, and others have C++ |
WiredHome | 0:6d899ce93ea0 | 141 | /// methods, which is outside the scope of this C-portable set of functions. |
WiredHome | 0:6d899ce93ea0 | 142 | /// |
WiredHome | 0:6d899ce93ea0 | 143 | /// @param l is a pointer to the string on the left |
WiredHome | 0:6d899ce93ea0 | 144 | /// @param r is a pointer to the string on the right |
WiredHome | 0:6d899ce93ea0 | 145 | /// @param n is the number of characters to compare |
WiredHome | 0:6d899ce93ea0 | 146 | /// @returns -1 if l < r |
WiredHome | 0:6d899ce93ea0 | 147 | /// @returns 0 if l == r |
WiredHome | 0:6d899ce93ea0 | 148 | /// @returns +1 if l > r |
WiredHome | 0:6d899ce93ea0 | 149 | /// |
WiredHome | 0:6d899ce93ea0 | 150 | int sw_strnicmp(const char *l, const char *r, size_t n) { |
WiredHome | 0:6d899ce93ea0 | 151 | int result = 0; |
WiredHome | 0:6d899ce93ea0 | 152 | |
WiredHome | 0:6d899ce93ea0 | 153 | if (n != 0) { |
WiredHome | 0:6d899ce93ea0 | 154 | do { |
WiredHome | 0:6d899ce93ea0 | 155 | result = sw_tolower(*l++) - sw_tolower(*r++); |
WiredHome | 0:6d899ce93ea0 | 156 | } while ((result == 0) && (*l != '\0') && (--n > 0)); |
WiredHome | 0:6d899ce93ea0 | 157 | } |
WiredHome | 0:6d899ce93ea0 | 158 | if (result < -1) |
WiredHome | 0:6d899ce93ea0 | 159 | result = -1; |
WiredHome | 0:6d899ce93ea0 | 160 | else if (result > 1) |
WiredHome | 0:6d899ce93ea0 | 161 | result = 1; |
WiredHome | 0:6d899ce93ea0 | 162 | return result; |
WiredHome | 0:6d899ce93ea0 | 163 | } |
WiredHome | 0:6d899ce93ea0 | 164 | |
WiredHome | 0:6d899ce93ea0 | 165 | |
WiredHome | 0:6d899ce93ea0 | 166 | /// sw_stristr exists because not all compiler libraries have this function. |
WiredHome | 0:6d899ce93ea0 | 167 | /// |
WiredHome | 0:6d899ce93ea0 | 168 | /// In a case-insenstive search, try to find the needle in the haystack. |
WiredHome | 0:6d899ce93ea0 | 169 | /// |
WiredHome | 0:6d899ce93ea0 | 170 | /// @param haystack is a pointer to string being searched |
WiredHome | 0:6d899ce93ea0 | 171 | /// @param needle is a pointer to a string to find |
WiredHome | 0:6d899ce93ea0 | 172 | /// @returns a pointer to the found needle in the haystack, or NULL |
WiredHome | 0:6d899ce93ea0 | 173 | /// |
WiredHome | 0:6d899ce93ea0 | 174 | const char * sw_stristr(const char * haystack, const char * needle) { |
WiredHome | 0:6d899ce93ea0 | 175 | while (*haystack) { |
WiredHome | 0:6d899ce93ea0 | 176 | if (sw_tolower(*haystack) == sw_tolower(*needle)) { |
WiredHome | 0:6d899ce93ea0 | 177 | if (sw_strnicmp(haystack, needle, strlen(needle)) == 0) { |
WiredHome | 0:6d899ce93ea0 | 178 | return (char *)haystack; |
WiredHome | 0:6d899ce93ea0 | 179 | } |
WiredHome | 0:6d899ce93ea0 | 180 | } |
WiredHome | 0:6d899ce93ea0 | 181 | haystack++; |
WiredHome | 0:6d899ce93ea0 | 182 | } |
WiredHome | 0:6d899ce93ea0 | 183 | return NULL; |
WiredHome | 0:6d899ce93ea0 | 184 | } |
WiredHome | 0:6d899ce93ea0 | 185 | |
WiredHome | 0:6d899ce93ea0 | 186 | /// sw_stristr exists because not all compiler libraries have this function. |
WiredHome | 0:6d899ce93ea0 | 187 | /// |
WiredHome | 0:6d899ce93ea0 | 188 | /// In a case-insenstive search, try to find the needle in the haystack. |
WiredHome | 0:6d899ce93ea0 | 189 | /// |
WiredHome | 0:6d899ce93ea0 | 190 | /// @param haystack is a pointer to string being searched |
WiredHome | 0:6d899ce93ea0 | 191 | /// @param needle is a pointer to a string to find |
WiredHome | 0:6d899ce93ea0 | 192 | /// @returns a pointer to the found needle in the haystack, or NULL |
WiredHome | 0:6d899ce93ea0 | 193 | /// |
WiredHome | 0:6d899ce93ea0 | 194 | char * sw_stristr(char * haystack, const char * needle) { |
WiredHome | 0:6d899ce93ea0 | 195 | while (*haystack) { |
WiredHome | 0:6d899ce93ea0 | 196 | if (sw_tolower(*haystack) == sw_tolower(*needle)) { |
WiredHome | 0:6d899ce93ea0 | 197 | if (sw_strnicmp(haystack, needle, strlen(needle)) == 0) { |
WiredHome | 0:6d899ce93ea0 | 198 | return (char *)haystack; |
WiredHome | 0:6d899ce93ea0 | 199 | } |
WiredHome | 0:6d899ce93ea0 | 200 | } |
WiredHome | 0:6d899ce93ea0 | 201 | haystack++; |
WiredHome | 0:6d899ce93ea0 | 202 | } |
WiredHome | 0:6d899ce93ea0 | 203 | return NULL; |
WiredHome | 0:6d899ce93ea0 | 204 | } |