Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/crt/string.c
Line
Count
Source (jump to first uncovered line)
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 <errno.h>
24
#include <stdio.h>
25
#include <ctype.h>
26
#include <wctype.h>
27
#include <wchar.h>
28
29
#include <winpr/crt.h>
30
#include <winpr/endian.h>
31
32
#if defined(WITH_URIPARSER)
33
#include <uriparser/Uri.h>
34
#endif
35
36
/* String Manipulation (CRT): http://msdn.microsoft.com/en-us/library/f0151s4x.aspx */
37
38
#include "../log.h"
39
#define TAG WINPR_TAG("crt")
40
41
#if defined(WITH_URIPARSER)
42
char* winpr_str_url_decode(const char* str, size_t len)
43
{
44
  char* dst = strndup(str, len);
45
  if (!dst)
46
    return NULL;
47
48
  if (!uriUnescapeInPlaceExA(dst, URI_FALSE, URI_FALSE))
49
  {
50
    free(dst);
51
    return NULL;
52
  }
53
54
  return dst;
55
}
56
57
char* winpr_str_url_encode(const char* str, size_t len)
58
{
59
  char* dst = calloc(len + 1, sizeof(char) * 3);
60
  if (!dst)
61
    return NULL;
62
63
  if (!uriEscapeA(str, dst, URI_FALSE, URI_FALSE))
64
  {
65
    free(dst);
66
    return NULL;
67
  }
68
  return dst;
69
}
70
71
#else
72
static const char rfc3986[] = {
73
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x2e, 0x00,
76
  0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77
  0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
78
  0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x5f,
79
  0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
80
  0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x00, 0x00, 0x00, 0x7e, 0x00,
81
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
82
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
83
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
84
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
86
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
88
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
89
};
90
91
static char hex2bin(char what)
92
0
{
93
0
  if (what >= 'a')
94
0
    what -= 'a' - 'A';
95
0
  if (what >= 'A')
96
0
    what -= ('A' - 10);
97
0
  else
98
0
    what -= '0';
99
0
  return what;
100
0
}
101
102
static char unescape(const char* what, size_t* px)
103
0
{
104
0
  if ((*what == '%') && (isxdigit(what[1]) && isxdigit(what[2])))
105
0
  {
106
0
    *px += 2;
107
0
    return 16 * hex2bin(what[1]) + hex2bin(what[2]);
108
0
  }
109
110
0
  return *what;
111
0
}
112
113
char* winpr_str_url_decode(const char* str, size_t len)
114
0
{
115
0
  char* dst = calloc(len + 1, sizeof(char));
116
0
  if (!dst)
117
0
    return NULL;
118
119
0
  size_t pos = 0;
120
0
  for (size_t x = 0; x < strnlen(str, len); x++)
121
0
  {
122
0
    const char* cur = &str[x];
123
0
    dst[pos++] = unescape(cur, &x);
124
0
  }
125
0
  return dst;
126
0
}
127
128
static char* escape(char* dst, char what)
129
0
{
130
0
  if (rfc3986[what & 0xff])
131
0
  {
132
0
    *dst = what;
133
0
    return dst + 1;
134
0
  }
135
136
0
  sprintf(dst, "%%%02" PRIX8, (BYTE)(what & 0xff));
137
0
  return dst + 3;
138
0
}
139
140
char* winpr_str_url_encode(const char* str, size_t len)
141
0
{
142
0
  char* dst = calloc(len + 1, sizeof(char) * 3);
143
0
  if (!dst)
144
0
    return NULL;
145
146
0
  char* ptr = dst;
147
0
  for (size_t x = 0; x < strnlen(str, len); x++)
148
0
  {
149
0
    const char cur = str[x];
150
0
    ptr = escape(ptr, cur);
151
0
  }
152
0
  return dst;
153
0
}
154
#endif
155
156
BOOL winpr_str_append(const char* what, char* buffer, size_t size, const char* separator)
157
0
{
158
0
  const size_t used = strnlen(buffer, size);
159
0
  const size_t add = strnlen(what, size);
160
0
  const size_t sep_len = separator ? strnlen(separator, size) : 0;
161
0
  const size_t sep = (used > 0) ? sep_len : 0;
162
163
0
  if (used + add + sep >= size)
164
0
    return FALSE;
165
166
0
  if ((used > 0) && (sep_len > 0))
167
0
    strncat(buffer, separator, sep_len);
168
169
0
  strncat(buffer, what, add);
170
0
  return TRUE;
171
0
}
172
173
WINPR_ATTR_FORMAT_ARG(3, 4)
174
int winpr_asprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, ...)
175
0
{
176
0
  va_list ap = { 0 };
177
178
0
  va_start(ap, templ);
179
0
  int rc = winpr_vasprintf(s, slen, templ, ap);
180
0
  va_end(ap);
181
0
  return rc;
182
0
}
183
184
WINPR_ATTR_FORMAT_ARG(3, 0)
185
int winpr_vasprintf(char** s, size_t* slen, WINPR_FORMAT_ARG const char* templ, va_list oap)
186
0
{
187
0
  va_list ap = { 0 };
188
189
0
  *s = NULL;
190
0
  *slen = 0;
191
192
0
  va_copy(ap, oap);
193
0
  const int length = vsnprintf(NULL, 0, templ, ap);
194
0
  va_end(ap);
195
0
  if (length < 0)
196
0
    return length;
197
198
0
  char* str = calloc((size_t)length + 1UL, sizeof(char));
199
0
  if (!str)
200
0
    return -1;
201
202
0
  va_copy(ap, oap);
203
0
  const int plen = vsnprintf(str, (size_t)length + 1UL, templ, ap);
204
0
  va_end(ap);
205
206
0
  if (length != plen)
207
0
  {
208
0
    free(str);
209
0
    return -1;
210
0
  }
211
0
  *s = str;
212
0
  *slen = (size_t)length;
213
0
  return length;
214
0
}
215
216
#ifndef _WIN32
217
218
char* _strdup(const char* strSource)
219
0
{
220
0
  if (strSource == NULL)
221
0
    return NULL;
222
223
0
  char* strDestination = strdup(strSource);
224
225
0
  if (strDestination == NULL)
226
0
    WLog_ERR(TAG, "strdup");
227
228
0
  return strDestination;
229
0
}
230
231
WCHAR* _wcsdup(const WCHAR* strSource)
232
0
{
233
0
  if (!strSource)
234
0
    return NULL;
235
236
0
  size_t len = _wcslen(strSource);
237
0
  WCHAR* strDestination = calloc(len + 1, sizeof(WCHAR));
238
239
0
  if (strDestination != NULL)
240
0
    memcpy(strDestination, strSource, len * sizeof(WCHAR));
241
242
0
  if (strDestination == NULL)
243
0
    WLog_ERR(TAG, "wcsdup");
244
245
0
  return strDestination;
246
0
}
247
248
WCHAR* _wcsncat(WCHAR* dst, const WCHAR* src, size_t sz)
249
0
{
250
0
  WINPR_ASSERT(dst);
251
0
  WINPR_ASSERT(src || (sz == 0));
252
253
0
  const size_t dlen = _wcslen(dst);
254
0
  const size_t slen = _wcsnlen(src, sz);
255
0
  for (size_t x = 0; x < slen; x++)
256
0
    dst[dlen + x] = src[x];
257
0
  dst[dlen + slen] = '\0';
258
0
  return dst;
259
0
}
260
261
int _stricmp(const char* string1, const char* string2)
262
0
{
263
0
  return strcasecmp(string1, string2);
264
0
}
265
266
int _strnicmp(const char* string1, const char* string2, size_t count)
267
0
{
268
0
  return strncasecmp(string1, string2, count);
269
0
}
270
271
/* _wcscmp -> wcscmp */
272
273
int _wcscmp(const WCHAR* string1, const WCHAR* string2)
274
0
{
275
0
  WINPR_ASSERT(string1);
276
0
  WINPR_ASSERT(string2);
277
278
0
  while (TRUE)
279
0
  {
280
0
    const WCHAR w1 = *string1++;
281
0
    const WCHAR w2 = *string2++;
282
283
0
    if (w1 != w2)
284
0
      return (int)w1 - w2;
285
0
    else if ((w1 == '\0') || (w2 == '\0'))
286
0
      return (int)w1 - w2;
287
0
  }
288
0
}
289
290
int _wcsncmp(const WCHAR* string1, const WCHAR* string2, size_t count)
291
0
{
292
0
  WINPR_ASSERT(string1);
293
0
  WINPR_ASSERT(string2);
294
295
0
  for (size_t x = 0; x < count; x++)
296
0
  {
297
0
    const WCHAR a = string1[x];
298
0
    const WCHAR b = string2[x];
299
300
0
    if (a != b)
301
0
      return (int)a - b;
302
0
    else if ((a == '\0') || (b == '\0'))
303
0
      return (int)a - b;
304
0
  }
305
0
  return 0;
306
0
}
307
308
/* _wcslen -> wcslen */
309
310
size_t _wcslen(const WCHAR* str)
311
0
{
312
0
  const WCHAR* p = str;
313
314
0
  WINPR_ASSERT(p);
315
316
0
  while (*p)
317
0
    p++;
318
319
0
  return (size_t)(p - str);
320
0
}
321
322
/* _wcsnlen -> wcsnlen */
323
324
size_t _wcsnlen(const WCHAR* str, size_t max)
325
0
{
326
0
  WINPR_ASSERT(str);
327
328
0
  size_t x = 0;
329
0
  for (; x < max; x++)
330
0
  {
331
0
    if (str[x] == 0)
332
0
      return x;
333
0
  }
334
335
0
  return x;
336
0
}
337
338
/* _wcsstr -> wcsstr */
339
340
WCHAR* _wcsstr(const WCHAR* str, const WCHAR* strSearch)
341
0
{
342
0
  WINPR_ASSERT(str);
343
0
  WINPR_ASSERT(strSearch);
344
345
0
  if (strSearch[0] == '\0')
346
0
    return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
347
348
0
  const size_t searchLen = _wcslen(strSearch);
349
0
  while (*str)
350
0
  {
351
0
    if (_wcsncmp(str, strSearch, searchLen) == 0)
352
0
      return WINPR_CAST_CONST_PTR_AWAY(str, WCHAR*);
353
0
    str++;
354
0
  }
355
0
  return NULL;
356
0
}
357
358
/* _wcschr -> wcschr */
359
360
WCHAR* _wcschr(const WCHAR* str, WCHAR c)
361
0
{
362
0
  union
363
0
  {
364
0
    const WCHAR* cc;
365
0
    WCHAR* c;
366
0
  } cnv;
367
0
  const WCHAR* p = str;
368
369
0
  while (*p && (*p != c))
370
0
    p++;
371
372
0
  cnv.cc = (*p == c) ? p : NULL;
373
0
  return cnv.c;
374
0
}
375
376
/* _wcsrchr -> wcsrchr */
377
378
WCHAR* _wcsrchr(const WCHAR* str, WCHAR c)
379
0
{
380
0
  union
381
0
  {
382
0
    const WCHAR* cc;
383
0
    WCHAR* c;
384
0
  } cnv;
385
0
  const WCHAR* p = NULL;
386
387
0
  if (!str)
388
0
    return NULL;
389
390
0
  for (; *str != '\0'; str++)
391
0
  {
392
0
    const WCHAR ch = *str;
393
0
    if (ch == c)
394
0
      p = str;
395
0
  }
396
397
0
  cnv.cc = p;
398
0
  return cnv.c;
399
0
}
400
401
char* strtok_s(char* strToken, const char* strDelimit, char** context)
402
0
{
403
0
  return strtok_r(strToken, strDelimit, context);
404
0
}
405
406
WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, WCHAR** context)
407
0
{
408
0
  WCHAR* nextToken = NULL;
409
0
  WCHAR value = 0;
410
411
0
  if (!strToken)
412
0
    strToken = *context;
413
414
0
  value = *strToken;
415
416
0
  while (*strToken && _wcschr(strDelimit, value))
417
0
  {
418
0
    strToken++;
419
0
    value = *strToken;
420
0
  }
421
422
0
  if (!*strToken)
423
0
    return NULL;
424
425
0
  nextToken = strToken++;
426
0
  value = *strToken;
427
428
0
  while (*strToken && !(_wcschr(strDelimit, value)))
429
0
  {
430
0
    strToken++;
431
0
    value = *strToken;
432
0
  }
433
434
0
  if (*strToken)
435
0
    *strToken++ = 0;
436
437
0
  *context = strToken;
438
0
  return nextToken;
439
0
}
440
441
#endif
442
443
#if !defined(_WIN32) || defined(_UWP)
444
445
/* Windows API Sets - api-ms-win-core-string-l2-1-0.dll
446
 * http://msdn.microsoft.com/en-us/library/hh802935/
447
 */
