Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/utils/cmdline.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Command-Line Utils
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
22
#include <winpr/crt.h>
23
#include <winpr/assert.h>
24
#include <winpr/cmdline.h>
25
26
#include "../log.h"
27
28
#define TAG WINPR_TAG("commandline")
29
30
/**
31
 * Command-line syntax: some basic concepts:
32
 * https://pythonconquerstheuniverse.wordpress.com/2010/07/25/command-line-syntax-some-basic-concepts/
33
 */
34
35
/**
36
 * Command-Line Syntax:
37
 *
38
 * <sigil><keyword><separator><value>
39
 *
40
 * <sigil>: '/' or '-' or ('+' | '-')
41
 *
42
 * <keyword>: option, named argument, flag
43
 *
44
 * <separator>: ':' or '='
45
 *
46
 * <value>: argument value
47
 *
48
 */
49
50
static void log_error(DWORD flags, LPCSTR message, int index, LPCSTR argv)
51
0
{
52
0
  if ((flags & COMMAND_LINE_SILENCE_PARSER) == 0)
53
0
    WLog_ERR(TAG, message, index, argv);
54
0
}
55
56
int CommandLineParseArgumentsA(int argc, LPSTR* argv, COMMAND_LINE_ARGUMENT_A* options, DWORD flags,
57
                               void* context, COMMAND_LINE_PRE_FILTER_FN_A preFilter,
58
                               COMMAND_LINE_POST_FILTER_FN_A postFilter)
59
0
{
60
0
  int status = 0;
61
0
  int count = 0;
62
0
  BOOL notescaped = FALSE;
63
0
  const char* sigil = NULL;
64
0
  size_t sigil_length = 0;
65
0
  char* keyword = NULL;
66
0
  size_t keyword_index = 0;
67
0
  char* separator = NULL;
68
0
  char* value = NULL;
69
0
  int toggle = 0;
70
71
0
  if (!argv)
72
0
    return status;
73
74
0
  if (argc == 1)
75
0
  {
76
0
    if (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD)
77
0
      status = 0;
78
0
    else
79
0
      status = COMMAND_LINE_STATUS_PRINT_HELP;
80
81
0
    return status;
82
0
  }
83
84
0
  for (int i = 1; i < argc; i++)
85
0
  {
86
0
    size_t keyword_length = 0;
87
0
    BOOL found = FALSE;
88
0
    BOOL escaped = TRUE;
89
90
0
    if (preFilter)
91
0
    {
92
0
      count = preFilter(context, i, argc, argv);
93
94
0
      if (count < 0)
95
0
      {
96
0
        log_error(flags, "Failed for index %d [%s]: PreFilter rule could not be applied", i,
97
0
                  argv[i]);
98
0
        status = COMMAND_LINE_ERROR;
99
0
        return status;
100
0
      }
101
102
0
      if (count > 0)
103
0
      {
104
0
        i += (count - 1);
105
0
        continue;
106
0
      }
107
0
    }
108
109
0
    sigil = argv[i];
110
0
    size_t length = strlen(argv[i]);
111
112
0
    if ((sigil[0] == '/') && (flags & COMMAND_LINE_SIGIL_SLASH))
113
0
    {
114
0
      sigil_length = 1;
115
0
    }
116
0
    else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_DASH))
117
0
    {
118
0
      sigil_length = 1;
119
120
0
      if (length > 2)
121
0
      {
122
0
        if ((sigil[1] == '-') && (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH))
123
0
          sigil_length = 2;
124
0
      }
125
0
    }
