Coverage Report

Created: 2026-09-14 06:32

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
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
77.9k
{
242
77.9k
  const size_t used = strnlen(buffer, size);
243
77.9k
  const size_t add = strnlen(what, size);
244
77.9k
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
245
77.9k
  const size_t sep = (used > 0) ? sep_len : 0;
246
247
77.9k
  if (used + add + sep >= size)
248
12
    return FALSE;
249
250
77.8k
  if ((used > 0) && (sep_len > 0))
251
0
    strncat(buffer, separator, sep_len);
252
253
77.8k
  strncat(buffer, what, add);
254
77.8k
  return TRUE;
255
77.9k
}
256
257
WINPR_ATTR_FORMAT_ARG(3, 4)
258
int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
259
26.3k
{
260
26.3k
  va_list ap = WINPR_C_ARRAY_INIT;
261
262
26.3k
  va_start(ap, templ);
263
26.3k
  int rc = winpr_vasprintf(s, slen, templ, ap);
264
26.3k
  va_end(ap);
265
26.3k
  return rc;
266
26.3k
}
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
71.5k
{
271
71.5k
  va_list ap = WINPR_C_ARRAY_INIT;
272
273
71.5k
  *s = nullptr;
274
71.5k
  *slen = 0;
275
276
71.5k
  va_copy(ap, oap);
277
71.5k
  const int length = vsnprintf(nullptr, 0, templ, ap);
278
71.5k
  va_end(ap);
279
71.5k
  if (length < 0)
280
0
    return length;
281
282
71.5k
  char* str = calloc((size_t)length + 1UL, sizeof(char));
283
71.5k
  if (!str)
284
0
    return -1;
285
286
71.5k
  va_copy(ap, oap);
287
71.5k
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
288
71.5k
  va_end(ap);
289
290
71.5k
  if (length != plen)
291
0
  {
292
0
    free(str);
293
0
    return -1;
294
0
  }
295
71.5k
  *s = str;
296
71.5k
  *slen = (size_t)length;
297
71.5k
  return length;
298
71.5k
}
299
300
#ifndef _WIN32
301
302
char* _strdup(const char* strSource)
303
74.5k
{
304
74.5k
  if (strSource == nullptr)
305
0
    return nullptr;
306
307
74.5k
  char* strDestination = strdup(strSource);
308
309
74.5k
  if (strDestination == nullptr)
310
0
    WLog_ERR(TAG, "strdup");
311
312
74.5k
  return strDestination;
313
74.5k
}
314
315
WCHAR* _wcsdup(const WCHAR* strSource)
316
64.6k
{
317
64.6k
  if (!strSource)
318
0
    return nullptr;
319
320
64.6k
  size_t len = _wcslen(strSource);
321
64.6k
  WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
322
323
64.6k
  if (strDestination != nullptr)
324
64.6k
    memcpy(strDestination, strSource, len * sizeof(WCHAR));
325
326
64.6k
  if (strDestination == nullptr)
327
0
    WLog_ERR(TAG, "wcsdup");
328
329
64.6k
  return strDestination;
330
64.6k
}
331
332
WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
333
12.1k
{
334
12.1k
  WINPR_ASSERT(dst);
335
12.1k
  WINPR_ASSERT(src || (sz == 0));
336
337
12.1k
  const size_t dlen = _wcslen(dst);
338
12.1k
  const size_t slen = _wcsnlen(src, sz);
339
231k
  for (size_t x = 0; x < slen; x++)
340
219k
    dst[dlen + x] = src[x];
341
12.1k
  dst[dlen + slen] = '\0';
342
12.1k
  return dst;
343
12.1k
}
344
345
int _stricmp(const char* string1, const char* string2)
346
803k
{
347
803k
  return strcasecmp(string1, string2);
348
803k
}
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
58.7k
{
359
58.7k
  WINPR_ASSERT(string1);
360
58.7k
  WINPR_ASSERT(string2);
361
362
82.8k
  while (TRUE)
363
82.8k
  {
364
82.8k
    const WCHAR w1 = *string1++;
365
82.8k
    const WCHAR w2 = *string2++;
366
367
82.8k
    if (w1 != w2)
368
46.6k
      return (int)w1 - w2;
369
36.1k
    else if ((w1 == '\0') || (w2 == '\0'))
370
12.0k
      return (int)w1 - w2;
371
82.8k
  }
372
58.7k
}
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
241k
{
396
241k
  const WCHAR* p = str;
397
398
241k
  WINPR_ASSERT(p);
399
400
7.69M
  while (*p)
401
7.45M
    p++;
402
403
241k
  return (size_t)(p - str);
404
241k
}
405
406
/* _wcsnlen -> wcsnlen */
407
408
size_t _wcsnlen(const WCHAR* str, size_t max)
409
72.1k
{
410
72.1k
  WINPR_ASSERT(str);
411
412
72.1k
  size_t x = 0;
413
4.65M
  for (; x < max; x++)
414
4.64M
  {
415
4.64M
    if (str[x] == 0)
416
65.0k
      return x;
417
4.64M
  }
418
419
7.04k
  return x;
420
72.1k
}
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
444
WCHAR* _wcschr(const WCHAR* str, WCHAR c)
445
0
{
446
0
  union
447
0
  {
448
0
    const WCHAR* cc;
449
0
    WCHAR* c;
450
0
  } cnv;
451
0
  const WCHAR* p = str;
452
453
0
  while (*p && (*p != c))
454
0
    p++;
455
456
0
  cnv.cc = (*p == c) ? p : nullptr;
457
0
  return cnv.c;
458
0
}
459
460
/* _wcsrchr -> wcsrchr */
461
462
WCHAR* _wcsrchr(const WCHAR* str, WCHAR c)
463
0
{
464
0
  union
465
0
  {
466
0
    const WCHAR* cc;
467
0
    WCHAR* c;
468
0
  } cnv;
469
0
  const WCHAR* p = nullptr;
470
471
0
  if (!str)
472
0
    return nullptr;
473
474
0
  for (; *str != '\0'; str++)
475
0
  {
476
0
    const WCHAR ch = *str;
477
0
    if (ch == c)
478
0
      p = str;
479
0
  }
480
481
0
  cnv.cc = p;
482
0
  return cnv.c;
483
0
}
484
485
char* strtok_s(char* strToken, const char* strDelimit, char** context)
486
597k
{
487
597k
  return strtok_r(strToken, strDelimit, context);
488
597k
}
489
490
WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context)
491
0
{
492
0
  WCHAR* nextToken = nullptr;
493
0
  WCHAR value = 0;
494
495
0
  if (!strToken)
496
0
    strToken = *context;
497
498
0
  value = *strToken;
499
500
0
  while (*strToken && _wcschr(strDelimit, value))
501
0
  {
502
0
    strToken++;
503
0
    value = *strToken;
504
0
  }
505
506
0
  if (!*strToken)
507
0
    return nullptr;
508
509
0
  nextToken = strToken++;
510
0
  value = *strToken;
511
512
0
  while (*strToken && !(_wcschr(strDelimit, value)))
513
0
  {
514
0
    strToken++;
515
0
    value = *strToken;
516
0
  }
517
518
0
  if (*strToken)
519
0
    *strToken++ = 0;
520
521
0
  *context = strToken;
522
0
  return nextToken;
523
0
}
524
525
#endif
526
527
#if !defined(_WIN32) || defined(_UWP)
528
529
/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll
530
 * http://msdn.microsoft.com/en-us/library/hh802935/
