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