Coverage Report

Created: 2026-10-28 06:54

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
1.03k
{
122
1.03k
  if (what >= 'a')
123
408
    what -= 'a' - 'A';
124
1.03k
  if (what >= 'A')
125
410
    what -= ('A' - 10);
126
628
  else
127
628
    what -= '0';
128
1.03k
  return what;
129
1.03k
}
130
131
static char unescape(const char* what, size_t* px)
132
21.0M
{
133
21.0M
  if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2])))
134
519
  {
135
519
    *px += 2;
136
519
    return 16 * hex2bin(what[1]) + hex2bin(what[2]);
137
519
  }
138
139
21.0M
  return *what;
140
21.0M
}
141
142
char* winpr_str_url_decode(const char* str, size_t len)
143
9.26k
{
144
9.26k
  char* dst = calloc(len + 1, sizeof(char));
145
9.26k
  if (!dst)
146
0
    return nullptr;
147
148
9.26k
  size_t pos = 0;
149
21.0M
  for (size_t x = 0; x < strnlen(str, len); x++)
150
21.0M
  {
151
21.0M
    const char* cur = &str[x];
152
21.0M
    dst[pos++] = unescape(cur, &x);
153
21.0M
  }
154
9.26k
  return dst;
155
9.26k
}
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
167k
{
242
167k
  const size_t used = strnlen(buffer, size);
243
167k
  const size_t add = strnlen(what, size);
244
167k
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
245
167k
  const size_t sep = (used > 0) ? sep_len : 0;
246
247
167k
  if (used + add + sep >= size)
248
29
    return FALSE;
249
250
166k
  if ((used > 0) && (sep_len > 0))
251
27.3k
    strncat(buffer, separator, sep_len);
252
253
166k
  strncat(buffer, what, add);
254
166k
  return TRUE;
255
167k
}
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.40M
{
260
1.40M
  va_list ap = WINPR_C_ARRAY_INIT;
261
262
1.40M
  va_start(ap, templ);
263
1.40M
  int rc = winpr_vasprintf(s, slen, templ, ap);
264
1.40M
  va_end(ap);
265
1.40M
  return rc;
266
1.40M
}
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
3.03M
{
271
3.03M
  va_list ap = WINPR_C_ARRAY_INIT;
272
273
3.03M
  *s = nullptr;
274
3.03M
  *slen = 0;
275
276
3.03M
  va_copy(ap, oap);
277
3.03M
  const int length = vsnprintf(nullptr, 0, templ, ap);
278
3.03M
  va_end(ap);
279
3.03M
  if (length < 0)
280
0
    return length;
281
282
3.03M
  char* str = calloc((size_t)length + 1UL, sizeof(char));
283
3.03M
  if (!str)
284
0
    return -1;
285
286
3.03M
  va_copy(ap, oap);
287
3.03M
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
288
3.03M
  va_end(ap);
289
290
3.03M
  if (length != plen)
291
0
  {
292
0
    free(str);
293
0
    return -1;
294
0
  }
295
3.03M
  *s = str;
296
3.03M
  *slen = (size_t)length;
297
3.03M
  return length;
298
3.03M
}
299
300
#ifndef _WIN32
301
302
char* _strdup(const char* strSource)
303
3.55M
{
304
3.55M
  if (strSource == nullptr)
305
0
    return nullptr;
306
307
3.55M
  char* strDestination = strdup(strSource);
308
309
3.55M
  if (strDestination == nullptr)
310
0
    WLog_ERR(TAG, "strdup");
311
312
3.55M
  return strDestination;
313
3.55M
}
314
315
WCHAR* _wcsdup(const WCHAR* strSource)
316
1.17M
{
317
1.17M
  if (!strSource)
318
0
    return nullptr;
319
320
1.17M
  size_t len = _wcslen(strSource);
321
1.17M
  WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
322
323
1.17M
  if (strDestination != nullptr)
324
1.17M
    memcpy(strDestination, strSource, len * sizeof(WCHAR));
325
326
1.17M
  if (strDestination == nullptr)
327
0
    WLog_ERR(TAG, "wcsdup");
328
329
1.17M
  return strDestination;
330
1.17M
}
331
332
WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
333
120k
{
334
120k
  WINPR_ASSERT(dst);
335
120k
  WINPR_ASSERT(src || (sz == 0));
336
337
120k
  const size_t dlen = _wcslen(dst);
338
120k
  const size_t slen = _wcsnlen(src, sz);
339
16.2M
  for (size_t x = 0; x < slen; x++)
340
16.1M
    dst[dlen + x] = src[x];
341
120k
  dst[dlen + slen] = '\0';
342
120k
  return dst;
343
120k
}
344
345
int _stricmp(const char* string1, const char* string2)
346
76.2M
{
347
76.2M
  return strcasecmp(string1, string2);
348
76.2M
}
349
350
int _strnicmp(const char* string1, const char* string2, size_t count)
351
2.02k
{
352
2.02k
  return strncasecmp(string1, string2, count);
353
2.02k
}
354
355
/* _wcscmp -> wcscmp */
356
357
int _wcscmp(const WCHAR* string1, const WCHAR* string2)
358
943k
{
359
943k
  WINPR_ASSERT(string1);
360
943k
  WINPR_ASSERT(string2);
361
362
1.18M
  while (TRUE)
363
1.18M
  {
364
1.18M
    const WCHAR w1 = *string1++;
365
1.18M
    const WCHAR w2 = *string2++;
366
367
1.18M
    if (w1 != w2)
368
823k
      return (int)w1 - w2;
369
359k
    else if ((w1 == '\0') || (w2 == '\0'))
370
119k
      return (int)w1 - w2;
371
1.18M
  }
372
943k
}
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.17M
{
396
4.17M
  const WCHAR* p = str;
397
398
4.17M
  WINPR_ASSERT(p);
399
400
305M
  while (*p)
401
301M
    p++;
402
403
4.17M
  return (size_t)(p - str);
404
4.17M
}
405
406
/* _wcsnlen -> wcsnlen */
407
408
size_t _wcsnlen(const WCHAR* str, size_t max)
409
4.96M
{
410
4.96M
  WINPR_ASSERT(str);
411
412
4.96M
  size_t x = 0;
413
225M
  for (; x < max; x++)
414
225M
  {
415
225M
    if (str[x] == 0)
416
4.77M
      return x;
417
225M
  }
418
419
180k
  return x;
420
4.96M
}
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.91M
{
490
1.91M
  return strtok_r(strToken, strDelimit, context);
491
1.91M
}
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
8.24k
{
540
8.24k
  size_t length = 0;
541
542
8.24k
  if (!lpsz)
543
0
    return nullptr;
544
545
8.24k
  length = strlen(lpsz);
546
547
8.24k
  if (length < 1)
548
0
    return (LPSTR) nullptr;
549
550
8.24k
  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
107k
  for (size_t i = 0; i < length; i++)
562
98.9k
  {
563
98.9k
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
564
41.2k
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
565
98.9k
  }
566
567
8.24k
  return lpsz;
568
8.24k
}
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
201
{
618
1.80k
  for (DWORD i = 0; i < cchLength; i++)
619
1.60k
  {
620
1.60k
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
621
1.60k
    value = WINPR_TOUPPERW(value);
622
1.60k
    winpr_Data_Write_UINT16(&lpsz[i], value);
623
1.60k
  }
624
625
201
  return cchLength;
626
201
}
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.72k
{
765
3.72k
  size_t skip = 0;
766
767
3.72k
  WINPR_ASSERT(str || (size == 0));
768
111M
  for (size_t x = 0; x < size; x++)
769
111M
  {
770
111M
    char c = str[x];
771
111M
    switch (c)
772
111M
    {
773
232k
      case '\r':
774
232k
        str[x - skip] = '\n';
775
232k
        if ((x + 1 < size) && (str[x + 1] == '\n'))
776
36.7k
          skip++;
777
232k
        break;
778
111M
      default:
779
111M
        str[x - skip] = c;
780
111M
        break;
781
111M
    }
782
111M
  }
783
3.72k
  if (skip > 0)
784
68
    str[size - skip] = '\0';
785
3.72k
  return size - skip;
786
3.72k
}
787
788
char* ConvertLineEndingToCRLF(const char* str, size_t* size)
789
402
{
790
402
  WINPR_ASSERT(size);
791
402
  const size_t s = *size;
792
402
  WINPR_ASSERT(str || (s == 0));
793
794
402
  *size = 0;
795
402
  if (s == 0)
796
2
    return nullptr;
797
798
400
  size_t linebreaks = 0;
799
9.17M
  for (size_t x = 0; x < s; x++)
800
9.17M
  {
801
9.17M
    char c = str[x];
802
9.17M
    switch (c)
803
9.17M
    {
804
1.66k
      case '\r':
805
2.90M
      case '\n':
806
2.90M
        linebreaks++;
807
2.90M
        break;
808
6.26M
      default:
809
6.26M
        break;
810
9.17M
    }
811
9.17M
  }
812
400
  char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
813
400
  if (!cnv)
814
0
    return nullptr;
815
816
400
  size_t pos = 0;
817
9.17M
  for (size_t x = 0; x < s; x++)
818
9.17M
  {
819
9.17M
    const char c = str[x];
820
9.17M
    switch (c)
821
9.17M
    {
822
1.66k
      case '\r':
823
1.66k
        cnv[pos++] = '\r';
824
1.66k
        cnv[pos++] = '\n';
825
1.66k
        break;
826
2.90M
      case '\n':
827
        /* Do not duplicate existing \r\n sequences */
828
2.90M
        if ((x == 0) || (str[x - 1] != '\r'))
829
2.90M
        {
830
2.90M
          cnv[pos++] = '\r';
831
2.90M
          cnv[pos++] = '\n';
832
2.90M
        }
833
2.90M
        break;
834
6.26M
      default:
835
6.26M
        cnv[pos++] = c;
836
6.26M
        break;
837
9.17M
    }
838
9.17M
  }
839
400
  *size = pos;
840
400
  return cnv;
841
400
}
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
1.00M
{
916
1.00M
  WINPR_ASSERT(str);
917
1.00M
  WINPR_ASSERT(buffer || (len == 0));
918
1.00M
  (void)ConvertUtf8ToWChar(str, buffer, len);
919
1.00M
  return buffer;
920
1.00M
}
921
922
WCHAR* wcsndup(const WCHAR* s, size_t n)
923
199
{
924
199
  if (!s)
925
0
    return nullptr;
926
927
199
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
928
199
  if (!copy)
929
0
    return nullptr;
930
199
  memcpy(copy, s, n * sizeof(WCHAR));
931
199
  return copy;
932
199
}
933
934
char* winpr_strnstr(const char* haystack, const char* needle, size_t hlen)
935
17.7M
{
936
17.7M
  WINPR_ASSERT(haystack || (hlen == 0));
937
17.7M
  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
17.7M
  const size_t needle_len = strlen(needle);
945
946
17.7M
  if (0 == needle_len)
947
0
    return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
948
949
202M
  for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
950
185M
  {
951
185M
    if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
952
573k
      return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
953
185M
  }
954
17.1M
  return nullptr;
955
17.7M
#endif
956
17.7M
}
957
958
BOOL winpr_str_has_newlines(const char* str)
959
46
{
960
46
  if (!str)
961
0
    return FALSE;
962
46
  do
963
1.25k
  {
964
1.25k
    char c = *str++;
965
1.25k
    switch (c)
966
1.25k
    {
967
1
      case '\r':
968
3
      case '\n':
969
5
      case 0x0b:       /* VT vertical tab */
970
6
      case 0x0c:       /* FF form feed */
971
12
      case (char)0x85: /* NEL next line */
972
12
        return TRUE;
973
142
      case (char)0xE2:
974
142
      {
975
142
        if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
976
2
          return TRUE;
977
142
      }
978
140
      break;
979
980
140
      case '\0':
981
32
        return FALSE;
982
1.07k
      default:
983
1.07k
        break;
984
1.25k
    }
985
1.25k
  } while (1);
986
46
}
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 = *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 = *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
128
{
1042
128
  if ((c >= 'a') && (c <= 'z'))
1043
8
    return TRUE;
1044
120
  if ((c >= 'A') && (c <= 'Z'))
1045
36
    return TRUE;
1046
84
  if ((c >= '0') && (c <= '9'))
1047
22
    return TRUE;
1048
1049
62
  switch (c)
1050
62
  {
1051
6
    case '-':
1052
19
    case '.':
1053
19
      return TRUE;
1054
43
    default:
1055
43
      return FALSE;
1056
62
  }
1057
62
}
1058
1059
BOOL winpr_str_is_valid_hostname(const char* str)
1060
71
{
1061
71
  if (!str)
1062
0
    return FALSE;
1063
  /* do a limited strnlen with DNS max length + 1, so we can abort early.
1064
   */
1065
71
  const size_t len = strnlen(str, 256);
1066
71
  return winpr_str_is_valid_hostnameN(str, len);
1067
71
}
1068
1069
BOOL winpr_str_is_valid_hostnameN(const char* str, size_t len)
1070
71
{
1071
71
  if (!str || (len == 0))
1072
8
  {
1073
8
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] is too short", str, len);
1074
8
    return FALSE;
1075
8
  }