126
0
    else if ((sigil[0] == '+') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
127
0
    {
128
0
      sigil_length = 1;
129
0
    }
130
0
    else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
131
0
    {
132
0
      sigil_length = 1;
133
0
    }
134
0
    else if (flags & COMMAND_LINE_SIGIL_NONE)
135
0
    {
136
0
      sigil_length = 0;
137
0
    }
138
0
    else if (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED)
139
0
    {
140
0
      if (notescaped)
141
0
      {
142
0
        log_error(flags, "Failed at index %d [%s]: Unescaped sigil", i, argv[i]);
143
0
        return COMMAND_LINE_ERROR;
144
0
      }
145
146
0
      sigil_length = 0;
147
0
      escaped = FALSE;
148
0
      notescaped = TRUE;
149
0
    }
150
0
    else
151
0
    {
152
0
      log_error(flags, "Failed at index %d [%s]: Invalid sigil", i, argv[i]);
153
0
      return COMMAND_LINE_ERROR;
154
0
    }
155
156
0
    if ((sigil_length > 0) || (flags & COMMAND_LINE_SIGIL_NONE) ||
157
0
        (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED))
158
0
    {
159
0
      if (length < (sigil_length + 1))
160
0
      {
161
0
        if ((flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD))
162
0
          continue;
163
164
0
        return COMMAND_LINE_ERROR_NO_KEYWORD;
165
0
      }
166
167
0
      keyword_index = sigil_length;
168
0
      keyword = &argv[i][keyword_index];
169
0
      toggle = -1;
170
171
0
      if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
172
0
      {
173
0
        if (strncmp(keyword, "enable-", 7) == 0)
174
0
        {
175
0
          toggle = TRUE;
176
0
          keyword_index += 7;
177
0
          keyword = &argv[i][keyword_index];
178
0
        }
179
0
        else if (strncmp(keyword, "disable-", 8) == 0)
180
0
        {
181
0
          toggle = FALSE;
182
0
          keyword_index += 8;
183
0
          keyword = &argv[i][keyword_index];
184
0
        }
185
0
      }
186
187
0
      separator = NULL;
188
189
0
      if ((flags & COMMAND_LINE_SEPARATOR_COLON) && (!separator))
190
0
        separator = strchr(keyword, ':');
191
192
0
      if ((flags & COMMAND_LINE_SEPARATOR_EQUAL) && (!separator))
193
0
        separator = strchr(keyword, '=');
194
195
0
      if (separator)
196
0
      {
197
0
        SSIZE_T separator_index = (separator - argv[i]);
198
0
        SSIZE_T value_index = separator_index + 1;
199
0
        keyword_length = WINPR_ASSERTING_INT_CAST(size_t, (separator - keyword));
200
0
        value = &argv[i][value_index];
201
0
      }
202
0
      else
203
0
      {
204
0
        if (length < keyword_index)
205
0
        {
206
0
          log_error(flags, "Failed at index %d [%s]: Argument required", i, argv[i]);
207
0
          return COMMAND_LINE_ERROR;
208
0
        }
209
210
0
        keyword_length = length - keyword_index;
211
0
        value = NULL;
212
0
      }
213
214
0
      if (!escaped)
215
0
        continue;
216
217
0
      for (size_t j = 0; options[j].Name != NULL; j++)
218
0
      {
219
0
        COMMAND_LINE_ARGUMENT_A* cur = &options[j];
220
0
        BOOL match = FALSE;
221
222
0
        if (strncmp(cur->Name, keyword, keyword_length) == 0)
223
0
        {
224
0
          if (strlen(cur->Name) == keyword_length)
225
0
            match = TRUE;
226
0
        }
227
228
0
        if ((!match) && (cur->Alias != NULL))
229
0
        {
230
0
          if (strncmp(cur->Alias, keyword, keyword_length) == 0)
231
0
          {
232
0
            if (strlen(cur->Alias) == keyword_length)
233
0
              match = TRUE;
234
0
          }
235
0
        }
236
237
0
        if (!match)
238
0
          continue;
239
240
0
        found = match;
241
0
        cur->Index = i;
242
243
0
        if ((flags & COMMAND_LINE_SEPARATOR_SPACE) && ((i + 1) < argc))
244
0
        {
245
0
          BOOL argument = 0;
246
0
          int value_present = 1;
247
248
0
          if (flags & COMMAND_LINE_SIGIL_DASH)
249
0
          {
250
0
            if (strncmp(argv[i + 1], "-", 1) == 0)
251
0
              value_present = 0;
252
0
          }
253
254
0
          if (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH)
255
0
          {
256
0
            if (strncmp(argv[i + 1], "--", 2) == 0)
257
0
              value_present = 0;
258
0
          }
259
260
0
          if (flags & COMMAND_LINE_SIGIL_SLASH)
261
0
          {
262
0
            if (strncmp(argv[i + 1], "/", 1) == 0)
263
0
              value_present = 0;
264
0
          }
265
266
0
          if ((cur->Flags & COMMAND_LINE_VALUE_REQUIRED) ||
267
0
              (cur->Flags & COMMAND_LINE_VALUE_OPTIONAL))
268
0
            argument = TRUE;
269
0
          else
270
0
            argument = FALSE;
271
272
0
          if (value_present && argument)
273
0
          {
274
0
            i++;
275
0
            value = argv[i];
276
0
          }
277
0
          else if (!value_present && (cur->Flags & COMMAND_LINE_VALUE_OPTIONAL))
278
0
          {
279
0
            value = NULL;
280
0
          }
281
0
          else if (!value_present && argument)
282
0
          {
283
0
            log_error(flags, "Failed at index %d [%s]: Argument required", i, argv[i]);
284
0
            return COMMAND_LINE_ERROR;
285
0
          }
286
0
        }
287
288
0
        if (!(flags & COMMAND_LINE_SEPARATOR_SPACE))
289
0
        {
290
0
          if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG))
291
0
          {
292
0
            log_error(flags, "Failed at index %d [%s]: Unexpected value", i, argv[i]);
293
0
            return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
294
0
          }
295
0
        }
296
0
        else
297
0
        {
298
0
          if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG))
