Coverage Report

Created: 2026-10-15 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/crt/string.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * String Manipulation (CRT)
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
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)
47
BOOL 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
59
BOOL 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
71
char* 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
86
char* 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
101
static 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
120
static char hex2bin(char what)
121
7.36k
{
122
7.36k
  if (what >= 'a')
123
5.87k
    what -= 'a' - 'A';
124
7.36k
  if (what >= 'A')
125
6.08k
    what -= ('A' - 10);
126
1.27k
  else
127
1.27k
    what -= '0';
128
7.36k
  return what;
129
7.36k
}
130
131
static char unescape(const char* what, size_t* px)
132
14.3M
{
133
14.3M
  if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2])))
134
3.68k
  {
135
3.68k
    *px += 2;
136
3.68k
    return 16 * hex2bin(what[1]) + hex2bin(what[2]);
137
3.68k
  }
138
139
14.3M
  return *what;
140
14.3M
}
141
142
char* winpr_str_url_decode(const char* str, size_t len)
143
9.51k
{
144
9.51k
  char* dst = calloc(len + 1, sizeof(char));
145
9.51k
  if (!dst)
146
0
    return nullptr;
147
148
9.51k
  size_t pos = 0;
149
14.3M
  for (size_t x = 0; x < strnlen(str, len); x++)
150
14.3M
  {
151
14.3M
    const char* cur = &str[x];
152
14.3M
    dst[pos++] = unescape(cur, &x);
153
14.3M
  }
154
9.51k
  return dst;
155
9.51k
}
156
157
WINPR_ATTR_NODISCARD
158
static char* escape(char* dst, char what)
159
0
{
160
0
  if (rfc3986[what & 0xff])
161
0
  {
162
0
    *dst = what;
163
0
    return dst + 1;
164
0
  }
165
166
0
  if (sprintf(dst, "%%%02" PRIX8, (BYTE)(what & 0xff)) < 0)
167
0
    return nullptr;
168
0
  return dst + 3;
169
0
}
170
171
char* winpr_str_url_encode(const char* str, size_t len)
172
0
{
173
0
  char* dst = calloc(len + 1, sizeof(char) * 3);
174
0
  if (!dst)
175
0
    return nullptr;
176
177
0
  char* ptr = dst;
178
0
  for (size_t x = 0; x < strnlen(str, len); x++)
179
0
  {
180
0
    const char cur = str[x];
181
0
    ptr = escape(ptr, cur);
182
0
  }
183
0
  return dst;
184
0
}
185
186
BOOL winpr_str_is_valid_urlN(const char* str, size_t len)
187
0
{
188
0
  if (!str || (len == 0))
189
0
    return FALSE;
190
191
0
  char* url = strndup(str, len);
192
0
  if (!url)
193
0
    return FALSE;
194
195
0
  const BOOL rc = winpr_str_is_valid_url(url);
196
0
  free(url);
197
0
  return rc;
198
0
}
199
200
#if defined(_WIN32)
201
#include <shlwapi.h>
202
203
BOOL 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)
211
BOOL winpr_str_is_valid_url(const char* str)
212
0
{
213
0
  if (!str)
214
0
    return FALSE;
215
0
  regex_t regex = WINPR_C_ARRAY_INIT;
216
0
  const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
217
218
0
  int ret = regcomp(&regex, pattern, REG_EXTENDED);
219
0
  if (ret)
220
0
  {
221
0
    printf("Could not compile regex\n");
222
0
    return FALSE;
223
0
  }
224
225
0
  ret = regexec(&regex, str, 0, nullptr, 0);
226
0
  regfree(&regex);
227
0
  return ret == 0;
228
0
}
229
230
#else
231
BOOL 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
240
BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
241
151k
{
242
151k
  const size_t used = strnlen(buffer, size);
243
151k
  const size_t add = strnlen(what, size);
244
151k
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
245
151k
  const size_t sep = (used > 0) ? sep_len : 0;
246
247
151k
  if (used + add + sep >= size)
248
14
    return FALSE;
249
250
151k
  if ((used > 0) && (sep_len > 0))
251
23.7k
    strncat(buffer, separator, sep_len);
252
253
151k
  strncat(buffer, what, add);
254
151k
  return TRUE;
255
151k
}
256
257
WINPR_ATTR_FORMAT_ARG(3, 4)
258
int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
259
1.23M
{
260
1.23M
  va_list ap = WINPR_C_ARRAY_INIT;
261
262
1.23M
  va_start(ap, templ);
263
1.23M
  int rc = winpr_vasprintf(s, slen, templ, ap);
264
1.23M
  va_end(ap);
265
1.23M
  return rc;
266
1.23M
}
267
268
WINPR_ATTR_FORMAT_ARG(3, 0)
269
int winpr_vasprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, va_list oap)
270
2.68M
{
271
2.68M
  va_list ap = WINPR_C_ARRAY_INIT;
272
273
2.68M
  *s = nullptr;
274
2.68M
  *slen = 0;
275
276
2.68M
  va_copy(ap, oap);
277
2.68M
  const int length = vsnprintf(nullptr, 0, templ, ap);
278
2.68M
  va_end(ap);
279
2.68M
  if (length < 0)
280
0
    return length;
281
282
2.68M
  char* str = calloc((size_t)length + 1UL, sizeof(char));
283
2.68M
  if (!str)
284
0
    return -1;
285
286
2.68M
  va_copy(ap, oap);
287
2.68M
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
288
2.68M
  va_end(ap);
289
290
2.68M
  if (length != plen)
291
0
  {
292
0
    free(str);
293
0
    return -1;
294
0
  }
295
2.68M
  *s = str;
296
2.68M
  *slen = (size_t)length;
297
2.68M
  return length;
298
2.68M
}
299
300
#ifndef _WIN32
301
302
char* _strdup(const char* strSource)
303
2.96M
{
304
2.96M
  if (strSource == nullptr)
305
0
    return nullptr;
306
307
2.96M
  char* strDestination = strdup(strSource);
308
309
2.96M
  if (strDestination == nullptr)
310
0
    WLog_ERR(TAG, "strdup");
311
312
2.96M
  return strDestination;
313
2.96M
}
314
315
WCHAR* _wcsdup(const WCHAR* strSource)
316
1.15M
{
317
1.15M
  if (!strSource)
318
0
    return nullptr;
319
320
1.15M
  size_t len = _wcslen(strSource);
321
1.15M
  WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
322
323
1.15M
  if (strDestination != nullptr)
324
1.15M
    memcpy(strDestination, strSource, len * sizeof(WCHAR));
325
326
1.15M
  if (strDestination == nullptr)
327
0
    WLog_ERR(TAG, "wcsdup");
328
329
1.15M
  return strDestination;
330
1.15M
}
331
332
WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
333
110k
{
334
110k
  WINPR_ASSERT(dst);
335
110k
  WINPR_ASSERT(src || (sz == 0));
336
337
110k
  const size_t dlen = _wcslen(dst);
338
110k
  const size_t slen = _wcsnlen(src, sz);
339
11.6M
  for (size_t x = 0; x < slen; x++)
340
11.5M
    dst[dlen + x] = src[x];
341
110k
  dst[dlen + slen] = '\0';
342
110k
  return dst;
343
110k
}
344
345
int _stricmp(const char* string1, const char* string2)
346
74.6M
{
347
74.6M
  return strcasecmp(string1, string2);
348
74.6M
}
349
350
int _strnicmp(const char* string1, const char* string2, size_t count)
351
1.99k
{
352
1.99k
  return strncasecmp(string1, string2, count);
353
1.99k
}
354
355
/* _wcscmp -> wcscmp */
356
357
int _wcscmp(const WCHAR* string1, const WCHAR* string2)
358
916k
{
359
916k
  WINPR_ASSERT(string1);
360
916k
  WINPR_ASSERT(string2);
361
362
1.13M
  while (TRUE)
363
1.13M
  {
364
1.13M
    const WCHAR w1 = *string1++;
365
1.13M
    const WCHAR w2 = *string2++;
366
367
1.13M
    if (w1 != w2)
368
806k
      return (int)w1 - w2;
369
330k
    else if ((w1 == '\0') || (w2 == '\0'))
370
110k
      return (int)w1 - w2;
371
1.13M
  }
372
916k
}
373
374
int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
375
0
{
376
0
  WINPR_ASSERT(string1);
377
0
  WINPR_ASSERT(string2);
378
379
0
  for (size_t x = 0; x < count; x++)
380
0
  {
381
0
    const WCHAR a = string1[x];
382
0
    const WCHAR b = string2[x];
383
384
0
    if (a != b)
385
0
      return (int)a - b;
386
0
    else if ((a == '\0') || (b == '\0'))
387
0
      return (int)a - b;
388
0
  }
389
0
  return 0;
390
0
}
391
392
/* _wcslen -> wcslen */
393
394
size_t _wcslen(const WCHAR* str)
395
4.08M
{
396
4.08M
  const WCHAR* p = str;
397
398
4.08M
  WINPR_ASSERT(p);
399
400
265M
  while (*p)
401
261M
    p++;
402
403
4.08M
  return (size_t)(p - str);
404
4.08M
}
405
406
/* _wcsnlen -> wcsnlen */
407
408
size_t _wcsnlen(const WCHAR* str, size_t max)
409
4.36M
{
410
4.36M
  WINPR_ASSERT(str);
411
412
4.36M
  size_t x = 0;
413
207M
  for (; x < max; x++)
414
206M
  {
415
206M
    if (str[x] == 0)
416
4.03M
      return x;
417
206M
  }
418
419
329k
  return x;
420
4.36M
}
421
422
/* _wcsstr -> wcsstr */
423
424
WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch)
425
0
{
426
0
  WINPR_ASSERT(str);
427
0
  WINPR_ASSERT(strSearch);
428
429
0
  if (strSearch[0] == '\0')
430
0
    return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
431
432
0
  const size_t searchLen = _wcslen(strSearch);
433
0
  while (*str)
434
0
  {
435
0
    if (_wcsncmp(str, strSearch, searchLen) == 0)
436
0
      return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
437
0
    str++;
438
0
  }
439
0
  return nullptr;
440
0
}
441
442
/* _wcschr -> wcschr */
443
#if defined(WITHOUT_WINPR_3x_DEPRECATED)
444
static
445
#endif
446
    WCHAR* _wcschr(const WCHAR* str, WCHAR c)
