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