299
0
          {
300
0
            i--;
301
0
            value = NULL;
302
0
          }
303
0
        }
304
305
0
        if (!value && (cur->Flags & COMMAND_LINE_VALUE_REQUIRED))
306
0
        {
307
0
          log_error(flags, "Failed at index %d [%s]: Missing value", i, argv[i]);
308
0
          status = COMMAND_LINE_ERROR_MISSING_VALUE;
309
0
          return status;
310
0
        }
311
312
0
        cur->Flags |= COMMAND_LINE_ARGUMENT_PRESENT;
313
314
0
        if (value)
315
0
        {
316
0
          if (!(cur->Flags & (COMMAND_LINE_VALUE_OPTIONAL | COMMAND_LINE_VALUE_REQUIRED)))
317
0
          {
318
0
            log_error(flags, "Failed at index %d [%s]: Unexpected value", i, argv[i]);
319
0
            return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
320
0
          }
321
322
0
          cur->Value = value;
323
0
          cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
324
0
        }
325
0
        else
326
0
        {
327
0
          if (cur->Flags & COMMAND_LINE_VALUE_FLAG)
328
0
          {
329
0
            cur->Value = (LPSTR)1;
330
0
            cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
331
0
          }
332
0
          else if (cur->Flags & COMMAND_LINE_VALUE_BOOL)
333
0
          {
334
0
            if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
335
0
            {
336
0
              if (toggle == -1)
337
0
                cur->Value = BoolValueTrue;
338
0
              else if (!toggle)
339
0
                cur->Value = BoolValueFalse;
340
0
              else
341
0
                cur->Value = BoolValueTrue;
342
0
            }
343
0
            else
344
0
            {
345
0
              if (sigil[0] == '+')
346
0
                cur->Value = BoolValueTrue;
347
0
              else if (sigil[0] == '-')
348
0
                cur->Value = BoolValueFalse;
349
0
              else
350
0
                cur->Value = BoolValueTrue;
351
0
            }
352
353
0
            cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
354
0
          }
355
0
        }
356
357
0
        if (postFilter)
358
0
        {
359
0
          count = postFilter(context, &options[j]);
360
361
0
          if (count < 0)
362
0
          {
363
0
            log_error(flags,
364
0
                      "Failed at index %d [%s]: PostFilter rule could not be applied",
365
0
                      i, argv[i]);
366
0
            status = COMMAND_LINE_ERROR;
367
0
            return status;
368
0
          }
369
0
        }