447
0
{
448
0
  union
449
0
  {
450
0
    const WCHAR* cc;
451
0
    WCHAR* c;
452
0
  } cnv;
453
0
  const WCHAR* p = str;
454
455
0
  while (*p && (*p != c))
456
0
    p++;
457
458
0
  cnv.cc = (*p == c) ? p : nullptr;
459
0
  return cnv.c;
460
0
}
461
462
/* _wcsrchr -> wcsrchr */
463
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
464
WCHAR* _wcsrchr(const WCHAR* str, WCHAR c)
465
0
{
466
0
  union
467
0
  {
468
0
    const WCHAR* cc;
469
0
    WCHAR* c;
470
0
  } cnv;
471
0
  const WCHAR* p = nullptr;
472
473
0
  if (!str)
474
0
    return nullptr;
475
476
0
  for (; *str != '\0'; str++)
477
0
  {
478
0
    const WCHAR ch = *str;
479
0
    if (ch == c)
480
0
      p = str;
481
0
  }
482
483
0
  cnv.cc = p;
484
0
  return cnv.c;
485
0
}
486
#endif
487
488
char* strtok_s(char* strToken, const char* strDelimit, char** context)
489
1.79M
{
490
1.79M
  return strtok_r(strToken, strDelimit, context);
491
1.79M
}
492
493
WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context)
494
0
{
495
0
  WCHAR* nextToken = nullptr;
496
0
  WCHAR value = 0;
497
498
0
  if (!strToken)
499
0
    strToken = *context;
500
501
0
  value = *strToken;
502
503
0
  while (*strToken && _wcschr(strDelimit, value))
504
0
  {
505
0
    strToken++;
506
0
    value = *strToken;
507
0
  }
508
509
0
  if (!*strToken)
510
0
    return nullptr;
511
512
0
  nextToken = strToken++;
513
0
  value = *strToken;
514
515
0
  while (*strToken && !(_wcschr(strDelimit, value)))
516
0
  {
517
0
    strToken++;
518
0
    value = *strToken;
519
0
  }
520
521
0
  if (*strToken)
522
0
    *strToken++ = 0;
523
524
0
  *context = strToken;
525
0
  return nextToken;
526
0
}
527
528
#endif
529
530
#if !defined(_WIN32) || defined(_UWP)
531
532
/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll
533
 * http://msdn.microsoft.com/en-us/library/hh802935/
