diff options
Diffstat (limited to 'winpr/libwinpr/crt/string.c')
-rw-r--r-- | winpr/libwinpr/crt/string.c | 832 |
1 files changed, 832 insertions, 0 deletions
diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c new file mode 100644 index 0000000..93721a6 --- /dev/null +++ b/winpr/libwinpr/crt/string.c @@ -0,0 +1,832 @@ +/** + * WinPR: Windows Portable Runtime + * String Manipulation (CRT) + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> +#include <winpr/assert.h> + +#include <errno.h> +#include <stdio.h> +#include <ctype.h> +#include <wctype.h> +#include <wchar.h> + +#include <winpr/crt.h> +#include <winpr/endian.h> + +#if defined(WITH_URIPARSER) +#include <uriparser/Uri.h> +#endif + +/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */ + +#include "../log.h" +#define TAG WINPR_TAG("crt") + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#if defined(WITH_URIPARSER) +char* winpr_str_url_decode(const char* str, size_t len) +{ + char* dst = strndup(str, len); + if (!dst) + return NULL; + + if (!uriUnescapeInPlaceExA(dst, URI_FALSE, URI_FALSE)) + { + free(dst); + return NULL; + } + + return dst; +} + +char* winpr_str_url_encode(const char* str, size_t len) +{ + char* dst = calloc(len + 1, sizeof(char) * 3); + if (!dst) + return NULL; + + if (!uriEscapeA(str, dst, URI_FALSE, URI_FALSE)) + { + free(dst); + return NULL; + } + return dst; +} + +#else +static const char rfc3986[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f, + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static char hex2bin(char what) +{ + if (what >= 'a') + what -= 'a' - 'A'; + if (what >= 'A') + what -= ('A' - 10); + else + what -= '0'; + return what; +} + +static char unescape(const char* what, size_t* px) +{ + if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2]))) + { + *px += 2; + return 16 * hex2bin(what[1]) + hex2bin(what[2]); + } + + return *what; +} + +char* winpr_str_url_decode(const char* str, size_t len) +{ + char* dst = calloc(len + 1, sizeof(char)); + if (!dst) + return NULL; + + size_t pos = 0; + for (size_t x = 0; x < strnlen(str, len); x++) + { + const char* cur = &str[x]; + dst[pos++] = unescape(cur, &x); + } + return dst; +} + +static char* escape(char* dst, char what) +{ + if (rfc3986[what & 0xff]) + { + *dst = what; + return dst + 1; + } + + sprintf(dst, "%%%02" PRIX8, (BYTE)(what & 0xff)); + return dst + 3; +} + +char* winpr_str_url_encode(const char* str, size_t len) +{ + char* dst = calloc(len + 1, sizeof(char) * 3); + if (!dst) + return NULL; + + char* ptr = dst; + for (size_t x = 0; x < strnlen(str, len); x++) + { + const char cur = str[x]; + ptr = escape(ptr, cur); + } + return dst; +} +#endif + +BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator) +{ + const size_t used = strnlen(buffer, size); + const size_t add = strnlen(what, size); + const size_t sep_len = separator ? strnlen(separator, size) : 0; + const size_t sep = (used > 0) ? sep_len : 0; + + if (used + add + sep >= size) + return FALSE; + + if ((used > 0) && (sep_len > 0)) + strncat(buffer, separator, sep_len); + + strncat(buffer, what, add); + return TRUE; +} + +WINPR_ATTR_FORMAT_ARG(3, 4) +int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...) +{ + va_list ap; + + va_start(ap, templ); + int rc = winpr_vasprintf(s, slen, templ, ap); + va_end(ap); + return rc; +} + +WINPR_ATTR_FORMAT_ARG(3, 0) +int winpr_vasprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, va_list oap) +{ + va_list ap; + + *s = NULL; + *slen = 0; + + va_copy(ap, oap); + const int length = vsnprintf(NULL, 0, templ, ap); + va_end(ap); + if (length < 0) + return length; + + char* str = calloc((size_t)length + 1ul, sizeof(char)); + if (!str) + return -1; + + va_copy(ap, oap); + const int plen = vsprintf(str, templ, ap); + va_end(ap); + + WINPR_ASSERT(length == plen); + *s = str; + *slen = (size_t)length; + return length; +} + +#ifndef _WIN32 + +char* _strdup(const char* strSource) +{ + if (strSource == NULL) + return NULL; + + char* strDestination = strdup(strSource); + + if (strDestination == NULL) + WLog_ERR(TAG, "strdup"); + + return strDestination; +} + +WCHAR* _wcsdup(const WCHAR* strSource) +{ + if (!strSource) + return NULL; + + size_t len = _wcslen(strSource); + WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR)); + + if (strDestination != NULL) + memcpy(strDestination, strSource, len * sizeof(WCHAR)); + + if (strDestination == NULL) + WLog_ERR(TAG, "wcsdup"); + + return strDestination; +} + +WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz) +{ + WINPR_ASSERT(dst); + WINPR_ASSERT(src || (sz == 0)); + + const size_t dlen = _wcslen(dst); + const size_t slen = _wcsnlen(src, sz); + for (size_t x = 0; x < slen; x++) + dst[dlen + x] = src[x]; + dst[dlen + slen] = '\0'; + return dst; +} + +int _stricmp(const char* string1, const char* string2) +{ + return strcasecmp(string1, string2); +} + +int _strnicmp(const char* string1, const char* string2, size_t count) +{ + return strncasecmp(string1, string2, count); +} + +/* _wcscmp -> wcscmp */ + +int _wcscmp(const WCHAR* string1, const WCHAR* string2) +{ + WINPR_ASSERT(string1); + WINPR_ASSERT(string2); + + while (TRUE) + { + const WCHAR w1 = *string1++; + const WCHAR w2 = *string2++; + + if (w1 != w2) + return (int)w1 - w2; + else if ((w1 == '\0') || (w2 == '\0')) + return (int)w1 - w2; + } +} + +int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count) +{ + WINPR_ASSERT(string1); + WINPR_ASSERT(string2); + + for (size_t x = 0; x < count; x++) + { + const WCHAR a = string1[x]; + const WCHAR b = string2[x]; + + if (a != b) + return (int)a - b; + else if ((a == '\0') || (b == '\0')) + return (int)a - b; + } + return 0; +} + +/* _wcslen -> wcslen */ + +size_t _wcslen(const WCHAR* str) +{ + const WCHAR* p = (const WCHAR*)str; + + WINPR_ASSERT(p); + + while (*p) + p++; + + return (size_t)(p - str); +} + +/* _wcsnlen -> wcsnlen */ + +size_t _wcsnlen(const WCHAR* str, size_t max) +{ + WINPR_ASSERT(str); + + size_t x = 0; + for (; x < max; x++) + { + if (str[x] == 0) + return x; + } + + return x; +} + +/* _wcsstr -> wcsstr */ + +WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch) +{ + WINPR_ASSERT(str); + WINPR_ASSERT(strSearch); + + if (strSearch[0] == '\0') + return (WCHAR*)str; + + const size_t searchLen = _wcslen(strSearch); + while (*str) + { + if (_wcsncmp(str, strSearch, searchLen) == 0) + return (WCHAR*)str; + str++; + } + return NULL; +} + +/* _wcschr -> wcschr */ + +WCHAR* _wcschr(const WCHAR* str, WCHAR value) +{ + union + { + const WCHAR* cc; + WCHAR* c; + } cnv; + const WCHAR* p = (const WCHAR*)str; + + while (*p && (*p != value)) + p++; + + cnv.cc = (*p == value) ? p : NULL; + return cnv.c; +} + +/* _wcsrchr -> wcsrchr */ + +WCHAR* _wcsrchr(const WCHAR* str, WCHAR c) +{ + union + { + const WCHAR* cc; + WCHAR* c; + } cnv; + const WCHAR* p = NULL; + + if (!str) + return NULL; + + for (; *str != '\0'; str++) + { + const WCHAR ch = *str; + if (ch == c) + p = str; + } + + cnv.cc = p; + return cnv.c; +} + +char* strtok_s(char* strToken, const char* strDelimit, char** context) +{ + return strtok_r(strToken, strDelimit, context); +} + +WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context) +{ + WCHAR* nextToken = NULL; + WCHAR value = 0; + + if (!strToken) + strToken = *context; + + value = *strToken; + + while (*strToken && _wcschr(strDelimit, value)) + { + strToken++; + value = *strToken; + } + + if (!*strToken) + return NULL; + + nextToken = strToken++; + value = *strToken; + + while (*strToken && !(_wcschr(strDelimit, value))) + { + strToken++; + value = *strToken; + } + + if (*strToken) + *strToken++ = 0; + + *context = strToken; + return nextToken; +} + +#endif + +#if !defined(_WIN32) || defined(_UWP) + +/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll + * http://msdn.microsoft.com/en-us/library/hh802935/ + */ + +#include "casing.c" + +LPSTR CharUpperA(LPSTR lpsz) +{ + size_t length = 0; + + if (!lpsz) + return NULL; + + length = strlen(lpsz); + + if (length < 1) + return (LPSTR)NULL; + + if (length == 1) + { + char c = *lpsz; + + if ((c >= 'a') && (c <= 'z')) + c = c - 'a' + 'A'; + + *lpsz = c; + return lpsz; + } + + for (size_t i = 0; i < length; i++) + { + if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z')) + lpsz[i] = lpsz[i] - 'a' + 'A'; + } + + return lpsz; +} + +LPWSTR CharUpperW(LPWSTR lpsz) +{ + size_t length = 0; + + if (!lpsz) + return NULL; + + length = _wcslen(lpsz); + + if (length < 1) + return (LPWSTR)NULL; + + if (length == 1) + { + WCHAR c = *lpsz; + + if ((c >= L'a') && (c <= L'z')) + c = c - L'a' + L'A'; + + *lpsz = c; + return lpsz; + } + + for (size_t i = 0; i < length; i++) + { + if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z')) + lpsz[i] = lpsz[i] - L'a' + L'A'; + } + + return lpsz; +} + +DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength) +{ + if (cchLength < 1) + return 0; + + for (DWORD i = 0; i < cchLength; i++) + { + if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z')) + lpsz[i] = lpsz[i] - 'a' + 'A'; + } + + return cchLength; +} + +DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength) +{ + WCHAR value = 0; + + for (DWORD i = 0; i < cchLength; i++) + { + Data_Read_UINT16(&lpsz[i], value); + value = WINPR_TOUPPERW(value); + Data_Write_UINT16(&lpsz[i], value); + } + + return cchLength; +} + +LPSTR CharLowerA(LPSTR lpsz) +{ + size_t length = 0; + + if (!lpsz) + return (LPSTR)NULL; + + length = strlen(lpsz); + + if (length < 1) + return (LPSTR)NULL; + + if (length == 1) + { + char c = *lpsz; + + if ((c >= 'A') && (c <= 'Z')) + c = c - 'A' + 'a'; + + *lpsz = c; + return lpsz; + } + + for (size_t i = 0; i < length; i++) + { + if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z')) + lpsz[i] = lpsz[i] - 'A' + 'a'; + } + + return lpsz; +} + +LPWSTR CharLowerW(LPWSTR lpsz) +{ + CharLowerBuffW(lpsz, _wcslen(lpsz)); + return lpsz; +} + +DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength) +{ + if (cchLength < 1) + return 0; + + for (DWORD i = 0; i < cchLength; i++) + { + if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z')) + lpsz[i] = lpsz[i] - 'A' + 'a'; + } + + return cchLength; +} + +DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength) +{ + WCHAR value = 0; + + for (DWORD i = 0; i < cchLength; i++) + { + Data_Read_UINT16(&lpsz[i], value); + value = WINPR_TOLOWERW(value); + Data_Write_UINT16(&lpsz[i], value); + } + + return cchLength; +} + +BOOL IsCharAlphaA(CHAR ch) +{ + if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z'))) + return 1; + else + return 0; +} + +BOOL IsCharAlphaW(WCHAR ch) +{ + if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z'))) + return 1; + else + return 0; +} + +BOOL IsCharAlphaNumericA(CHAR ch) +{ + if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || + ((ch >= '0') && (ch <= '9'))) + return 1; + else + return 0; +} + +BOOL IsCharAlphaNumericW(WCHAR ch) +{ + if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) || + ((ch >= L'0') && (ch <= L'9'))) + return 1; + else + return 0; +} + +BOOL IsCharUpperA(CHAR ch) +{ + if ((ch >= 'A') && (ch <= 'Z')) + return 1; + else + return 0; +} + +BOOL IsCharUpperW(WCHAR ch) +{ + if ((ch >= L'A') && (ch <= L'Z')) + return 1; + else + return 0; +} + +BOOL IsCharLowerA(CHAR ch) +{ + if ((ch >= 'a') && (ch <= 'z')) + return 1; + else + return 0; +} + +BOOL IsCharLowerW(WCHAR ch) +{ + if ((ch >= L'a') && (ch <= L'z')) + return 1; + else + return 0; +} + +#endif + +size_t ConvertLineEndingToLF(char* str, size_t size) +{ + size_t skip = 0; + + WINPR_ASSERT(str || (size == 0)); + for (size_t x = 0; x < size; x++) + { + char c = str[x]; + switch (c) + { + case '\r': + str[x - skip] = '\n'; + if ((x + 1 < size) && (str[x + 1] == '\n')) + skip++; + break; + default: + str[x - skip] = c; + break; + } + } + return size - skip; +} + +char* ConvertLineEndingToCRLF(const char* str, size_t* size) +{ + WINPR_ASSERT(size); + const size_t s = *size; + WINPR_ASSERT(str || (s == 0)); + + *size = 0; + if (s == 0) + return NULL; + + size_t linebreaks = 0; + for (size_t x = 0; x < s - 1; x++) + { + char c = str[x]; + switch (c) + { + case '\r': + case '\n': + linebreaks++; + break; + default: + break; + } + } + char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char)); + if (!cnv) + return NULL; + + size_t pos = 0; + for (size_t x = 0; x < s; x++) + { + const char c = str[x]; + switch (c) + { + case '\r': + cnv[pos++] = '\r'; + cnv[pos++] = '\n'; + break; + case '\n': + /* Do not duplicate existing \r\n sequences */ + if ((x > 0) && (str[x - 1] != '\r')) + { + cnv[pos++] = '\r'; + cnv[pos++] = '\n'; + } + break; + default: + cnv[pos++] = c; + break; + } + } + *size = pos; + return cnv; +} + +char* StrSep(char** stringp, const char* delim) +{ + char* start = *stringp; + char* p = NULL; + p = (start != NULL) ? strpbrk(start, delim) : NULL; + + if (!p) + *stringp = NULL; + else + { + *p = '\0'; + *stringp = p + 1; + } + + return start; +} + +INT64 GetLine(char** lineptr, size_t* size, FILE* stream) +{ +#if defined(_WIN32) + char c; + char* n; + size_t step = 32; + size_t used = 0; + + if (!lineptr || !size) + { + errno = EINVAL; + return -1; + } + + do + { + if (used + 2 >= *size) + { + *size += step; + n = realloc(*lineptr, *size); + + if (!n) + { + return -1; + } + + *lineptr = n; + } + + c = fgetc(stream); + + if (c != EOF) + (*lineptr)[used++] = c; + } while ((c != '\n') && (c != '\r') && (c != EOF)); + + (*lineptr)[used] = '\0'; + return used; +#elif !defined(ANDROID) && !defined(IOS) + return getline(lineptr, size, stream); +#else + return -1; +#endif +} + +#if !defined(WINPR_HAVE_STRNDUP) +char* strndup(const char* src, size_t n) +{ + char* dst = calloc(n + 1, sizeof(char)); + if (dst) + strncpy(dst, src, n); + return dst; +} +#endif + +const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len) +{ + WINPR_ASSERT(str); + WINPR_ASSERT(buffer || (len == 0)); + ConvertUtf8ToWChar(str, buffer, len); + return buffer; +} |