1076
1077
63
  if (len > 255)
1078
1
  {
1079
1
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] is too long", str, len);
1080
1
    return FALSE;
1081
1
  }
1082
1083
  // may not start or end with dot
1084
62
  if (str[0] == '.')
1085
1
  {
1086
1
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] starts with a dot", str, len);
1087
1
    return FALSE;
1088
1
  }
1089
61
  if (str[len - 1] == '.')
1090
1
  {
1091
1
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] starts ends a dot", str, len);
1092
1
    return FALSE;
1093
1
  }
1094
1095
60
  size_t segmentCount = 1;
1096
60
  size_t segment = 0;
1097
141
  for (size_t x = 0; x < len; x++)
1098
128
  {
1099
128
    char c = str[x];
1100
128
    if (!validHostChar(c))
1101
43
    {
1102
43
      WLog_VRB(TAG, "hostname %s [%" PRIuz "] invalid character %c at %" PRIuz, str, len, c,
1103
43
               x);
1104
43
      return FALSE;
1105
43
    }
1106
1107
85
    if (c == '.')
1108
13
    {
1109
13
      segmentCount++;
1110
13
      segment = 0;
1111
1112
      // may not start with dot
1113
13
      if (x == 0)
1114
0
      {
1115
0
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment starts with a dot", str, len);
1116
0
        return FALSE;
1117
0
      }
1118
      // filter dotdot
1119
13
      else if (str[x - 1] == c)
1120
1
      {
1121
1
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment contains dotdot", str, len);
1122
1
        return FALSE;
1123
1
      }
1124
      // segments may not end with a hyphen
1125
12
      else if (str[x - 1] == '-')
1126
1
      {
1127
1
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment ends with a hyphen", str, len);
1128
1
        return FALSE;
1129
1
      }
1130
13
    }
1131
72
    else
1132
72
    {
1133
72
      segment++;
1134
72
      if (segment > 63)
1135
0
      {
1136
0
        WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment longer than 63 characters", str,
1137
0
                 len);
1138
0
        return FALSE;
1139
0
      }
1140
1141
      // - may not occur as first in segment
1142
72
      if (c == '-')
1143
6
      {
1144
        // segments may not start with a hyphen
1145
6
        if ((x == 0) || (str[x - 1] == '.'))
1146
2
        {
1147
2
          WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment starts with a hyphen", str,
1148
2
                   len);
1149
2
          return FALSE;
1150
2
        }
1151
6
      }
1152
72
    }
1153
85
  }
1154
1155
  // segments may not end with a hyphen
1156
13
  if (str[len - 1] == '-')
1157
2
  {
1158
2
    WLog_VRB(TAG, "hostname %s [%" PRIuz "] segment ends with a hyphen", str, len);
1159
2
    return FALSE;
1160
2
  }
1161
1162
  // last segment may not contain a hyphen
1163
11
  if (segmentCount > 1)
1164
3
  {
1165
3
    if (segment < 2)
1166
1
    {
1167
1
      WLog_VRB(TAG, "hostname %s [%" PRIuz "] TLD segment too short", str, len);
1168
1
      return FALSE;
1169
1
    }
1170
3
  }
1171
1172
10
  return TRUE;
1173
11
}