534
 */
535
536
#include "casing.h"
537
538
LPSTR CharUpperA(LPSTR lpsz)
539
7.81k
{
540
7.81k
  size_t length = 0;
541
542
7.81k
  if (!lpsz)
543
0
    return nullptr;
544
545
7.81k
  length = strlen(lpsz);
546
547
7.81k
  if (length < 1)
548
0
    return (LPSTR) nullptr;
549
550
7.81k
  if (length == 1)
551
0
  {
552
0
    char c = *lpsz;
553
554
0
    if ((c >= 'a') && (c <= 'z'))
555
0
      c = (char)(c - 'a' + 'A');
556
557
0
    *lpsz = c;
558
0
    return lpsz;
559
0
  }
560
561
101k
  for (size_t i = 0; i < length; i++)
562
93.7k
  {
563
93.7k
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
564
31.2k
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
565
93.7k
  }
566
567
7.81k
  return lpsz;
568
7.81k
}
569
570
LPWSTR CharUpperW(LPWSTR lpsz)
571
0
{
572
0
  size_t length = 0;
573
574
0
  if (!lpsz)
575
0
    return nullptr;
576
577
0
  length = _wcslen(lpsz);
578
579
0
  if (length < 1)
580
0
    return (LPWSTR) nullptr;
581
582
0
  if (length == 1)
583
0
  {
584
0
    WCHAR c = *lpsz;
585
586
0
    if ((c >= L'a') && (c <= L'z'))
587
0
      c = c - L'a' + L'A';
588
589
0
    *lpsz = c;
590
0
    return lpsz;
591
0
  }
592
593
0
  for (size_t i = 0; i < length; i++)
594
0
  {
595
0
    if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z'))
596
0
      lpsz[i] = lpsz[i] - L'a' + L'A';
597
0
  }
598
599
0
  return lpsz;
600
0
}
601
602
DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength)
603
0
{
604
0
  if (cchLength < 1)
605
0
    return 0;
606
607
0
  for (DWORD i = 0; i < cchLength; i++)
608
0
  {
609
0
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
610
0
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
611
0
  }
612
613
0
  return cchLength;
614
0
}
615
616
DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength)
617
195
{
618
1.75k
  for (DWORD i = 0; i < cchLength; i++)
619
1.56k
  {
620
1.56k
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
621
1.56k
    value = WINPR_TOUPPERW(value);
622
1.56k
    winpr_Data_Write_UINT16(&lpsz[i], value);
623
1.56k
  }
624
625
195
  return cchLength;
626
195
}
627
628
LPSTR CharLowerA(LPSTR lpsz)
629
0
{
630
0
  size_t length = 0;
631
632
0
  if (!lpsz)
633
0
    return (LPSTR) nullptr;
634
635
0
  length = strlen(lpsz);
636
637
0
  if (length < 1)
638
0
    return (LPSTR) nullptr;
639
640
0
  if (length == 1)
641
0
  {
642
0
    char c = *lpsz;
643
644
0
    if ((c >= 'A') && (c <= 'Z'))
645
0
      c = (char)(c - 'A' + 'a');
646
647
0
    *lpsz = c;
648
0
    return lpsz;
649
0
  }
650
651
0
  for (size_t i = 0; i < length; i++)
652
0
  {
653
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
654
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
655
0
  }
656
657
0
  return lpsz;
658
0
}
659
660
LPWSTR CharLowerW(LPWSTR lpsz)
661
0
{
662
0
  const size_t len = _wcsnlen(lpsz, UINT32_MAX + 1);
663
0
  if (len > UINT32_MAX)
664
0
    return nullptr;
665
0
  CharLowerBuffW(lpsz, (UINT32)len);
666
0
  return lpsz;
667
0
}
668
669
DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength)
670
0
{
671
0
  if (cchLength < 1)
672
0
    return 0;
673
674
0
  for (DWORD i = 0; i < cchLength; i++)
675
0
  {
676
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
677
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
678
0
  }
679
680
0
  return cchLength;
681
0
}
682
683
DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength)
684
0
{
685
0
  for (DWORD i = 0; i < cchLength; i++)
686
0
  {
687
0
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
688
0
    value = WINPR_TOLOWERW(value);
689
0
    winpr_Data_Write_UINT16(&lpsz[i], value);
690
0
  }
691
692
0
  return cchLength;
693
0
}
694
695
BOOL IsCharAlphaA(CHAR ch)
696
0
{
697
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
698
0
    return 1;
699
0
  else
700
0
    return 0;
701
0
}
702
703
BOOL IsCharAlphaW(WCHAR ch)
704
0
{
705
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')))
706
0
    return 1;
707
0
  else
708
0
    return 0;
709
0
}
710
711
BOOL IsCharAlphaNumericA(CHAR ch)
712
0
{
713
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
714
0
      ((ch >= '0') && (ch <= '9')))