531
 */
532
533
#include "casing.h"
534
535
LPSTR CharUpperA(LPSTR lpsz)
536
8.12k
{
537
8.12k
  size_t length = 0;
538
539
8.12k
  if (!lpsz)
540
0
    return nullptr;
541
542
8.12k
  length = strlen(lpsz);
543
544
8.12k
  if (length < 1)
545
0
    return (LPSTR) nullptr;
546
547
8.12k
  if (length == 1)
548
0
  {
549
0
    char c = *lpsz;
550
551
0
    if ((c >= 'a') && (c <= 'z'))
552
0
      c = (char)(c - 'a' + 'A');
553
554
0
    *lpsz = c;
555
0
    return lpsz;
556
0
  }
557
558
105k
  for (size_t i = 0; i < length; i++)
559
97.5k
  {
560
97.5k
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
561
16.2k
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
562
97.5k
  }
563
564
8.12k
  return lpsz;
565
8.12k
}
566
567
LPWSTR CharUpperW(LPWSTR lpsz)
568
0
{
569
0
  size_t length = 0;
570
571
0
  if (!lpsz)
572
0
    return nullptr;
573
574
0
  length = _wcslen(lpsz);
575
576
0
  if (length < 1)
577
0
    return (LPWSTR) nullptr;
578
579
0
  if (length == 1)
580
0
  {
581
0
    WCHAR c = *lpsz;
582
583
0
    if ((c >= L'a') && (c <= L'z'))
584
0
      c = c - L'a' + L'A';
585
586
0
    *lpsz = c;
587
0
    return lpsz;
588
0
  }
589
590
0
  for (size_t i = 0; i < length; i++)
591
0
  {
592
0
    if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z'))
593
0
      lpsz[i] = lpsz[i] - L'a' + L'A';
594
0
  }
595
596
0
  return lpsz;
597
0
}
598
599
DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength)
600
0
{
601
0
  if (cchLength < 1)
602
0
    return 0;
603
604
0
  for (DWORD i = 0; i < cchLength; i++)
605
0
  {
606
0
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
607
0
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
608
0
  }
609
610
0
  return cchLength;
611
0
}
612
613
DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength)
614
180
{
615
1.62k
  for (DWORD i = 0; i < cchLength; i++)
616
1.44k
  {
617
1.44k
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
618
1.44k
    value = WINPR_TOUPPERW(value);
619
1.44k
    winpr_Data_Write_UINT16(&lpsz[i], value);
620
1.44k
  }
621
622
180
  return cchLength;
623
180
}
624
625
LPSTR CharLowerA(LPSTR lpsz)
626
0
{
627
0
  size_t length = 0;
628
629
0
  if (!lpsz)
630
0
    return (LPSTR) nullptr;
631
632
0
  length = strlen(lpsz);
633
634
0
  if (length < 1)
635
0
    return (LPSTR) nullptr;
636
637
0
  if (length == 1)
638
0
  {
639
0
    char c = *lpsz;
640
641
0
    if ((c >= 'A') && (c <= 'Z'))
642
0
      c = (char)(c - 'A' + 'a');
643
644
0
    *lpsz = c;
645
0
    return lpsz;
646
0
  }
647
648
0
  for (size_t i = 0; i < length; i++)
649
0
  {
650
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
651
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
652
0
  }
653
654
0
  return lpsz;
655
0
}
656
657
LPWSTR CharLowerW(LPWSTR lpsz)
658
0
{
659
0
  const size_t len = _wcsnlen(lpsz, UINT32_MAX + 1);
660
0
  if (len > UINT32_MAX)
661
0
    return nullptr;
662
0
  CharLowerBuffW(lpsz, (UINT32)len);
663
0
  return lpsz;
664
0
}
665
666
DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength)
667
0
{
668
0
  if (cchLength < 1)
669
0
    return 0;
670
671
0
  for (DWORD i = 0; i < cchLength; i++)
672
0
  {
673
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
674
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
675
0
  }
676
677
0
  return cchLength;
678
0
}
679
680
DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength)
681
0
{
682
0
  for (DWORD i = 0; i < cchLength; i++)
683
0
  {
684
0
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
685
0
    value = WINPR_TOLOWERW(value);
686
0
    winpr_Data_Write_UINT16(&lpsz[i], value);
687
0
  }
688
689
0
  return cchLength;
690
0
}
691
692
BOOL IsCharAlphaA(CHAR ch)
693
0
{
694
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
695
0
    return 1;
696
0
  else
697
0
    return 0;
698
0
}
699
700
BOOL IsCharAlphaW(WCHAR ch)
701
0
{
702
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')))
703
0
    return 1;
704
0
  else
705
0
    return 0;
706
0
}
707
708
BOOL IsCharAlphaNumericA(CHAR ch)
709
0
{
710
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
711
0
      ((ch >= '0') && (ch <= '9')))