370
371
0
        if (cur->Flags & COMMAND_LINE_PRINT)
372
0
          return COMMAND_LINE_STATUS_PRINT;
373
0
        else if (cur->Flags & COMMAND_LINE_PRINT_HELP)
374
0
          return COMMAND_LINE_STATUS_PRINT_HELP;
375
0
        else if (cur->Flags & COMMAND_LINE_PRINT_VERSION)
376
0
          return COMMAND_LINE_STATUS_PRINT_VERSION;
377
0
        else if (cur->Flags & COMMAND_LINE_PRINT_BUILDCONFIG)
378
0
          return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
379
0
      }
380
381
0
      if (!found && (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD) == 0)
382
0
      {
383
0
        log_error(flags, "Failed at index %d [%s]: Unexpected keyword", i, argv[i]);
384
0
        return COMMAND_LINE_ERROR_NO_KEYWORD;
385
0
      }
386
0
    }
387
0
  }
388
389
0
  return status;
390
0
}
391
392
int CommandLineParseArgumentsW(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED LPWSTR* argv,
393
                               WINPR_ATTR_UNUSED COMMAND_LINE_ARGUMENT_W* options,
394
                               WINPR_ATTR_UNUSED DWORD flags, WINPR_ATTR_UNUSED void* context,
395
                               WINPR_ATTR_UNUSED COMMAND_LINE_PRE_FILTER_FN_W preFilter,
396
                               WINPR_ATTR_UNUSED COMMAND_LINE_POST_FILTER_FN_W postFilter)
397
0
{
398
0
  WLog_ERR("TODO", "TODO: implement");
399
0
  return 0;
400
0
}
401
402
int CommandLineClearArgumentsA(COMMAND_LINE_ARGUMENT_A* options)
403
0
{
404
0
  for (size_t i = 0; options[i].Name != NULL; i++)
405
0
  {
406
0
    options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
407
0
    options[i].Value = NULL;
408
0
  }
409
410
0
  return 0;
411
0
}
412
413
int CommandLineClearArgumentsW(COMMAND_LINE_ARGUMENT_W* options)
414
0
{
415
0
  for (int i = 0; options[i].Name != NULL; i++)
416
0
  {
417
0
    options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
418
0
    options[i].Value = NULL;
419
0
  }
420
421
0
  return 0;
422
0
}
423
424
const COMMAND_LINE_ARGUMENT_A* CommandLineFindArgumentA(const COMMAND_LINE_ARGUMENT_A* options,
425
                                                        LPCSTR Name)
426
0
{
427
0
  WINPR_ASSERT(options);
428
0
  WINPR_ASSERT(Name);
429
430
0
  for (size_t i = 0; options[i].Name != NULL; i++)
431
0
  {
432
0
    if (strcmp(options[i].Name, Name) == 0)
433
0
      return &options[i];
434
435
0
    if (options[i].Alias != NULL)
436
0
    {
437
0
      if (strcmp(options[i].Alias, Name) == 0)
438
0
        return &options[i];
439
0
    }
440
0
  }
441
442
0
  return NULL;
443
0
}
444
445
const COMMAND_LINE_ARGUMENT_W* CommandLineFindArgumentW(const COMMAND_LINE_ARGUMENT_W* options,
446
                                                        LPCWSTR Name)
