FreeRDP
Loading...
Searching...
No Matches
winpr/libwinpr/crt/string.c
1
20#include <winpr/config.h>
21#include <winpr/assert.h>
22
23#include <string.h>
24#include <errno.h>
25#include <stdio.h>
26#include <ctype.h>
27#include <wctype.h>
28#include <wchar.h>
29
30#include <winpr/crt.h>
31#include <winpr/endian.h>
32
33#if defined(WITH_URIPARSER)
34#include <uriparser/Uri.h>
35#endif
36
37#if defined(WINPR_HAVE_REGCOMP)
38#include <regex.h>
39#endif
40
41/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
42
43#include "../log.h"
44#define TAG WINPR_TAG("crt")
45
46#if defined(WITH_URIPARSER)
47BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
48{
49 if (!str || (len == 0))
50 return FALSE;
51
52 const char* errorPos = nullptr;
53 UriUriA uri = WINPR_C_ARRAY_INIT;
54 const int rc = uriParseSingleUriExA(&uri, str, &str[len - 1], &errorPos);
55 uriFreeUriMembersA(&uri);
56 return rc == URI_SUCCESS;
57}
58
59BOOL winpr_str_is_valid_url(const char* str)
60{
61 if (!str)
62 return FALSE;
63
64 const char* errorPos = nullptr;
65 UriUriA uri = WINPR_C_ARRAY_INIT;
66 const int rc = uriParseSingleUriA(&uri, str, &errorPos);
67 uriFreeUriMembersA(&uri);
68 return rc == URI_SUCCESS;
69}
70
71char* winpr_str_url_decode(const char* str, size_t len)
72{
73 char* dst = strndup(str, len);
74 if (!dst)
75 return nullptr;
76
77 if (!uriUnescapeInPlaceExA(dst, URI_FALSE, URI_BR_DONT_TOUCH))
78 {
79 free(dst);
80 return nullptr;
81 }
82
83 return dst;
84}
85
86char* winpr_str_url_encode(const char* str, size_t len)
87{
88 char* dst = calloc(len + 1, sizeof(char) * 3);
89 if (!dst)
90 return nullptr;
91
92 if (!uriEscapeA(str, dst, URI_FALSE, URI_FALSE))
93 {
94 free(dst);
95 return nullptr;
96 }
97 return dst;
98}
99
100#else
101static const char rfc3986[] = {
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
103 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00,
105 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
106 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
107 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f,
108 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
109 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x7e, 0x00,
110 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
111 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
113 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
115 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
116 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118};
119
120static char hex2bin(char what)
121{
122 if (what >= 'a')
123 what -= 'a' - 'A';
124 if (what >= 'A')
125 what -= ('A' - 10);
126 else
127 what -= '0';
128 return what;
129}
130
131static char unescape(const char* what, size_t* px)
132{
133 if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2])))
134 {
135 *px += 2;
136 return 16 * hex2bin(what[1]) + hex2bin(what[2]);
137 }
138
139 return *what;
140}
141
142char* winpr_str_url_decode(const char* str, size_t len)
143{
144 char* dst = calloc(len + 1, sizeof(char));
145 if (!dst)
146 return nullptr;
147
148 size_t pos = 0;
149 for (size_t x = 0; x < strnlen(str, len); x++)
150 {
151 const char* cur = &str[x];
152 dst[pos++] = unescape(cur, &x);
153 }
154 return dst;
155}
156
157WINPR_ATTR_NODISCARD
158static char* escape(char* dst, char what)
159{
160 if (rfc3986[what & 0xff])
161 {
162 *dst = what;
163 return dst + 1;
164 }
165
166 if (sprintf(dst, "%%%02" PRIX8, (BYTE)(what & 0xff)) < 0)
167 return nullptr;
168 return dst + 3;
169}
170
171char* winpr_str_url_encode(const char* str, size_t len)
172{
173 char* dst = calloc(len + 1, sizeof(char) * 3);
174 if (!dst)
175 return nullptr;
176
177 char* ptr = dst;
178 for (size_t x = 0; x < strnlen(str, len); x++)
179 {
180 const char cur = str[x];
181 ptr = escape(ptr, cur);
182 }
183 return dst;
184}
185
186BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
187{
188 if (!str || (len == 0))
189 return FALSE;
190
191 char* url = strndup(str, len);
192 if (!url)
193 return FALSE;
194
195 const BOOL rc = winpr_str_is_valid_url(url);
196 free(url);
197 return rc;
198}
199
200#if defined(_WIN32)
201#include <shlwapi.h>
202
203BOOL winpr_str_is_valid_url(const char* str)
204{
205 if (!str)
206 return FALSE;
207 return PathIsURLA(str);
208}
209
210#elif defined(WINPR_HAVE_REGCOMP)
211BOOL winpr_str_is_valid_url(const char* str)
212{
213 if (!str)
214 return FALSE;
215 regex_t regex = WINPR_C_ARRAY_INIT;
216 const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
217
218 int ret = regcomp(&regex, pattern, REG_EXTENDED);
219 if (ret)
220 {
221 printf("Could not compile regex\n");
222 return FALSE;
223 }
224
225 ret = regexec(&regex, str, 0, nullptr, 0);
226 regfree(&regex);
227 return ret == 0;
228}
229
230#else
231BOOL winpr_str_is_valid_url(WINPR_ATTR_UNUSED const char* str)
232{
233 WLog_WARN(TAG, "URL validation not supported by your build, not performing check. Build with "
234 "-DWITH_URIPARSER=ON to fix.");
235 return TRUE;
236}
237#endif
238#endif
239
240BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
241{
242 const size_t used = strnlen(buffer, size);
243 const size_t add = strnlen(what, size);
244 const size_t sep_len = separator ? strnlen(separator, size) : 0;
245 const size_t sep = (used > 0) ? sep_len : 0;
246
247 if (used + add + sep >= size)
248 return FALSE;
249
250 if ((used > 0) && (sep_len > 0))
251 strncat(buffer, separator, sep_len);
252
253 strncat(buffer, what, add);
254 return TRUE;
255}
256
257WINPR_ATTR_FORMAT_ARG(3, 4)
258int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
259{
260 va_list ap = WINPR_C_ARRAY_INIT;
261
262 va_start(ap, templ);
263 int rc = winpr_vasprintf(s, slen, templ, ap);
264 va_end(ap);
265 return rc;
266}
267
268WINPR_ATTR_FORMAT_ARG(3, 0)
269int winpr_vasprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, va_list oap)
270{
271 va_list ap = WINPR_C_ARRAY_INIT;
272
273 *s = nullptr;
274 *slen = 0;
275
276 va_copy(ap, oap);
277 const int length = vsnprintf(nullptr, 0, templ, ap);
278 va_end(ap);
279 if (length < 0)
280 return length;
281
282 char* str = calloc((size_t)length + 1UL, sizeof(char));
283 if (!str)
284 return -1;
285
286 va_copy(ap, oap);
287 const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
288 va_end(ap);
289
290 if (length != plen)
291 {
292 free(str);
293 return -1;
294 }
295 *s = str;
296 *slen = (size_t)length;
297 return length;
298}
299
300#ifndef _WIN32
301
302char* _strdup(const char* strSource)
303{
304 if (strSource == nullptr)
305 return nullptr;
306
307 char* strDestination = strdup(strSource);
308
309 if (strDestination == nullptr)
310 WLog_ERR(TAG, "strdup");
311
312 return strDestination;
313}
314
315WCHAR* _wcsdup(const WCHAR* strSource)
316{
317 if (!strSource)
318 return nullptr;
319
320 size_t len = _wcslen(strSource);
321 WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
322
323 if (strDestination != nullptr)
324 memcpy(strDestination, strSource, len * sizeof(WCHAR));
325
326 if (strDestination == nullptr)
327 WLog_ERR(TAG, "wcsdup");
328
329 return strDestination;
330}
331
332WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
333{
334 WINPR_ASSERT(dst);
335 WINPR_ASSERT(src || (sz == 0));
336
337 const size_t dlen = _wcslen(dst);
338 const size_t slen = _wcsnlen(src, sz);
339 for (size_t x = 0; x < slen; x++)
340 dst[dlen + x] = src[x];
341 dst[dlen + slen] = '\0';
342 return dst;
343}
344
345int _stricmp(const char* string1, const char* string2)
346{
347 return strcasecmp(string1, string2);
348}
349
350int _strnicmp(const char* string1, const char* string2, size_t count)
351{
352 return strncasecmp(string1, string2, count);
353}
354
355/* _wcscmp -> wcscmp */
356
357int _wcscmp(const WCHAR* string1, const WCHAR* string2)
358{
359 WINPR_ASSERT(string1);
360 WINPR_ASSERT(string2);
361
362 while (TRUE)
363 {
364 const WCHAR w1 = *string1++;
365 const WCHAR w2 = *string2++;
366
367 if (w1 != w2)
368 return (int)w1 - w2;
369 else if ((w1 == '\0') || (w2 == '\0'))
370 return (int)w1 - w2;
371 }
372}
373
374int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
375{
376 WINPR_ASSERT(string1);
377 WINPR_ASSERT(string2);
378
379 for (size_t x = 0; x < count; x++)
380 {
381 const WCHAR a = string1[x];
382 const WCHAR b = string2[x];
383
384 if (a != b)
385 return (int)a - b;
386 else if ((a == '\0') || (b == '\0'))
387 return (int)a - b;
388 }
389 return 0;
390}
391
392/* _wcslen -> wcslen */
393
394size_t _wcslen(const WCHAR* str)
395{
396 const WCHAR* p = str;
397
398 WINPR_ASSERT(p);
399
400 while (*p)
401 p++;
402
403 return (size_t)(p - str);
404}
405
406/* _wcsnlen -> wcsnlen */
407
408size_t _wcsnlen(const WCHAR* str, size_t max)
409{
410 WINPR_ASSERT(str);
411
412 size_t x = 0;
413 for (; x < max; x++)
414 {
415 if (str[x] == 0)
416 return x;
417 }
418
419 return x;
420}
421
422/* _wcsstr -> wcsstr */
423
424WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch)
425{
426 WINPR_ASSERT(str);
427 WINPR_ASSERT(strSearch);
428
429 if (strSearch[0] == '\0')
430 return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
431
432 const size_t searchLen = _wcslen(strSearch);
433 while (*str)
434 {
435 if (_wcsncmp(str, strSearch, searchLen) == 0)
436 return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
437 str++;
438 }
439 return nullptr;
440}
441
442/* _wcschr -> wcschr */
443
444WCHAR* _wcschr(const WCHAR* str, WCHAR c)
445{
446 union
447 {
448 const WCHAR* cc;
449 WCHAR* c;
450 } cnv;
451 const WCHAR* p = str;
452
453 while (*p && (*p != c))
454 p++;
455
456 cnv.cc = (*p == c) ? p : nullptr;
457 return cnv.c;
458}
459
460/* _wcsrchr -> wcsrchr */
461
462WCHAR* _wcsrchr(const WCHAR* str, WCHAR c)
463{
464 union
465 {
466 const WCHAR* cc;
467 WCHAR* c;
468 } cnv;
469 const WCHAR* p = nullptr;
470
471 if (!str)
472 return nullptr;
473
474 for (; *str != '\0'; str++)
475 {
476 const WCHAR ch = *str;
477 if (ch == c)
478 p = str;
479 }
480
481 cnv.cc = p;
482 return cnv.c;
483}
484
485char* strtok_s(char* strToken, const char* strDelimit, char** context)
486{
487 return strtok_r(strToken, strDelimit, context);
488}
489
490WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context)
491{
492 WCHAR* nextToken = nullptr;
493 WCHAR value = 0;
494
495 if (!strToken)
496 strToken = *context;
497
498 value = *strToken;
499
500 while (*strToken && _wcschr(strDelimit, value))
501 {
502 strToken++;
503 value = *strToken;
504 }
505
506 if (!*strToken)
507 return nullptr;
508
509 nextToken = strToken++;
510 value = *strToken;
511
512 while (*strToken && !(_wcschr(strDelimit, value)))
513 {
514 strToken++;
515 value = *strToken;
516 }
517
518 if (*strToken)
519 *strToken++ = 0;
520
521 *context = strToken;
522 return nextToken;
523}
524
525#endif
526
527#if !defined(_WIN32) || defined(_UWP)
528
529/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll
530 * http://msdn.microsoft.com/en-us/library/hh802935/
531 */
532
533#include "casing.h"
534
535LPSTR CharUpperA(LPSTR lpsz)
536{
537 size_t length = 0;
538
539 if (!lpsz)
540 return nullptr;
541
542 length = strlen(lpsz);
543
544 if (length < 1)
545 return (LPSTR) nullptr;
546
547 if (length == 1)
548 {
549 char c = *lpsz;
550
551 if ((c >= 'a') && (c <= 'z'))
552 c = (char)(c - 'a' + 'A');
553
554 *lpsz = c;
555 return lpsz;
556 }
557
558 for (size_t i = 0; i < length; i++)
559 {
560 if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
561 lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
562 }
563
564 return lpsz;
565}
566
567LPWSTR CharUpperW(LPWSTR lpsz)
568{
569 size_t length = 0;
570
571 if (!lpsz)
572 return nullptr;
573
574 length = _wcslen(lpsz);
575
576 if (length < 1)
577 return (LPWSTR) nullptr;
578
579 if (length == 1)
580 {
581 WCHAR c = *lpsz;
582
583 if ((c >= L'a') && (c <= L'z'))
584 c = c - L'a' + L'A';
585
586 *lpsz = c;
587 return lpsz;
588 }
589
590 for (size_t i = 0; i < length; i++)
591 {
592 if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z'))
593 lpsz[i] = lpsz[i] - L'a' + L'A';
594 }
595
596 return lpsz;
597}
598
599DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength)
600{
601 if (cchLength < 1)
602 return 0;
603
604 for (DWORD i = 0; i < cchLength; i++)
605 {
606 if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
607 lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
608 }
609
610 return cchLength;
611}
612
613DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength)
614{
615 for (DWORD i = 0; i < cchLength; i++)
616 {
617 WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
618 value = WINPR_TOUPPERW(value);
619 winpr_Data_Write_UINT16(&lpsz[i], value);
620 }
621
622 return cchLength;
623}
624
625LPSTR CharLowerA(LPSTR lpsz)
626{
627 size_t length = 0;
628
629 if (!lpsz)
630 return (LPSTR) nullptr;
631
632 length = strlen(lpsz);
633
634 if (length < 1)
635 return (LPSTR) nullptr;
636
637 if (length == 1)
638 {
639 char c = *lpsz;
640
641 if ((c >= 'A') && (c <= 'Z'))
642 c = (char)(c - 'A' + 'a');
643
644 *lpsz = c;
645 return lpsz;
646 }
647
648 for (size_t i = 0; i < length; i++)
649 {
650 if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
651 lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
652 }
653
654 return lpsz;
655}
656
657LPWSTR CharLowerW(LPWSTR lpsz)
658{
659 const size_t len = _wcsnlen(lpsz, UINT32_MAX + 1);
660 if (len > UINT32_MAX)
661 return nullptr;
662 CharLowerBuffW(lpsz, (UINT32)len);
663 return lpsz;
664}
665
666DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength)
667{
668 if (cchLength < 1)
669 return 0;
670
671 for (DWORD i = 0; i < cchLength; i++)
672 {
673 if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
674 lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
675 }
676
677 return cchLength;
678}
679
680DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength)
681{
682 for (DWORD i = 0; i < cchLength; i++)
683 {
684 WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
685 value = WINPR_TOLOWERW(value);
686 winpr_Data_Write_UINT16(&lpsz[i], value);
687 }
688
689 return cchLength;
690}
691
692BOOL IsCharAlphaA(CHAR ch)
693{
694 if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
695 return 1;
696 else
697 return 0;
698}
699
700BOOL IsCharAlphaW(WCHAR ch)
701{
702 if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')))
703 return 1;
704 else
705 return 0;
706}
707
708BOOL IsCharAlphaNumericA(CHAR ch)
709{
710 if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
711 ((ch >= '0') && (ch <= '9')))
712 return 1;
713 else
714 return 0;
715}
716
717BOOL IsCharAlphaNumericW(WCHAR ch)
718{
719 if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) ||
720 ((ch >= L'0') && (ch <= L'9')))
721 return 1;
722 else
723 return 0;
724}
725
726BOOL IsCharUpperA(CHAR ch)
727{
728 if ((ch >= 'A') && (ch <= 'Z'))
729 return 1;
730 else
731 return 0;
732}
733
734BOOL IsCharUpperW(WCHAR ch)
735{
736 if ((ch >= L'A') && (ch <= L'Z'))
737 return 1;
738 else
739 return 0;
740}
741
742BOOL IsCharLowerA(CHAR ch)
743{
744 if ((ch >= 'a') && (ch <= 'z'))
745 return 1;
746 else
747 return 0;
748}
749
750BOOL IsCharLowerW(WCHAR ch)
751{
752 if ((ch >= L'a') && (ch <= L'z'))
753 return 1;
754 else
755 return 0;
756}
757
758#endif
759
760size_t ConvertLineEndingToLF(char* str, size_t size)
761{
762 size_t skip = 0;
763
764 WINPR_ASSERT(str || (size == 0));
765 for (size_t x = 0; x < size; x++)
766 {
767 char c = str[x];
768 switch (c)
769 {
770 case '\r':
771 str[x - skip] = '\n';
772 if ((x + 1 < size) && (str[x + 1] == '\n'))
773 skip++;
774 break;
775 default:
776 str[x - skip] = c;
777 break;
778 }
779 }
780 return size - skip;
781}
782
783char* ConvertLineEndingToCRLF(const char* str, size_t* size)
784{
785 WINPR_ASSERT(size);
786 const size_t s = *size;
787 WINPR_ASSERT(str || (s == 0));
788
789 *size = 0;
790 if (s == 0)
791 return nullptr;
792
793 size_t linebreaks = 0;
794 for (size_t x = 0; x < s - 1; x++)
795 {
796 char c = str[x];
797 switch (c)
798 {
799 case '\r':
800 case '\n':
801 linebreaks++;
802 break;
803 default:
804 break;
805 }
806 }
807 char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
808 if (!cnv)
809 return nullptr;
810
811 size_t pos = 0;
812 for (size_t x = 0; x < s; x++)
813 {
814 const char c = str[x];
815 switch (c)
816 {
817 case '\r':
818 cnv[pos++] = '\r';
819 cnv[pos++] = '\n';
820 break;
821 case '\n':
822 /* Do not duplicate existing \r\n sequences */
823 if ((x > 0) && (str[x - 1] != '\r'))
824 {
825 cnv[pos++] = '\r';
826 cnv[pos++] = '\n';
827 }
828 break;
829 default:
830 cnv[pos++] = c;
831 break;
832 }
833 }
834 *size = pos;
835 return cnv;
836}
837
838char* StrSep(char** stringp, const char* delim)
839{
840 char* start = *stringp;
841 char* p = nullptr;
842 p = (start != nullptr) ? strpbrk(start, delim) : nullptr;
843
844 if (!p)
845 *stringp = nullptr;
846 else
847 {
848 *p = '\0';
849 *stringp = p + 1;
850 }
851
852 return start;
853}
854
855INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
856{
857#if defined(_WIN32)
858 char c;
859 char* n;
860 size_t step = 32;
861 size_t used = 0;
862
863 if (!lineptr || !size)
864 {
865 errno = EINVAL;
866 return -1;
867 }
868
869 do
870 {
871 if (used + 2 >= *size)
872 {
873 *size += step;
874 n = realloc(*lineptr, *size);
875
876 if (!n)
877 {
878 return -1;
879 }
880
881 *lineptr = n;
882 }
883
884 c = fgetc(stream);
885
886 if (c != EOF)
887 (*lineptr)[used++] = c;
888 } while ((c != '\n') && (c != '\r') && (c != EOF));
889
890 (*lineptr)[used] = '\0';
891 return used;
892#elif !defined(ANDROID) && !defined(IOS)
893 return getline(lineptr, size, stream);
894#else
895 return -1;
896#endif
897}
898
899#if !defined(WINPR_HAVE_STRNDUP)
900char* strndup(const char* src, size_t n)
901{
902 char* dst = calloc(n + 1, sizeof(char));
903 if (dst)
904 strncpy(dst, src, n);
905 return dst;
906}
907#endif
908
909const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len)
910{
911 WINPR_ASSERT(str);
912 WINPR_ASSERT(buffer || (len == 0));
913 (void)ConvertUtf8ToWChar(str, buffer, len);
914 return buffer;
915}
916
917WCHAR* wcsndup(const WCHAR* s, size_t n)
918{
919 if (!s)
920 return nullptr;
921
922 WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
923 if (!copy)
924 return nullptr;
925 memcpy(copy, s, n * sizeof(WCHAR));
926 return copy;
927}
928
929char* winpr_strnstr(char* haystack, const char* needle, size_t hlen)
930{
931 WINPR_ASSERT(haystack || (hlen == 0));
932 WINPR_ASSERT(needle);
933
934#if defined(WINPR_HAVE_STRNSTR)
935 return strnstr(haystack, needle, hlen);
936#else
937 /* Match native strnstr semantics: search at most hlen characters and stop at
938 * the haystack NUL terminator, whichever comes first. */
939 const size_t needle_len = strlen(needle);
940
941 if (0 == needle_len)
942 return haystack;
943
944 for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
945 {
946 if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
947 return haystack;
948 }
949 return nullptr;
950#endif
951}
952
953BOOL winpr_str_has_newlines(const char* str)
954{
955 if (!str)
956 return FALSE;
957 do
958 {
959 char c = *str++;
960 switch (c)
961 {
962 case '\r':
963 case '\n':
964 case 0x0b: /* VT vertical tab */
965 case 0x0c: /* FF form feed */
966 case (char)0x85: /* NEL next line */
967 return TRUE;
968 case (char)0xE2:
969 {
970 if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
971 return TRUE;
972 }
973 break;
974
975 case '\0':
976 return FALSE;
977 default:
978 break;
979 }
980 } while (1);
981}