712
0
    return 1;
713
0
  else
714
0
    return 0;
715
0
}
716
717
BOOL IsCharAlphaNumericW(WCHAR ch)
718
0
{
719
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) ||
720
0
      ((ch >= L'0') && (ch <= L'9')))
721
0
    return 1;
722
0
  else
723
0
    return 0;
724
0
}
725
726
BOOL IsCharUpperA(CHAR ch)
727
0
{
728
0
  if ((ch >= 'A') && (ch <= 'Z'))
729
0
    return 1;
730
0
  else
731
0
    return 0;
732
0
}
733
734
BOOL IsCharUpperW(WCHAR ch)
735
0
{
736
0
  if ((ch >= L'A') && (ch <= L'Z'))
737
0
    return 1;
738
0
  else
739
0
    return 0;
740
0
}
741
742
BOOL IsCharLowerA(CHAR ch)
743
0
{
744
0
  if ((ch >= 'a') && (ch <= 'z'))
745
0
    return 1;
746
0
  else
747
0
    return 0;
748
0
}
749
750
BOOL IsCharLowerW(WCHAR ch)
751
0
{
752
0
  if ((ch >= L'a') && (ch <= L'z'))
753
0
    return 1;
754
0
  else
755
0
    return 0;
756
0
}
757
758
#endif
759
760
size_t ConvertLineEndingToLF(char* str, size_t size)
761
397
{
762
397
  size_t skip = 0;
763
764
397
  WINPR_ASSERT(str || (size == 0));
765
552k
  for (size_t x = 0; x < size; x++)
766
551k
  {
767
551k
    char c = str[x];
768
551k
    switch (c)
769
551k
    {
770
12.5k
      case '\r':
771
12.5k
        str[x - skip] = '\n';
772
12.5k
        if ((x + 1 < size) && (str[x + 1] == '\n'))
773
5.34k
          skip++;
774
12.5k
        break;
775
539k
      default:
776
539k
        str[x - skip] = c;
777
539k
        break;
778
551k
    }
779
551k
  }
780
397
  return size - skip;
781
397
}
782
783
char* ConvertLineEndingToCRLF(const char* str, size_t* size)
784
784
{
785
784
  WINPR_ASSERT(size);
786
784
  const size_t s = *size;
787
784
  WINPR_ASSERT(str || (s == 0));
788
789
784
  *size = 0;
790
784
  if (s == 0)
791
2
    return nullptr;
792
793
782
  size_t linebreaks = 0;
794
1.10M
  for (size_t x = 0; x < s - 1; x++)
795
1.10M
  {
796
1.10M
    char c = str[x];
797
1.10M
    switch (c)
798
1.10M
    {
799
25.0k
      case '\r':
800
801k
      case '\n':
801
801k
        linebreaks++;
802
801k
        break;
803
300k
      default:
804
300k
        break;
805
1.10M
    }
806
1.10M
  }
807
782
  char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
808
782
  if (!cnv)
809
0
    return nullptr;
810
811
782
  size_t pos = 0;
812
1.10M
  for (size_t x = 0; x < s; x++)
813
1.10M
  {
814
1.10M
    const char c = str[x];
815
1.10M
    switch (c)
816
1.10M
    {
817
25.0k
      case '\r':
818
25.0k
        cnv[pos++] = '\r';
819
25.0k
        cnv[pos++] = '\n';
820
25.0k
        break;
821
776k
      case '\n':
822
        /* Do not duplicate existing \r\n sequences */
823
776k
        if ((x > 0) && (str[x - 1] != '\r'))
824
765k
        {
825
765k
          cnv[pos++] = '\r';
826
765k
          cnv[pos++] = '\n';
827
765k
        }
828
776k
        break;
829
301k
      default:
830
301k
        cnv[pos++] = c;
831
301k
        break;
832
1.10M
    }
833
1.10M
  }
834
782
  *size = pos;
835
782
  return cnv;
836
782
}
837
838
char* StrSep(char** stringp, const char* delim)
839
0
{
840
0
  char* start = *stringp;
841
0
  char* p = nullptr;
842
0
  p = (start != nullptr) ? strpbrk(start, delim) : nullptr;
843
844
0
  if (!p)
845
0
    *stringp = nullptr;
846
0
  else
847
0
  {
848
0
    *p = '\0';
849
0
    *stringp = p + 1;
850
0
  }
851
852
0
  return start;
853
0
}
854
855
INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
856
0
{
857
#if defined(_WIN32)
858
  char c;
859
  char* n;
860
  size_t step = 32;
861
  size_t used = 0;
862
863
  if (!lineptr || !size)
864
  {
865
    errno = EINVAL;
866
    return -1;
867
  }
868
869
  do
870
  {
871
    if (used + 2 >= *size)
872
    {
873
      *size += step;
874
      n = realloc(*lineptr, *size);
875
876
      if (!n)
877
      {
878
        return -1;
879
      }
880
881
      *lineptr = n;
882
    }
883
884
    c = fgetc(stream);
885
886
    if (c != EOF)
887
      (*lineptr)[used++] = c;
888
  } while ((c != '\n') && (c != '\r') && (c != EOF));
889
890
  (*lineptr)[used] = '\0';
891
  return used;
892
#elif !defined(ANDROID) && !defined(IOS)
893
  return getline(lineptr, size, stream);
894
#else
895
  return -1;
896
#endif
897
0
}
898
899
#if !defined(WINPR_HAVE_STRNDUP)
900
char* strndup(const char* src, size_t n)
901
{
902
  char* dst = calloc(n + 1, sizeof(char));
903
  if (dst)
904
    strncpy(dst, src, n);
905
  return dst;
906
}
907
#endif
908
909
const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len)
910
64.7k
{
911
64.7k
  WINPR_ASSERT(str);
912
64.7k
  WINPR_ASSERT(buffer || (len == 0));
913
64.7k
  (void)ConvertUtf8ToWChar(str, buffer, len);
914
64.7k
  return buffer;
915
64.7k
}
916
917
WCHAR* wcsndup(const WCHAR* s, size_t n)
918
182
{
919
182
  if (!s)
920
0
    return nullptr;
921
922
182
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
923
182
  if (!copy)
924
0
    return nullptr;
925
182
  memcpy(copy, s, n * sizeof(WCHAR));
926
182
  return copy;
927
182
}
928
929
char* winpr_strnstr(char* haystack, const char* needle, size_t hlen)
930
369k
{
931
369k
  WINPR_ASSERT(haystack || (hlen == 0));
932
369k
  WINPR_ASSERT(needle);
933
934
#if defined(WINPR_HAVE_STRNSTR)
935
  return strnstr(haystack, needle, hlen);
936
#else
937
  /* Match native strnstr semantics: search at most hlen characters and stop at
938
   * the haystack NUL terminator, whichever comes first. */
939
369k
  const size_t needle_len = strlen(needle);
940
941
369k
  if (0 == needle_len)
942
0
    return haystack;
943
944
77.2M
  for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
945
77.2M
  {
946
77.2M
    if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
947
349k
      return haystack;
948
77.2M
  }
949
19.8k
  return nullptr;
950
369k
#endif
951
369k
}
952
953
BOOL winpr_str_has_newlines(const char* str)
954
0
{
955
0
  if (!str)
956
0
    return FALSE;
957
0
  do
958
0
  {
959
0
    char c = *str++;
960
0
    switch (c)
961
0
    {
962
0
      case '\r':
963
0
      case '\n':
964
0
      case 0x0b:       /* VT vertical tab */
965
0
      case 0x0c:       /* FF form feed */
966
0
      case (char)0x85: /* NEL next line */
967
0
        return TRUE;
968
0
      case (char)0xE2:
969
0
      {
970
0
        if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
971
0
          return TRUE;
972
0
      }
973
0
      break;
974
975
0
      case '\0':
976
0
        return FALSE;
977
0
      default:
978
0
        break;
979
0
    }
980
0
  } while (1);
981
0
}