447
0
{
448
0
  WINPR_ASSERT(options);
449
0
  WINPR_ASSERT(Name);
450
451
0
  for (size_t i = 0; options[i].Name != NULL; i++)
452
0
  {
453
0
    if (_wcscmp(options[i].Name, Name) == 0)
454
0
      return &options[i];
455
456
0
    if (options[i].Alias != NULL)
457
0
    {
458
0
      if (_wcscmp(options[i].Alias, Name) == 0)
459
0
        return &options[i];
460
0
    }
461
0
  }
462
463
0
  return NULL;
464
0
}
465
466
const COMMAND_LINE_ARGUMENT_A* CommandLineFindNextArgumentA(const COMMAND_LINE_ARGUMENT_A* argument)
467
0
{
468
0
  const COMMAND_LINE_ARGUMENT_A* nextArgument = NULL;
469
470
0
  if (!argument || !argument->Name)
471
0
    return NULL;
472
473
0
  nextArgument = &argument[1];
474
475
0
  if (nextArgument->Name == NULL)
476
0
    return NULL;
477
478
0
  return nextArgument;
479
0
}
480
481
static int is_quoted(char c)
482
0
{
483
0
  switch (c)
484
0
  {
485
0
    case '"':
486
0
      return 1;
487
0
    case '\'':
488
0
      return -1;
489
0
    default:
490
0
      return 0;
491
0
  }
492
0
}
493
494
static size_t get_element_count(const char* list, BOOL* failed, BOOL fullquoted)
495
0
{
496
0
  size_t count = 0;
497
0
  int quoted = 0;
498
0
  BOOL finished = FALSE;
499
0
  BOOL first = TRUE;
500
0
  const char* it = list;
501
502
0
  if (!list)
503
0
    return 0;
504
0
  if (strlen(list) == 0)
505
0
    return 0;
506
507
0
  while (!finished)
508
0
  {
509
0
    BOOL nextFirst = FALSE;
510
0
    switch (*it)
511
0
    {
512
0
      case '\0':
513
0
        if (quoted != 0)
514
0
        {
515
0
          WLog_ERR(TAG, "Invalid argument (missing closing quote) '%s'", list);
516
0
          *failed = TRUE;
517
0
          return 0;
518
0
        }
519
0
        finished = TRUE;
520
0
        break;
521
0
      case '\'':
522
0
      case '"':
523
0
        if (!fullquoted)
524
0
        {
525
0
          int now = is_quoted(*it);
526
0
          if (now == quoted)
527
0
            quoted = 0;
528
0
          else if (quoted == 0)
529
0
            quoted = now;
530
0
        }
531
0
        break;
532
0
      case ',':
533
0
        if (first)
534
0
        {
535
0
          WLog_ERR(TAG, "Invalid argument (empty list elements) '%s'", list);
536
0
          *failed = TRUE;
537
0
          return 0;
538
0
        }
539
0
        if (quoted == 0)
540
0
        {
541
0
          nextFirst = TRUE;
542
0
          count++;
543
0
        }
544
0
        break;
545
0
      default:
546
0
        break;
547
0
    }
548
549
0
    first = nextFirst;
550
0
    it++;
551
0
  }
552
0
  return count + 1;
553
0
}
554
555
static char* get_next_comma(char* string, BOOL fullquoted)
556
0
{
557
0
  const char* log = string;
558
0
  int quoted = 0;
559
0
  BOOL first = TRUE;
560
561
0
  WINPR_ASSERT(string);
562
563
0
  while (TRUE)
564
0
  {
565
0
    switch (*string)
566
0
    {
567
0
      case '\0':
568
0
        if (quoted != 0)
569
0
          WLog_ERR(TAG, "Invalid quoted argument '%s'", log);
570
0
        return NULL;
571
572
0
      case '\'':
573
0
      case '"':
574
0
        if (!fullquoted)
575
0
        {
576
0
          int now = is_quoted(*string);
577
0
          if ((quoted == 0) && !first)
578
0
          {
579
0
            WLog_ERR(TAG, "Invalid quoted argument '%s'", log);
580
0
            return NULL;
581
0
          }
582
0
          if (now == quoted)
583
0
            quoted = 0;
584
0
          else if (quoted == 0)
585
0
            quoted = now;
586
0
        }
587
0
        break;
588
589
0
      case ',':
590
0
        if (first)
591
0
        {
592
0
          WLog_ERR(TAG, "Invalid argument (empty list elements) '%s'", log);
593
0
          return NULL;
594
0
        }
595
0
        if (quoted == 0)
596
0
          return string;
597
0
        break;
598
599
0
      default:
600
0
        break;
601
0
    }
602
0
    first = FALSE;
603
0
    string++;
604
0
  }
605
606
0
  return NULL;
607
0
}
608
609
static BOOL is_valid_fullquoted(const char* string)
610
0
{
611
0
  char cur = '\0';
612
0
  char last = '\0';
613
0
  const char quote = *string++;
614
615
  /* We did not start with a quote. */
616
0
  if (is_quoted(quote) == 0)
617
0
    return FALSE;
618
619
0
  while ((cur = *string++) != '\0')
620
0
  {
621
    /* A quote is found. */
622
0
    if (cur == quote)
623
0
    {
624
      /* If the quote was escaped, it is valid. */
625
0
      if (last != '\\')
626
0
      {
627
        /* Only allow unescaped quote as last character in string. */
628
0
        if (*string != '\0')
629
0
          return FALSE;
630
0
      }
631
      /* If the last quote in the string is escaped, it is wrong. */
632
0
      else if (*string != '\0')
633
0
        return FALSE;
634
0
    }
635
0
    last = cur;
636
0
  }
637
638
  /* The string did not terminate with the same quote as it started. */
639
0
  if (last != quote)
640
0
    return FALSE;
641
0
  return TRUE;
642
0
}
643
644
char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list, size_t* count)
645
0
{
646
0
  char** p = NULL;
647
0
  char* str = NULL;
648
0
  size_t nArgs = 0;
649
0
  size_t prefix = 0;
650
0
  size_t len = 0;
651
0
  size_t namelen = 0;
652
0
  BOOL failed = FALSE;
653
0
  char* copy = NULL;
654
0
  char* unquoted = NULL;
655
0
  BOOL fullquoted = FALSE;
656
657
0
  BOOL success = FALSE;
658
0
  if (count == NULL)
659
0
    goto fail;
660
661
0
  *count = 0;
662
0
  if (list)
663
0
  {
664
0
    int start = 0;
665
0
    int end = 0;
666
0
    unquoted = copy = _strdup(list);
667
0
    if (!copy)
668
0
      goto fail;
669
670
0
    len = strlen(unquoted);
671
0
    if (len > 0)
672
0
    {
673
0
      start = is_quoted(unquoted[0]);
674
0
      end = is_quoted(unquoted[len - 1]);
675
676
0
      if ((start != 0) && (end != 0))
677
0
      {
678
0
        if (start != end)
679
0
        {
680
0
          WLog_ERR(TAG, "invalid argument (quote mismatch) '%s'", list);
681
0
          goto fail;
682
0
        }
683
0
        if (!is_valid_fullquoted(unquoted))
684
0
          goto fail;
685
0
        unquoted[len - 1] = '\0';
686
0
        unquoted++;
687
0
        len -= 2;
688
0
        fullquoted = TRUE;
689
0
      }
690
0
    }
691
0
  }
692
693
0
  *count = get_element_count(unquoted, &failed, fullquoted);
694
0
  if (failed)
695
0
    goto fail;
696
697
0
  if (*count == 0)
698
0
  {
699
0
    if (!name)
700
0
      goto fail;
701
0
    else
702
0
    {
703
0
      size_t clen = strlen(name);
704
0
      p = (char**)calloc(2UL + clen, sizeof(char*));
705
706
0
      if (p)
707
0
      {
708
0
        char* dst = (char*)&p[1];
709
0
        p[0] = dst;
710
0
        (void)sprintf_s(dst, clen + 1, "%s", name);
711
0
        *count = 1;
712
0
        success = TRUE;
713
0
        goto fail;
714
0
      }
715
0
    }
716
0
  }
717
718
0
  nArgs = *count;
719
720
0
  if (name)
721
0
    nArgs++;
722
723
0
  prefix = (nArgs + 1UL) * sizeof(char*);
724
0
  if (name)
725
0
    namelen = strlen(name);
726
0
  p = (char**)calloc(len + prefix + 1 + namelen + 1, sizeof(char*));
727
728
0
  if (!p)
729
0
    goto fail;
730
731
0
  str = &((char*)p)[prefix];
732
0
  memcpy(str, unquoted, len);
733
734
0
  if (name)
735
0
  {
736
0
    char* namestr = &((char*)p)[prefix + len + 1];
737
0
    memcpy(namestr, name, namelen);
738
739
0
    p[0] = namestr;
740
0
  }
741
742
0
  for (size_t index = name ? 1 : 0; index < nArgs; index++)
743
0
  {
744
0
    char* ptr = str;
745
0
    const int quote = is_quoted(*ptr);
746
0
    char* comma = get_next_comma(str, fullquoted);
747
748
0
    if ((quote != 0) && !fullquoted)
749
0
      ptr++;
750
751
0
    p[index] = ptr;
752
753
0
    if (comma)
754
0
    {
755
0
      char* last = comma - 1;
756
0
      const int lastQuote = is_quoted(*last);
757
758
0
      if (!fullquoted)
759
0
      {
760
0
        if (lastQuote != quote)
761
0
        {
762
0
          WLog_ERR(TAG, "invalid argument (quote mismatch) '%s'", list);
763
0
          goto fail;
764
0
        }
765
0
        else if (lastQuote != 0)
766
0
          *last = '\0';
767
0
      }
768
0
      *comma = '\0';
769
770
0
      str = comma + 1;
771
0
    }
772
0
    else if (quote)
773
0
    {
774
0
      char* end = strrchr(ptr, '"');
775
0
      if (!end)
776
0
        goto fail;
777
0
      *end = '\0';
778
0
    }
779
0
  }
780
781
0
  *count = nArgs;
782
0
  success = TRUE;
783
0
fail:
784
0
  free(copy);
785
0
  if (!success)
786
0
  {
787
0
    if (count)
788
0
      *count = 0;
789
0
    free((void*)p);
790
0
    return NULL;
791
0
  }
792
0
  return p;
793
0
}
794
795
char** CommandLineParseCommaSeparatedValues(const char* list, size_t* count)
796
0
{
797
0
  return CommandLineParseCommaSeparatedValuesEx(NULL, list, count);
798
0
}
799
800
char* CommandLineToCommaSeparatedValues(int argc, char* argv[])
801
0
{
802
0
  return CommandLineToCommaSeparatedValuesEx(argc, argv, NULL, 0);
803
0
}
804
805
static const char* filtered(const char* arg, const char* filters[], size_t number)
806
0
{
807
0
  if (number == 0)
808
0
    return arg;
809
0
  for (size_t x = 0; x < number; x++)
810
0
  {
811
0
    const char* filter = filters[x];
812
0
    size_t len = strlen(filter);
813
0
    if (_strnicmp(arg, filter, len) == 0)
814
0
      return &arg[len];
815
0
  }
816
0
  return NULL;
817
0
}
818
819
char* CommandLineToCommaSeparatedValuesEx(int argc, char* argv[], const char* filters[],
820
                                          size_t number)
