Coverage Report

Created: 2026-10-15 06:30

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
0
{
122
0
  if (what >= 'a')
123
0
    what -= 'a' - 'A';
124
0
  if (what >= 'A')
125
0
    what -= ('A' - 10);
126
0
  else
127
0
    what -= '0';
128
0
  return what;
129
0
}
130
131
static char unescape(const char* what, size_t* px)
132
0
{
133
0
  if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2])))
134
0
  {
135
0
    *px += 2;
136
0
    return 16 * hex2bin(what[1]) + hex2bin(what[2]);
137
0
  }
138
139
0
  return *what;
140
0
}
141
142
char* winpr_str_url_decode(const char* str, size_t len)
143
0
{
144
0
  char* dst = calloc(len + 1, sizeof(char));
145
0
  if (!dst)
146
0
    return nullptr;
147
148
0
  size_t pos = 0;
149
0
  for (size_t x = 0; x < strnlen(str, len); x++)
150
0
  {
151
0
    const char* cur = &str[x];
152
0
    dst[pos++] = unescape(cur, &x);
153
0
  }
154
0
  return dst;
155
0
}
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
0
{
242
0
  const size_t used = strnlen(buffer, size);
243
0
  const size_t add = strnlen(what, size);
244
0
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
245
0
  const size_t sep = (used > 0) ? sep_len : 0;
246
247
0
  if (used + add + sep >= size)
248
0
    return FALSE;
249
250
0
  if ((used > 0) && (sep_len > 0))
251
0
    strncat(buffer, separator, sep_len);
252
253
0
  strncat(buffer, what, add);
254
0
  return TRUE;
255
0
}
256
257
WINPR_ATTR_FORMAT_ARG(3, 4)
258
int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
259
337k
{
260
337k
  va_list ap = WINPR_C_ARRAY_INIT;
261
262
337k
  va_start(ap, templ);
263
337k
  int rc = winpr_vasprintf(s, slen, templ, ap);
264
337k
  va_end(ap);
265
337k
  return rc;
266
337k
}
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
747k
{
271
747k
  va_list ap = WINPR_C_ARRAY_INIT;
272
273
747k
  *s = nullptr;
274
747k
  *slen = 0;
275
276
747k
  va_copy(ap, oap);
277
747k
  const int length = vsnprintf(nullptr, 0, templ, ap);
278
747k
  va_end(ap);
279
747k
  if (length < 0)
280
0
    return length;
281
282
747k
  char* str = calloc((size_t)length + 1UL, sizeof(char));
283
747k
  if (!str)
284
0
    return -1;
285
286
747k
  va_copy(ap, oap);
287
747k
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
288
747k
  va_end(ap);
289
290
747k
  if (length != plen)
291
0
  {
292
0
    free(str);
293
0
    return -1;
294
0
  }
295
747k
  *s = str;
296
747k
  *slen = (size_t)length;
297
747k
  return length;
298
747k
}
299
300
#ifndef _WIN32
301
302
char* _strdup(const char* strSource)
303
1.68M
{
304
1.68M
  if (strSource == nullptr)
305
0
    return nullptr;
306
307
1.68M
  char* strDestination = strdup(strSource);
308
309
1.68M
  if (strDestination == nullptr)
310
0
    WLog_ERR(TAG, "strdup");
311
312
1.68M
  return strDestination;
313
1.68M
}
314
315
WCHAR* _wcsdup(const WCHAR* strSource)
316
0
{
317
0
  if (!strSource)
318
0
    return nullptr;
319
320
0
  size_t len = _wcslen(strSource);
321
0
  WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
322
323
0
  if (strDestination != nullptr)
324
0
    memcpy(strDestination, strSource, len * sizeof(WCHAR));
325
326
0
  if (strDestination == nullptr)
327
0
    WLog_ERR(TAG, "wcsdup");
328
329
0
  return strDestination;
330
0
}
331
332
WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
333
0
{
334
0
  WINPR_ASSERT(dst);
335
0
  WINPR_ASSERT(src || (sz == 0));
336
337
0
  const size_t dlen = _wcslen(dst);
338
0
  const size_t slen = _wcsnlen(src, sz);
339
0
  for (size_t x = 0; x < slen; x++)
340
0
    dst[dlen + x] = src[x];
341
0
  dst[dlen + slen] = '\0';
342
0
  return dst;
343
0
}
344
345
int _stricmp(const char* string1, const char* string2)
346
42.5M
{
347
42.5M
  return strcasecmp(string1, string2);
348
42.5M
}
349
350
int _strnicmp(const char* string1, const char* string2, size_t count)
351
0
{
352
0
  return strncasecmp(string1, string2, count);
353
0
}
354
355
/* _wcscmp -> wcscmp */
356
357
int _wcscmp(const WCHAR* string1, const WCHAR* string2)
358
0
{
359
0
  WINPR_ASSERT(string1);
360
0
  WINPR_ASSERT(string2);
361
362
0
  while (TRUE)
363
0
  {
364
0
    const WCHAR w1 = *string1++;
365
0
    const WCHAR w2 = *string2++;
366
367
0
    if (w1 != w2)
368
0
      return (int)w1 - w2;
369
0
    else if ((w1 == '\0') || (w2 == '\0'))
370
0
      return (int)w1 - w2;
371
0
  }
372
0
}
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
0
{
396
0
  const WCHAR* p = str;
397
398
0
  WINPR_ASSERT(p);
399
400
0
  while (*p)
401
0
    p++;
402
403
0
  return (size_t)(p - str);
404
0
}
405
406
/* _wcsnlen -> wcsnlen */
407
408
size_t _wcsnlen(const WCHAR* str, size_t max)
409
193k
{
410
193k
  WINPR_ASSERT(str);
411
412
193k
  size_t x = 0;
413
8.69M
  for (; x < max; x++)
414
8.69M
  {
415
8.69M
    if (str[x] == 0)
416
192k
      return x;
417
8.69M
  }
418
419
397
  return x;
420
193k
}
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.08M
{
490
1.08M
  return strtok_r(strToken, strDelimit, context);
491
1.08M
}
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
0
{
540
0
  size_t length = 0;
541
542
0
  if (!lpsz)
543
0
    return nullptr;
544
545
0
  length = strlen(lpsz);
546
547
0
  if (length < 1)
548
0
    return (LPSTR) nullptr;
549
550
0
  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
0
  for (size_t i = 0; i < length; i++)
562
0
  {
563
0
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
564
0
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
565
0
  }
566
567
0
  return lpsz;
568
0
}
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
0
{
618
0
  for (DWORD i = 0; i < cchLength; i++)
619
0
  {
620
0
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
621
0
    value = WINPR_TOUPPERW(value);
622
0
    winpr_Data_Write_UINT16(&lpsz[i], value);
623
0
  }
624
625
0
  return cchLength;
626
0
}
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
0
{
765
0
  size_t skip = 0;
766
767
0
  WINPR_ASSERT(str || (size == 0));
768
0
  for (size_t x = 0; x < size; x++)
769
0
  {
770
0
    char c = str[x];
771
0
    switch (c)
772
0
    {
773
0
      case '\r':
774
0
        str[x - skip] = '\n';
775
0
        if ((x + 1 < size) && (str[x + 1] == '\n'))
776
0
          skip++;
777
0
        break;
778
0
      default:
779
0
        str[x - skip] = c;
780
0
        break;
781
0
    }
782
0
  }
783
0
  if (skip > 0)
784
0
    str[size - skip] = '\0';
785
0
  return size - skip;
786
0
}
787
788
char* ConvertLineEndingToCRLF(const char* str, size_t* size)
789
0
{
790
0
  WINPR_ASSERT(size);
791
0
  const size_t s = *size;
792
0
  WINPR_ASSERT(str || (s == 0));
793
794
0
  *size = 0;
795
0
  if (s == 0)
796
0
    return nullptr;
797
798
0
  size_t linebreaks = 0;
799
0
  for (size_t x = 0; x < s; x++)
800
0
  {
801
0
    char c = str[x];
802
0
    switch (c)
803
0
    {
804
0
      case '\r':
805
0
      case '\n':
806
0
        linebreaks++;
807
0
        break;
808
0
      default:
809
0
        break;
810
0
    }
811
0
  }
812
0
  char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
813
0
  if (!cnv)
814
0
    return nullptr;
815
816
0
  size_t pos = 0;
817
0
  for (size_t x = 0; x < s; x++)
818
0
  {
819
0
    const char c = str[x];
820
0
    switch (c)
821
0
    {
822
0
      case '\r':
823
0
        cnv[pos++] = '\r';
824
0
        cnv[pos++] = '\n';
825
0
        break;
826
0
      case '\n':
827
        /* Do not duplicate existing \r\n sequences */
828
0
        if ((x == 0) || (str[x - 1] != '\r'))
829
0
        {
830
0
          cnv[pos++] = '\r';
831
0
          cnv[pos++] = '\n';
832
0
        }
833
0
        break;
834
0
      default:
835
0
        cnv[pos++] = c;
836
0
        break;
837
0
    }
838
0
  }
839
0
  *size = pos;
840
0
  return cnv;
841
0
}
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
0
{
916
0
  WINPR_ASSERT(str);
917
0
  WINPR_ASSERT(buffer || (len == 0));
918
0
  (void)ConvertUtf8ToWChar(str, buffer, len);
919
0
  return buffer;
920
0
}
921
922
WCHAR* wcsndup(const WCHAR* s, size_t n)
923
0
{
924
0
  if (!s)
925
0
    return nullptr;
926
927
0
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
928
0
  if (!copy)
929
0
    return nullptr;
930
0
  memcpy(copy, s, n * sizeof(WCHAR));
931
0
  return copy;
932
0
}
933
934
char* winpr_strnstr(const char* haystack, const char* needle, size_t hlen)
935
0
{
936
0
  WINPR_ASSERT(haystack || (hlen == 0));
937
0
  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
0
  const size_t needle_len = strlen(needle);
945
946
0
  if (0 == needle_len)
947
0
    return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
948
949
0
  for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
950
0
  {
951
0
    if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
952
0
      return WINPR_CAST_CONST_PTR_AWAY(haystack, char*);
953
0
  }
954
0
  return nullptr;
955
0
#endif
956
0
}
957
958
BOOL winpr_str_has_newlines(const char* str)
959
0
{
960
0
  if (!str)
961
0
    return FALSE;
962
0
  do
963
0
  {
964
0
    char c = *str++;
965
0
    switch (c)
966
0
    {
967
0
      case '\r':
968
0
      case '\n':
969
0
      case 0x0b:       /* VT vertical tab */
970
0
      case 0x0c:       /* FF form feed */
971
0
      case (char)0x85: /* NEL next line */
972
0
        return TRUE;
973
0
      case (char)0xE2:
974
0
      {
975
0
        if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
976
0
          return TRUE;
977
0
      }
978
0
      break;
979
980
0
      case '\0':
981
0
        return FALSE;
982
0
      default:
983
0
        break;
984
0
    }
985
0
  } while (1);
986
0
}
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
0
{
1042
0
  if ((c >= 'a') && (c <= 'z'))
1043
0
    return TRUE;
1044
0
  if ((c >= 'A') && (c <= 'Z'))
1045
0
    return TRUE;
1046
0
  if ((c >= '0') && (c <= '9'))
1047
0
    return TRUE;
1048
1049
0
  switch (c)
1050
0
  {
1051
0
    case '-':
1052
0
    case '.':
1053
0
      return TRUE;
1054
0
    default:
1055
0
      return FALSE;
1056
0
  }
1057
0
}
1058
1059
BOOL winpr_str_is_valid_hostname(const char* str)
1060
0
{
1061
0
  if (!str)
1062
0
    return FALSE;
1063
  /* do a limited strnlen with DNS max length + 1, so we can abort early.
1064
   */
1065
0
  const size_t len = strnlen(str, 256);
1066
0
  return winpr_str_is_valid_hostnameN(str, len);
1067
0
}
1068
1069
BOOL winpr_str_is_valid_hostnameN(const char* str, size_t len)
1070
0
{
1071
0
  if (!str || (len == 0))
1072
0
    return FALSE;
1073
1074
0
  if (len > 255)
1075
0
    return FALSE;
1076
1077
  // may not start or end with dot
1078
0
  if (str[0] == '.')
1079
0
    return FALSE;
1080
0
  if (str[len - 1] == '.')
1081
0
    return FALSE;
1082
1083
0
  size_t segment = 0;
1084
0
  BOOL segmentHasHyphen = FALSE;
1085
0
  for (size_t x = 0; x < len; x++)
1086
0
  {
1087
0
    char c = str[x];
1088
0
    if (!validHostChar(c))
1089
0
      return FALSE;
1090
1091
0
    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
0
    else
1107
0
    {
1108
0
      segment++;
1109
0
      if (segment > 63)
1110
0
        return FALSE;
1111
1112
      // - may not occur as first in segment
1113
0
      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
0
    }
1122
0
  }
1123
1124
  // last segment may not contain a hyphen
1125
0
  if (segmentHasHyphen)
1126
0
    return FALSE;
1127
1128
  // assume TLD is at least 2 char
1129
0
  if (segment < 2)
1130
0
    return FALSE;
1131
1132
0
  return TRUE;
1133
0
}