448
449
#include "casing.h"
450
451
LPSTR CharUpperA(LPSTR lpsz)
452
0
{
453
0
  size_t length = 0;
454
455
0
  if (!lpsz)
456
0
    return NULL;
457
458
0
  length = strlen(lpsz);
459
460
0
  if (length < 1)
461
0
    return (LPSTR)NULL;
462
463
0
  if (length == 1)
464
0
  {
465
0
    char c = *lpsz;
466
467
0
    if ((c >= 'a') && (c <= 'z'))
468
0
      c = (char)(c - 'a' + 'A');
469
470
0
    *lpsz = c;
471
0
    return lpsz;
472
0
  }
473
474
0
  for (size_t i = 0; i < length; i++)
475
0
  {
476
0
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
477
0
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
478
0
  }
479
480
0
  return lpsz;
481
0
}
482
483
LPWSTR CharUpperW(LPWSTR lpsz)
484
0
{
485
0
  size_t length = 0;
486
487
0
  if (!lpsz)
488
0
    return NULL;
489
490
0
  length = _wcslen(lpsz);
491
492
0
  if (length < 1)
493
0
    return (LPWSTR)NULL;
494
495
0
  if (length == 1)
496
0
  {
497
0
    WCHAR c = *lpsz;
498
499
0
    if ((c >= L'a') && (c <= L'z'))
500
0
      c = c - L'a' + L'A';
501
502
0
    *lpsz = c;
503
0
    return lpsz;
504
0
  }
505
506
0
  for (size_t i = 0; i < length; i++)
507
0
  {
508
0
    if ((lpsz[i] >= L'a') && (lpsz[i] <= L'z'))
509
0
      lpsz[i] = lpsz[i] - L'a' + L'A';
510
0
  }
511
512
0
  return lpsz;
513
0
}
514
515
DWORD CharUpperBuffA(LPSTR lpsz, DWORD cchLength)
516
0
{
517
0
  if (cchLength < 1)
518
0
    return 0;
519
520
0
  for (DWORD i = 0; i < cchLength; i++)
521
0
  {
522
0
    if ((lpsz[i] >= 'a') && (lpsz[i] <= 'z'))
523
0
      lpsz[i] = (char)(lpsz[i] - 'a' + 'A');
524
0
  }
525
526
0
  return cchLength;
527
0
}
528
529
DWORD CharUpperBuffW(LPWSTR lpsz, DWORD cchLength)
530
0
{
531
0
  for (DWORD i = 0; i < cchLength; i++)
532
0
  {
533
0
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
534
0
    value = WINPR_TOUPPERW(value);
535
0
    winpr_Data_Write_UINT16(&lpsz[i], value);
536
0
  }
537
538
0
  return cchLength;
539
0
}
540
541
LPSTR CharLowerA(LPSTR lpsz)
542
0
{
543
0
  size_t length = 0;
544
545
0
  if (!lpsz)
546
0
    return (LPSTR)NULL;
547
548
0
  length = strlen(lpsz);
549
550
0
  if (length < 1)
551
0
    return (LPSTR)NULL;
552
553
0
  if (length == 1)
554
0
  {
555
0
    char c = *lpsz;
556
557
0
    if ((c >= 'A') && (c <= 'Z'))
558
0
      c = (char)(c - 'A' + 'a');
559
560
0
    *lpsz = c;
561
0
    return lpsz;
562
0
  }
563
564
0
  for (size_t i = 0; i < length; i++)
565
0
  {
566
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
567
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
568
0
  }
569
570
0
  return lpsz;
571
0
}
572
573
LPWSTR CharLowerW(LPWSTR lpsz)
574
0
{
575
0
  const size_t len = _wcsnlen(lpsz, UINT32_MAX + 1);
576
0
  if (len > UINT32_MAX)
577
0
    return NULL;
578
0
  CharLowerBuffW(lpsz, (UINT32)len);
579
0
  return lpsz;
580
0
}
581
582
DWORD CharLowerBuffA(LPSTR lpsz, DWORD cchLength)
583
0
{
584
0
  if (cchLength < 1)
585
0
    return 0;
586
587
0
  for (DWORD i = 0; i < cchLength; i++)
588
0
  {
589
0
    if ((lpsz[i] >= 'A') && (lpsz[i] <= 'Z'))
590
0
      lpsz[i] = (char)(lpsz[i] - 'A' + 'a');
591
0
  }
592
593
0
  return cchLength;
594
0
}
595
596
DWORD CharLowerBuffW(LPWSTR lpsz, DWORD cchLength)
597
0
{
598
0
  for (DWORD i = 0; i < cchLength; i++)
599
0
  {
600
0
    WCHAR value = winpr_Data_Get_UINT16(&lpsz[i]);
601
0
    value = WINPR_TOLOWERW(value);
602
0
    winpr_Data_Write_UINT16(&lpsz[i], value);
603
0
  }
604
605
0
  return cchLength;
606
0
}
607
608
BOOL IsCharAlphaA(CHAR ch)
609
0
{
610
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')))
611
0
    return 1;
612
0
  else
613
0
    return 0;
614
0
}
615
616
BOOL IsCharAlphaW(WCHAR ch)
617
0
{
618
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')))
619
0
    return 1;
620
0
  else
621
0
    return 0;
622
0
}
623
624
BOOL IsCharAlphaNumericA(CHAR ch)
625
0
{
626
0
  if (((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) ||
627
0
      ((ch >= '0') && (ch <= '9')))
628
0
    return 1;
629
0
  else
630
0
    return 0;
631
0
}
632
633
BOOL IsCharAlphaNumericW(WCHAR ch)
634
0
{
635
0
  if (((ch >= L'a') && (ch <= L'z')) || ((ch >= L'A') && (ch <= L'Z')) ||
636
0
      ((ch >= L'0') && (ch <= L'9')))
637
0
    return 1;
638
0
  else
639
0
    return 0;
640
0
}
641
642
BOOL IsCharUpperA(CHAR ch)
643
0
{
644
0
  if ((ch >= 'A') && (ch <= 'Z'))
645
0
    return 1;
646
0
  else
647
0
    return 0;
648
0
}
649
650
BOOL IsCharUpperW(WCHAR ch)
651
0
{
652
0
  if ((ch >= L'A') && (ch <= L'Z'))
653
0
    return 1;
654
0
  else
655
0
    return 0;
656
0
}
657
658
BOOL IsCharLowerA(CHAR ch)
659
0
{
660
0
  if ((ch >= 'a') && (ch <= 'z'))
661
0
    return 1;
662
0
  else
663
0
    return 0;
664
0
}
665
666
BOOL IsCharLowerW(WCHAR ch)
667
0
{
668
0
  if ((ch >= L'a') && (ch <= L'z'))
669
0
    return 1;
670
0
  else
671
0
    return 0;
672
0
}
673
674
#endif
675
676
size_t ConvertLineEndingToLF(char* str, size_t size)
677
0
{
678
0
  size_t skip = 0;
679
680
0
  WINPR_ASSERT(str || (size == 0));
681
0
  for (size_t x = 0; x < size; x++)
682
0
  {
683
0
    char c = str[x];
684
0
    switch (c)
685
0
    {
686
0
      case '\r':
687
0
        str[x - skip] = '\n';
688
0
        if ((x + 1 < size) && (str[x + 1] == '\n'))
689
0
          skip++;
690
0
        break;
691
0
      default:
692
0
        str[x - skip] = c;
693
0
        break;
694
0
    }
695
0
  }
696
0
  return size - skip;
697
0
}
698
699
char* ConvertLineEndingToCRLF(const char* str, size_t* size)
700
0
{
701
0
  WINPR_ASSERT(size);
702
0
  const size_t s = *size;
703
0
  WINPR_ASSERT(str || (s == 0));
704
705
0
  *size = 0;
706
0
  if (s == 0)
707
0
    return NULL;
708
709
0
  size_t linebreaks = 0;
710
0
  for (size_t x = 0; x < s - 1; x++)
711
0
  {
712
0
    char c = str[x];
713
0
    switch (c)
714
0
    {
715
0
      case '\r':
716
0
      case '\n':
717
0
        linebreaks++;
718
0
        break;
719
0
      default:
720
0
        break;
721
0
    }
722
0
  }
723
0
  char* cnv = calloc(s + linebreaks * 2ull + 1ull, sizeof(char));
724
0
  if (!cnv)
725
0
    return NULL;
726
727
0
  size_t pos = 0;
728
0
  for (size_t x = 0; x < s; x++)
729
0
  {
730
0
    const char c = str[x];
731
0
    switch (c)
732
0
    {
733
0
      case '\r':
734
0
        cnv[pos++] = '\r';
735
0
        cnv[pos++] = '\n';
736
0
        break;
737
0
      case '\n':
738
        /* Do not duplicate existing \r\n sequences */
739
0
        if ((x > 0) && (str[x - 1] != '\r'))
740
0
        {
741
0
          cnv[pos++] = '\r';
742
0
          cnv[pos++] = '\n';
743
0
        }
744
0
        break;
745
0
      default:
746
0
        cnv[pos++] = c;
747
0
        break;
748
0
    }
749
0
  }
750
0
  *size = pos;
751
0
  return cnv;
752
0
}
753
754
char* StrSep(char** stringp, const char* delim)
755
0
{
756
0
  char* start = *stringp;
757
0
  char* p = NULL;
758
0
  p = (start != NULL) ? strpbrk(start, delim) : NULL;
759
760
0
  if (!p)
761
0
    *stringp = NULL;
762
0
  else
763
0
  {
764
0
    *p = '\0';
765
0
    *stringp = p + 1;
766
0
  }
767
768
0
  return start;
769
0
}
770
771
INT64 GetLine(char** lineptr, size_t* size, FILE* stream)
772
0
{
773
#if defined(_WIN32)
774
  char c;
775
  char* n;
776
  size_t step = 32;
777
  size_t used = 0;
778
779
  if (!lineptr || !size)
780
  {
781
    errno = EINVAL;
782
    return -1;
783
  }
784
785
  do
786
  {
787
    if (used + 2 >= *size)
788
    {
789
      *size += step;
790
      n = realloc(*lineptr, *size);
791
792
      if (!n)
793
      {
794
        return -1;
795
      }
796
797
      *lineptr = n;
798
    }
799
800
    c = fgetc(stream);
801
802
    if (c != EOF)
803
      (*lineptr)[used++] = c;
804
  } while ((c != '\n') && (c != '\r') && (c != EOF));
805
806
  (*lineptr)[used] = '\0';
807
  return used;
808
#elif !defined(ANDROID) && !defined(IOS)
809
  return getline(lineptr, size, stream);
810
#else
811
  return -1;
812
#endif
813
0
}
814
815
#if !defined(WINPR_HAVE_STRNDUP)
816
char* strndup(const char* src, size_t n)
817
{
818
  char* dst = calloc(n + 1, sizeof(char));
819
  if (dst)
820
    strncpy(dst, src, n);
821
  return dst;
822
}
823
#endif
824
825
const WCHAR* InitializeConstWCharFromUtf8(const char* str, WCHAR* buffer, size_t len)
826
0
{
827
0
  WINPR_ASSERT(str);
828
0
  WINPR_ASSERT(buffer || (len == 0));
829
0
  (void)ConvertUtf8ToWChar(str, buffer, len);
830
0
  return buffer;
831
0
}
832
833
WCHAR* wcsndup(const WCHAR* s, size_t n)
834
0
{
835
0
  if (!s)
836
0
    return NULL;
837
838
0
  WCHAR* copy = calloc(n + 1, sizeof(WCHAR));
839
0
  if (!copy)
840
0
    return NULL;
841
0
  memcpy(copy, s, n * sizeof(WCHAR));
842
0
  return copy;
843
0
}