821
0
{
822
0
  char* str = NULL;
823
0
  size_t offset = 0;
824
0
  size_t size = WINPR_ASSERTING_INT_CAST(size_t, argc) + 1;
825
0
  if ((argc <= 0) || !argv)
826
0
    return NULL;
827
828
0
  for (int x = 0; x < argc; x++)
829
0
    size += strlen(argv[x]);
830
831
0
  str = calloc(size, sizeof(char));
832
0
  if (!str)
833
0
    return NULL;
834
0
  for (int x = 0; x < argc; x++)
835
0
  {
836
0
    int rc = 0;
837
0
    const char* arg = filtered(argv[x], filters, number);
838
0
    if (!arg)
839
0
      continue;
840
0
    rc = _snprintf(&str[offset], size - offset, "%s,", arg);
841
0
    if (rc <= 0)
842
0
    {
843
0
      free(str);
844
0
      return NULL;
845
0
    }
846
0
    offset += (size_t)rc;
847
0
  }
848
0
  if (offset > 0)
849
0
    str[offset - 1] = '\0';
850
0
  return str;
851
0
}
852
853
void CommandLineParserFree(char** ptr)
854
0
{
855
0
  union
856
0
  {
857
0
    char* p;
858
0
    char** pp;
859
0
  } uptr;
860
0
  uptr.pp = ptr;
861
0
  free(uptr.p);
862
0
}