715
0
    return 1;
716
0
  else
717
0
    return 0;
718
0
}
719
720
BOOL IsCharAlphaNumericW(WCHAR ch)
721
0
{
722
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) ||
723
0
      ((ch >= L'0') && (ch <= L'9')))
724
0
    return 1;
725
0
  else
726
0
    return 0;
727
0
}
728
729
BOOL IsCharUpperA(CHAR ch)
730
0
{
731
0
  if ((ch >= 'A') && (ch <= 'Z'))
732
0
    return 1;
733
0
  else
734
0
    return 0;
735
0
}
736
737
BOOL IsCharUpperW(WCHAR ch)
738
0
{
739
0
  if ((ch >= L'A') && (ch <= L'Z'))
740
0
    return 1;
741
0
  else
742
0
    return 0;
743
0
}
744
745
BOOL IsCharLowerA(CHAR ch)
746
0
{
747
0
  if ((ch >= 'a') && (ch <= 'z'))
748
0
    return 1;
749
0
  else
750
0
    return 0;
751
0
}
752
753
BOOL IsCharLowerW(WCHAR ch)
754
0
{
755
0
  if ((ch >= L'a') && (ch <= L'z'))
756
0
    return 1;
757
0
  else
758
0
    return 0;
759
0
}
760
761
#endif
762
763
size_t ConvertLineEndingToLF(char* str, size_t size)
764
3.80k
{
765
3.80k
  size_t skip = 0;
766
767
3.80k
  WINPR_ASSERT(str || (size == 0));
768
110M
  for (size_t x = 0; x < size; x++)
769
110M
  {
770
110M
    char c = str[x];
771
110M
    switch (c)
772
110M
    {
773
186k
      case '\r':
774
186k
        str[x - skip] = '\n';
775
186k
        if ((x + 1 < size) && (str[x + 1] == '\n'))
776
9.46k
          skip++;
777
186k
        break;
778
110M
      default:
779
110M
        str[x - skip] = c;
780
110M
        break;
781
110M
    }
782
110M
  }
783
3.80k
  if (skip > 0)
784
95
    str[size - skip] = '\0';
785
3.80k
  return size - skip;
786
3.80k
}
787
788
char* ConvertLineEndingToCRLF(const char* str, size_t* size)
789
409
{
790
409
  WINPR_ASSERT(size);
791
409
  const size_t s = *size;
792
409
  WINPR_ASSERT(str || (s == 0));
793
794
409
  *size = 0;
795
409
  if (s == 0)
796
2
    return nullptr;
797
798
407
  size_t linebreaks = 0;
799
9.23M
  for (size_t x = 0; x < s; x++)
800
9.23M
  {
801
9.23M
    char c = str[x];
802
9.23M
    switch (c)
803
9.23M
    {
804
1.19k
      case '\r':
805
5.18M
      case '\n':
806
5.18M
        linebreaks++;
807
5.18M
        break;
808
4.04M
      default:
809
4.04M
        break;
810
9.23M
    }
811
9.23M
  }
812
407
  char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
813
407
  if (!cnv)
814
0
    return nullptr;
815
816
407
  size_t pos = 0;
817
9.23M
  for (size_t x = 0; x < s; x++)
818
9.23M
  {
819
9.23M
    const char c = str[x];
820
9.23M
    switch (c)
821
9.23M
    {
822
1.19k
      case '\r':
823
1.19k
        cnv[pos++] = '\r';
824
1.19k
        cnv[pos++] = '\n';
825
1.19k
        break;
826
5.18M
      case '\n':
827
        /* Do not duplicate existing \r\n sequences */
828
5.18M
        if ((x == 0) || (str[x - 1] != '\r'))
829
5.18M
        {
830
5.18M
          cnv[pos++] = '\r';
831
5.18M
          cnv[pos++] = '\n';
832
5.18M
        }
833
5.18M
        break;
834
4.04M
      default:
835
4.04M
        cnv[pos++] = c;
836
4.04M
        break;
837
9.23M
    }
838
9.23M
  }
839
407
  *size = pos;
840
407
  return cnv;
841
407
}
842
843
char* StrSep(char** stringp, const char* delim)
844
0
{
845
0
  char* start = *stringp;
846
0
  char* p = nullptr;
847
0
  p = (start != nullptr) ? strpbrk(start, delim) : nullptr;
848
849
0
  if (!p)
850
0
    *stringp = nullptr;
851
0
  else
852
0
  {
853
0
    *p = '\0';
854
0
    *stringp = p + 1;
855
0
  }
856
857
0
  return start;
858
0
}
859
860
INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
861
0
{
862
#if defined(_WIN32)
863
  char c;
864
  char* n;
865
  size_t step = 32;
866
  size_t used = 0;
867
868
  if (!lineptr || !size)
869
  {
870
    errno = EINVAL;
871
    return -1;
872
  }
873
874
  do
875
  {
876
    if (used + 2 >= *size)
877
    {
878
      *size += step;
879
      n = realloc(*lineptr, *size);
880
881
      if (!n)
882
      {
883
        return -1;
884
      }
885
886
      *lineptr = n;
887
    }
888
889
    c = fgetc(stream);
890
891
    if (c != EOF)
892
      (*lineptr)[used++] = c;
893
  } while ((c != '\n') && (c != '\r') && (c != EOF));
894
895
  (*lineptr)[used] = '\0';
896
  return used;
897
#elif !defined(ANDROID) && !defined(IOS)
898
  return getline(lineptr, size, stream);
899
#else
900
  return -1;
901
#endif
902
0
}
903
904
#if !defined(WINPR_HAVE_STRNDUP)
905
char* strndup(const char* src, size_t n)
906
{
907
  char* dst = calloc(n + 1, sizeof(char));
908
  if (dst)
909
    strncpy(dst, src, n);
910
  return dst;
911
}
912
#endif
913
914
const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len)
915
971k
{
916
971k
  WINPR_ASSERT(str);
917
971k
  WINPR_ASSERT(buffer || (len == 0));
918
971k
  (void)ConvertUtf8ToWChar(str, buffer, len);
919
971k
  return buffer;
920
971k
}
921
922
WCHAR* wcsndup(const WCHAR* s, size_t n)
923
181
{
924
181
  if (!s)
925
0
    return nullptr;
926
927
181
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
928
181
  if (!copy)
929
0
    return nullptr;
930
181
  memcpy(copy, s, n * sizeof(WCHAR));
931
181
  return copy;
932
181
}
933
934
char* winpr_strnstr(const char* haystack, const char* needle, size_t hlen)
935
19.4M
{
936
19.4M
  WINPR_ASSERT(haystack || (hlen == 0));
937
19.4M
  WINPR_ASSERT(needle);
938
939
#if defined(WINPR_HAVE_STRNSTR)
940
  return strnstr(haystack, needle, hlen);
941
#else
942
  /* Match native strnstr semantics: search at most hlen characters and stop at
943
   * the haystack NUL terminator, whichever comes first. */
944
19.4M
  const size_t needle_len = strlen(needle);
945
946
19.4M
  if (0 == needle_len)
947
0
    return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
948
949
225M
  for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
950
206M
  {
951
206M
    if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
952
460k
      return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
953
206M
  }
954
19.0M
  return nullptr;
955
19.4M
#endif
956
19.4M
}
957
958
BOOL winpr_str_has_newlines(const char* str)
959
54
{
960
54
  if (!str)
961
0
    return FALSE;
962
54
  do
963
1.41k
  {
964
1.41k
    char c = *str++;
965
1.41k
    switch (c)
966
1.41k
    {
967
1
      case '\r':
968
3
      case '\n':
969
6
      case 0x0b:       /* VT vertical tab */
970
7
      case 0x0c:       /* FF form feed */
971
13
      case (char)0x85: /* NEL next line */
972
13
        return TRUE;
973
194
      case (char)0xE2:
974
194
      {
975
194
        if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
976
2
          return TRUE;
977
194
      }
978
192
      break;
979
980
192
      case '\0':
981
39
        return FALSE;
982
1.16k
      default:
983
1.16k
        break;
984
1.41k
    }
985
1.41k
  } while (1);
986
54
}
987
988
WCHAR* winpr_wcsnchr(const WCHAR* str, size_t charLen, WCHAR c)
989
0
{
990
0
  if (!str || (charLen == 0))
991
0
    return nullptr;
992
993
0
  const WCHAR* p = str;
994
0
  const WCHAR* end = &str[charLen];
995
996
0
  while (p != end)
997
0
  {
998
0
    const WCHAR wc = winpr_Data_Get_UINT16(p);
999
0
    if (wc == c)
1000
0
    {
1001
0
      union
1002
0
      {
1003
0
        WCHAR* w;
1004
0
        const WCHAR* cw;
1005
0
      } cnv;
1006
0
      cnv.cw = p;
1007
0
      return cnv.w;
1008
0
    }
1009
0
    p++;
1010
0
  }
1011
0
  return nullptr;
1012
0
}
1013
1014
WCHAR* winpr_wcsnrchr(const WCHAR* str, size_t charLen, WCHAR c)
1015
0
{
1016
0
  if (!str || (charLen == 0))
1017
0
    return nullptr;
1018
1019
0
  const WCHAR* p = str;
1020
0
  const WCHAR* end = &str[charLen];
1021
1022
0
  union
1023
0
  {
1024
0
    WCHAR* w;
1025
0
    const WCHAR* cw;
1026
0
  } cnv;
1027
1028
0
  cnv.cw = nullptr;
1029
0
  while (p != end)
1030
0
  {
1031
0
    const WCHAR wc = winpr_Data_Get_UINT16(p);
1032
0
    if (wc == c)
1033
0
      cnv.cw = p;
1034
0
    p++;
1035
0
  }
1036
0
  return cnv.w;
1037
0
}
1038
1039
WINPR_ATTR_NODISCARD
1040
static BOOL validHostChar(char c)
1041
13
{
1042
13
  if ((c >= 'a') && (c <= 'z'))
1043
2
    return TRUE;
1044
11
  if ((c >= 'A') && (c <= 'Z'))
1045
2
    return TRUE;
1046
9
  if ((c >= '0') && (c <= '9'))
1047
2
    return TRUE;
1048
1049
7
  switch (c)
1050
7
  {
1051
0
    case '-':
1052
0
    case '.':
1053
0
      return TRUE;
1054
7
    default:
1055
7
      return FALSE;
1056
7
  }
1057
7
}
1058
1059
BOOL winpr_str_is_valid_hostname(const char* str)
1060
17
{
1061
17
  if (!str)
1062
0
    return FALSE;
1063
  /* do a limited strnlen with DNS max length + 1, so we can abort early.
1064
   */
1065
17
  const size_t len = strnlen(str, 256);
1066
17
  return winpr_str_is_valid_hostnameN(str, len);
1067
17
}
1068
1069
BOOL winpr_str_is_valid_hostnameN(const char* str, size_t len)
1070
17
{
1071
17
  if (!str || (len == 0))
1072
4
    return FALSE;
1073
1074
13
  if (len > 255)
1075
0
    return FALSE;
1076
1077
  // may not start or end with dot
1078
13
  if (str[0] == '.')
1079
0
    return FALSE;
1080
13
  if (str[len - 1] == '.')
1081
0
    return FALSE;
1082
1083
13
  size_t segment = 0;
1084
13
  BOOL segmentHasHyphen = FALSE;
1085
19
  for (size_t x = 0; x < len; x++)
1086
13
  {
1087
13
    char c = str[x];
1088
13
    if (!validHostChar(c))
1089
7
      return FALSE;
1090
1091
6
    if (c == '.')
1092
0
    {
1093
0
      segment = 0;
1094
0
      segmentHasHyphen = FALSE;
1095
1096
      // may not start with dot
1097
0
      if (x == 0)
1098
0
        return FALSE;
1099
      // filter dotdot
1100
0
      else if (str[x - 1] == c)
1101
0
        return FALSE;
1102
      // segments may not end with a hyphen
1103
0
      else if (str[x - 1] == '-')
1104
0
        return FALSE;
1105
0
    }
1106
6
    else
1107
6
    {
1108
6
      segment++;
1109
6
      if (segment > 63)
1110
0
        return FALSE;
1111
1112
      // - may not occur as first in segment
1113
6
      if (c == '-')
1114
0
      {
1115
0
        segmentHasHyphen = TRUE;
1116
1117
        // segments may not start with a hyphen
1118
0
        if ((x == 0) || (str[x - 1] == '.'))
1119
0
          return FALSE;
1120
0
      }
1121
6
    }
1122
6
  }
1123
1124
  // last segment may not contain a hyphen
1125
6
  if (segmentHasHyphen)
1126
0
    return FALSE;
1127
1128
  // assume TLD is at least 2 char
1129
6
  if (segment < 2)
1130
6
    return FALSE;
1131
1132
0
  return TRUE;
1133
6
}