Coverage Report

Created: 2026-09-04 06:58

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
13
{
213
13
  if (!str)
214
0
    return FALSE;
215
13
  regex_t regex = WINPR_C_ARRAY_INIT;
216
13
  const char pattern[] = "^([a-z]+://)?([a-zA-Z0-9.-]+)(:[0-9]+)?(/[a-zA-Z0-9./?=&%-]*)?$";
217
218
13
  int ret = regcomp(&regex, pattern, REG_EXTENDED);
219
13
  if (ret)
220
0
  {
221
0
    printf("Could not compile regex\n");
222
0
    return FALSE;
223
0
  }
224
225
13
  ret = regexec(&regex, str, 0, nullptr, 0);
226
13
  regfree(&regex);
227
13
  return ret == 0;
228
13
}
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
160k
{
242
160k
  const size_t used = strnlen(buffer, size);
243
160k
  const size_t add = strnlen(what, size);
244
160k
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
245
160k
  const size_t sep = (used > 0) ? sep_len : 0;
246
247
160k
  if (used + add + sep >= size)
248
21
    return FALSE;
249
250
160k
  if ((used > 0) && (sep_len > 0))
251
26.8k
    strncat(buffer, separator, sep_len);
252
253
160k
  strncat(buffer, what, add);
254
160k
  return TRUE;
255
160k
}
256
257
WINPR_ATTR_FORMAT_ARG(3, 4)
258
int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
259
866k
{
260
866k
  va_list ap = WINPR_C_ARRAY_INIT;
261
262
866k
  va_start(ap, templ);
263
866k
  int rc = winpr_vasprintf(s, slen, templ, ap);
264
866k
  va_end(ap);
265
866k
  return rc;
266
866k
}
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
1.75M
{
271
1.75M
  va_list ap = WINPR_C_ARRAY_INIT;
272
273
1.75M
  *s = nullptr;
274
1.75M
  *slen = 0;
275
276
1.75M
  va_copy(ap, oap);
277
1.75M
  const int length = vsnprintf(nullptr, 0, templ, ap);
278
1.75M
  va_end(ap);
279
1.75M
  if (length < 0)
280
0
    return length;
281
282
1.75M
  char* str = calloc((size_t)length + 1UL, sizeof(char));
283
1.75M
  if (!str)
284
0
    return -1;
285
286
1.75M
  va_copy(ap, oap);
287
1.75M
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
288
1.75M
  va_end(ap);
289
290
1.75M
  if (length != plen)
291
0
  {
292
0
    free(str);
293
0
    return -1;
294
0
  }
295
1.75M
  *s = str;
296
1.75M
  *slen = (size_t)length;
297
1.75M
  return length;
298
1.75M
}
299
300
#ifndef _WIN32
301
302
char* _strdup(const char* strSource)
303
934k
{
304
934k
  if (strSource == nullptr)
305
0
    return nullptr;
306
307
934k
  char* strDestination = strdup(strSource);
308
309
934k
  if (strDestination == nullptr)
310
0
    WLog_ERR(TAG, "strdup");
311
312
934k
  return strDestination;
313
934k
}
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
855k
{
347
855k
  return strcasecmp(string1, string2);
348
855k
}
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
2.22M
{
410
2.22M
  WINPR_ASSERT(str);
411
412
2.22M
  size_t x = 0;
413
33.7M
  for (; x < max; x++)
414
33.5M
  {
415
33.5M
    if (str[x] == 0)
416
2.03M
      return x;
417
33.5M
  }
418
419
194k
  return x;
420
2.22M
}
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
623k
{
487
623k
  return strtok_r(strToken, strDelimit, context);
488
623k
}
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
7.92k
{
537
7.92k
  size_t length = 0;
538
539
7.92k
  if (!lpsz)
540
0
    return nullptr;
541
542
7.92k
  length = strlen(lpsz);
543
544
7.92k
  if (length < 1)
545
0
    return (LPSTR) nullptr;
546
547
7.92k
  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
103k
  for (size_t i = 0; i < length; i++)
559
95.1k
  {
560
95.1k
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
561
39.6k
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
562
95.1k
  }
563
564
7.92k
  return lpsz;
565
7.92k
}
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
172
{
615
1.54k
  for (DWORD i = 0; i < cchLength; i++)
616
1.37k
  {
617
1.37k
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
618
1.37k
    value = WINPR_TOUPPERW(value);
619
1.37k
    winpr_Data_Write_UINT16(&lpsz[i], value);
620
1.37k
  }
621
622
172
  return cchLength;
623
172
}
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
165
{
919
165
  if (!s)
920
0
    return nullptr;
921
922
165
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
923
165
  if (!copy)
924
0
    return nullptr;
925
165
  memcpy(copy, s, n * sizeof(WCHAR));
926
165
  return copy;
927
165
}
928
929
char* winpr_strnstr(char* haystack, const char* needle, size_t hlen)
930
452k
{
931
452k
  WINPR_ASSERT(haystack || (hlen == 0));
932
452k
  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
452k
  const size_t needle_len = strlen(needle);
940
941
452k
  if (0 == needle_len)
942
0
    return haystack;
943
944
73.6M
  for (; (*haystack != '\0') && (hlen >= needle_len); haystack++, hlen--)
945
73.6M
  {
946
73.6M
    if ((haystack[0] == needle[0]) && (0 == strncmp(haystack, needle, needle_len)))
947
429k
      return haystack;
948
73.6M
  }
949
23.7k
  return nullptr;
950
452k
#endif
951
452k
}
952
953
BOOL winpr_str_has_newlines(const char* str)
954
44
{
955
44
  if (!str)
956
0
    return FALSE;
957
44
  do
958
1.42k
  {
959
1.42k
    char c = *str++;
960
1.42k
    switch (c)
961
1.42k
    {
962
1
      case '\r':
963
2
      case '\n':
964
5
      case 0x0b:       /* VT vertical tab */
965
6
      case 0x0c:       /* FF form feed */
966
13
      case (char)0x85: /* NEL next line */
967
13
        return TRUE;
968
137
      case (char)0xE2:
969
137
      {
970
137
        if ((str[0] == (char)0x80) && ((str[1] == (char)0xA8) || (str[1] == (char)0xA9)))
971
2
          return TRUE;
972
137
      }
973
135
      break;
974
975
135
      case '\0':
976
29
        return FALSE;
977
1.24k
      default:
978
1.24k
        break;
979
1.42k
    }
980
1.42k
  } while (1);
981
44
}