Coverage Report

Created: 2026-07-16 07:09

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