Coverage Report

Created: 2026-08-31 06:29

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
13
{
211
13
  if (!str)
212
0
    return FALSE;
213
13
  regex_t regex = WINPR_C_ARRAY_INIT;
214
13
  const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
215
216
13
  int ret = regcomp(&regex, pattern, REG_EXTENDED);
217
13
  if (ret)
218
0
  {
219
0
    printf("Could not compile regex\n");
220
0
    return FALSE;
221
0
  }
222
223
13
  ret = regexec(&regex, str, 0, nullptr, 0);
224
13
  regfree(&regex);
225
13
  return ret == 0;
226
13
}
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
165k
{
240
165k
  const size_t used = strnlen(buffer, size);
241
165k
  const size_t add = strnlen(what, size);
242
165k
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
243
165k
  const size_t sep = (used > 0) ? sep_len : 0;
244
245
165k
  if (used + add + sep >= size)
246
22
    return FALSE;
247
248
165k
  if ((used > 0) && (sep_len > 0))
249
28.5k
    strncat(buffer, separator, sep_len);
250
251
165k
  strncat(buffer, what, add);
252
165k
  return TRUE;
253
165k
}
254
255
WINPR_ATTR_FORMAT_ARG(3, 4)
256
int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
257
885k
{
258
885k
  va_list ap = WINPR_C_ARRAY_INIT;
259
260
885k
  va_start(ap, templ);
261
885k
  int rc = winpr_vasprintf(s, slen, templ, ap);
262
885k
  va_end(ap);
263
885k
  return rc;
264
885k
}
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
1.78M
{
269
1.78M
  va_list ap = WINPR_C_ARRAY_INIT;
270
271
1.78M
  *s = nullptr;
272
1.78M
  *slen = 0;
273
274
1.78M
  va_copy(ap, oap);
275
1.78M
  const int length = vsnprintf(nullptr, 0, templ, ap);
276
1.78M
  va_end(ap);
277
1.78M
  if (length < 0)
278
0
    return length;
279
280
1.78M
  char* str = calloc((size_t)length + 1UL, sizeof(char));
281
1.78M
  if (!str)
282
0
    return -1;
283
284
1.78M
  va_copy(ap, oap);
285
1.78M
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
286
1.78M
  va_end(ap);
287
288
1.78M
  if (length != plen)
289
0
  {
290
0
    free(str);
291
0
    return -1;
292
0
  }
293
1.78M
  *s = str;
294
1.78M
  *slen = (size_t)length;
295
1.78M
  return length;
296
1.78M
}
297
298
#ifndef _WIN32
299
300
char* _strdup(const char* strSource)
301
954k
{
302
954k
  if (strSource == nullptr)
303
0
    return nullptr;
304
305
954k
  char* strDestination = strdup(strSource);
306
307
954k
  if (strDestination == nullptr)
308
0
    WLog_ERR(TAG, "strdup");
309
310
954k
  return strDestination;
311
954k
}
312
313
WCHAR* _wcsdup(const WCHAR* strSource)
314
64.6k
{
315
64.6k
  if (!strSource)
316
0
    return nullptr;
317
318
64.6k
  size_t len = _wcslen(strSource);
319
64.6k
  WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
320
321
64.6k
  if (strDestination != nullptr)
322
64.6k
    memcpy(strDestination, strSource, len * sizeof(WCHAR));
323
324
64.6k
  if (strDestination == nullptr)
325
0
    WLog_ERR(TAG, "wcsdup");
326
327
64.6k
  return strDestination;
328
64.6k
}
329
330
WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
331
12.1k
{
332
12.1k
  WINPR_ASSERT(dst);
333
12.1k
  WINPR_ASSERT(src || (sz == 0));
334
335
12.1k
  const size_t dlen = _wcslen(dst);
336
12.1k
  const size_t slen = _wcsnlen(src, sz);
337
231k
  for (size_t x = 0; x < slen; x++)
338
219k
    dst[dlen + x] = src[x];
339
12.1k
  dst[dlen + slen] = '\0';
340
12.1k
  return dst;
341
12.1k
}
342
343
int _stricmp(const char* string1, const char* string2)
344
856k
{
345
856k
  return strcasecmp(string1, string2);
346
856k
}
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
58.7k
{
357
58.7k
  WINPR_ASSERT(string1);
358
58.7k
  WINPR_ASSERT(string2);
359
360
82.8k
  while (TRUE)
361
82.8k
  {
362
82.8k
    const WCHAR w1 = *string1++;
363
82.8k
    const WCHAR w2 = *string2++;
364
365
82.8k
    if (w1 != w2)
366
46.6k
      return (int)w1 - w2;
367
36.1k
    else if ((w1 == '\0') || (w2 == '\0'))
368
12.0k
      return (int)w1 - w2;
369
82.8k
  }
370
58.7k
}
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
241k
{
394
241k
  const WCHAR* p = str;
395
396
241k
  WINPR_ASSERT(p);
397
398
7.69M
  while (*p)
399
7.45M
    p++;
400
401
241k
  return (size_t)(p - str);
402
241k
}
403
404
/* _wcsnlen -> wcsnlen */
405
406
size_t _wcsnlen(const WCHAR* str, size_t max)
407
2.56M
{
408
2.56M
  WINPR_ASSERT(str);
409
410
2.56M
  size_t x = 0;
411
30.0M
  for (; x < max; x++)
412
29.8M
  {
413
29.8M
    if (str[x] == 0)
414
2.36M
      return x;
415
29.8M
  }
416
417
198k
  return x;
418
2.56M
}
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
541k
{
485
541k
  return strtok_r(strToken, strDelimit, context);
486
541k
}
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
7.78k
{
535
7.78k
  size_t length = 0;
536
537
7.78k
  if (!lpsz)
538
0
    return nullptr;
539
540
7.78k
  length = strlen(lpsz);
541
542
7.78k
  if (length < 1)
543
0
    return (LPSTR) nullptr;
544
545
7.78k
  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
101k
  for (size_t i = 0; i < length; i++)
557
93.4k
  {
558
93.4k
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
559
7.78k
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
560
93.4k
  }
561
562
7.78k
  return lpsz;
563
7.78k
}
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
178
{
613
1.60k
  for (DWORD i = 0; i < cchLength; i++)
614
1.42k
  {
615
1.42k
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
616
1.42k
    value = WINPR_TOUPPERW(value);
617
1.42k
    winpr_Data_Write_UINT16(&lpsz[i], value);
618
1.42k
  }
619
620
178
  return cchLength;
621
178
}
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
64.7k
{
909
64.7k
  WINPR_ASSERT(str);
910
64.7k
  WINPR_ASSERT(buffer || (len == 0));
911
64.7k
  (void)ConvertUtf8ToWChar(str, buffer, len);
912
64.7k
  return buffer;
913
64.7k
}
914
915
WCHAR* wcsndup(const WCHAR* s, size_t n)
916
170
{
917
170
  if (!s)
918
0
    return nullptr;
919
920
170
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
921
170
  if (!copy)
922
0
    return nullptr;
923
170
  memcpy(copy, s, n * sizeof(WCHAR));
924
170
  return copy;
925
170
}
926
927
char* winpr_strnstr(char* haystack, const char* needle, size_t hlen)
928
272k
{
929
272k
  WINPR_ASSERT(haystack || (hlen == 0));
930
272k
  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
272k
  const size_t needle_len = strlen(needle);
938
939
272k
  if (0 == needle_len)
940
0
    return haystack;
941
942
74.3M
  for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
943
74.3M
  {
944
74.3M
    if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
945
255k
      return haystack;
946
74.3M
  }
947
17.6k
  return nullptr;
948
272k
#endif
949
272k
}
950
951
BOOL winpr_str_has_newlines(const char* str)
952
45
{
953
45
  if (!str)
954
0
    return FALSE;
955
45
  do
956
1.37k
  {
957
1.37k
    char c = *str++;
958
1.37k
    switch (c)
959
1.37k
    {
960
1
      case '\r':
961
3
      case '\n':
962
6
      case 0x0b:       /* VT vertical tab */
963
7
      case 0x0c:       /* FF form feed */
964
12
      case (char)0x85: /* NEL next line */
965
12
        return TRUE;
966
132
      case (char)0xE2:
967
132
      {
968
132
        if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
969
2
          return TRUE;
970
132
      }
971
130
      break;
972
973
130
      case '\0':
974
31
        return FALSE;
975
1.19k
      default:
976
1.19k
        break;
977
1.37k
    }
978
1.37k
  } while (1);
979
45
}