1// tCmdLine.cpp 
2// 
3// Parses a command line. A description of how to use the parser is in the header. Internally the first step is the 
4// expansion of combined single hyphen options. Next the parameters and options are parsed out. For each registered 
5// tOption and tParam object, its members are set to reflect the current command line when the tParse call is made. 
6// You may have more than one tOption that responds to the same option name. You may have more than one tParam that 
7// responds to the same parameter number. You may also collect all parameters in a single tParam by setting the 
8// parameter number to -1. 
9// 
10// Copyright (c) 2017, 2020, 2023-2025 Tristan Grimmer. 
11// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
12// granted, provided that the above copyright notice and this permission notice appear in all copies. 
13// 
14// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
15// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
16// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
17// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
18// PERFORMANCE OF THIS SOFTWARE. 
19 
20#include <Foundation/tFundamentals.h> 
21#include "System/tCmdLine.h" 
22#include "System/tFile.h" 
23 
24 
25namespace tCmdLine 
26
27 // Any single-hyphen combined option arguments are expanded here. Ex. -abc becomes -a -b -c. 
28 void ExpandArgs(tList<tStringItem>& args); 
29 int IndentSpaces(tString* dest, int numSpaces); 
30 
31 void SyntaxInternal(tString* dest, int width); 
32 void VersionAuthorInternal(tString& verAuthDest, const char8_t* author, int major, int minor = -1, int revision = -1); 
33 void UsageInternal(tString* dest, bool indent, const char8_t* verAuth, const char8_t* desc); 
34 
35 // I'm relying on zero initialization here. It's all zeroes before any items are constructed. 
36 tList<tParam> Params(tListMode::StaticZero); 
37 tList<tOption> Options(tListMode::StaticZero); 
38 tString Program
39 tString Empty
40
41 
42 
43tString tCmdLine::tGetProgram() 
44
45 return Program
46
47 
48 
49tCmdLine::tParam::tParam(int paramNumber, const char* name, const char* description, bool exclude) : 
50 ParamNumber(paramNumber), 
51 Values(tListMode::ListOwns), 
52 Name(), 
53 Description(), 
54 ExcludeFromUsage(exclude
55
56 tAssert(ParamNumber >= 0); 
57 if (name
58 Name.Set(name); 
59 else 
60 tsPrintf(dest&: Name, format: "Param%d", paramNumber); 
61 
62 if (description
63 Description = tString(description); 
64 
65 Params.Append(item: this); 
66
67 
68 
69tCmdLine::tOption::tOption(const char* description, char shortName, const char* longName, int numArgs, bool exclude) : 
70 ShortName(shortName), 
71 LongName(longName), 
72 Description(description), 
73 NumArgsPerOption(numArgs), 
74 Args(tListMode::ListOwns), 
75 Present(false), 
76 ExcludeFromUsage(exclude
77
78 Options.Append(item: this); 
79
80 
81 
82tCmdLine::tOption::tOption(const char* description, const char* longName, char shortName, int numArgs, bool exclude) : 
83 ShortName(shortName), 
84 LongName(longName), 
85 Description(description), 
86 NumArgsPerOption(numArgs), 
87 Args(tListMode::ListOwns), 
88 Present(false), 
89 ExcludeFromUsage(exclude
90
91 Options.Append(item: this); 
92
93 
94 
95tCmdLine::tOption::tOption(const char* description, char shortName, int numArgs, bool exclude) : 
96 ShortName(shortName), 
97 LongName(), 
98 Description(description), 
99 NumArgsPerOption(numArgs), 
100 Args(tListMode::ListOwns), 
101 Present(false), 
102 ExcludeFromUsage(exclude
103
104 Options.Append(item: this); 
105
106 
107 
108tCmdLine::tOption::tOption(const char* description, const char* longName, int numArgs, bool exclude) : 
109 ShortName(), 
110 LongName(longName), 
111 Description(description), 
112 NumArgsPerOption(numArgs), 
113 Args(tListMode::ListOwns), 
114 Present(false), 
115 ExcludeFromUsage(exclude
116
117 Options.Append(item: this); 
118
119 
120 
121int tCmdLine::IndentSpaces(tString* dest, int numSpaces
122
123 for (int s = 0; s < numSpaces; s++) 
124 tsaPrintf(dest, format: " "); 
125 
126 return numSpaces
127
128 
129 
130const tString& tCmdLine::tOption::ArgN(int n) const 
131
132 for (tStringItem* arg = Args.First(); arg; arg = arg->Next(), n--) 
133 if (n <= 1
134 return *arg
135 
136 return Empty
137
138 
139 
140void tCmdLine::tParse(int argc, char** argv, bool sortOptions
141
142 if (argc <= 0
143 return
144 
145 // Create a single line string of all the separate argv's. Arguments with quotes and spaces will come in as 
146 // distinct argv's, but they all get combined here. I don't believe two consecutive spaces will work. 
147 tString line
148 for (int a = 0; a < argc; a++) 
149
150 char* arg = argv[a]; 
151 if (!arg || (tStd::tStrlen(s: arg) == 0)) 
152 continue
153 
154 // Arg may have spaces within it. Such arguments need to be enclosed in quotes. 
155 tString argStr(arg); 
156 if (argStr.FindChar(c: ' ') != -1
157 argStr = tString("\"") + argStr + tString("\""); 
158 
159 line += argStr
160 if (a < (argc - 1)) 
161 line += " "
162
163 
164 tParse(commandLine: line, fullCommandLine: true, sortOptions); 
165
166 
167 
168void tCmdLine::tParse(int argc, char16_t** argv, bool sortOptions
169
170 if (argc <= 0
171 return
172 
173 // Create a single line string of all the separate argv's. Arguments with quotes and spaces will come in as 
174 // distinct argv's, but they all get combined here. I don't believe two consecutive spaces will work. 
175 tString line
176 for (int a = 0; a < argc; a++) 
177
178 char16_t* arg16 = argv[a]; 
179 if (!arg16
180 continue
181 
182 // Arg may have spaces within it. Such arguments need to be enclosed in quotes. 
183 // We can construct a UTF-8 tString from a UTF-16 char16_t pointer. 
184 tString argStr(arg16); 
185 if (argStr.IsEmpty()) 
186 continue
187 
188 if (argStr.FindChar(c: ' ') != -1
189 argStr = tString("\"") + argStr + tString("\""); 
190 
191 line += argStr
192 if (a < (argc - 1)) 
193 line += " "
194
195 
196 tParse(commandLine: line, fullCommandLine: true, sortOptions); 
197
198 
199 
200#ifdef PLATFORM_WINDOWS 
201void tCmdLine::tParse(int argc, wchar_t** argv, bool sortOptions) 
202
203 tParse(argc, (char16_t**)argv, sortOptions); 
204
205#endif 
206 
207 
208void tCmdLine::ExpandArgs(tList<tStringItem>& args
209
210 tList<tStringItem> expArgs(tListMode::ListOwns); 
211 while (tStringItem* arg = args.Remove()) 
212
213 if ((arg->Length() < 2) || ((*arg)[0] != '-') || (((*arg)[0] == '-') && ((*arg)[1] == '-'))) 
214
215 expArgs.Append(item: arg); 
216 continue
217
218 // It's now a single hyphen with something after it. 
219 
220 bool recognized = false
221 for (tOption* option = Options.First(); option; option = option->Next()) 
222
223 if ( option->ShortName == tString((*arg)[1]) ) 
224
225 recognized = true
226 break
227
228
229 
230 // Unrecognized options are left unmodified. Means you can put -10 and have it treated as a parameter. 
231 // as long as you don't have an option with shortname "1". 
232 if (!recognized
233
234 expArgs.Append(item: arg); 
235 continue
236
237 
238 // By now it's a single hyphen and is expandble. e.g. -abc -> -a -b -c 
239 for (int flag = 1; flag < arg->Length(); flag++) 
240
241 tString newArg = "-" + tString((*arg)[flag]); 
242 expArgs.Append(item: new tStringItem(newArg)); 
243
244 
245 delete arg
246
247 
248 // Repopulate args. 
249 while (tStringItem* arg = expArgs.Remove()) 
250 args.Append(item: arg); 
251
252 
253 
254static bool ParamSortFn(const tCmdLine::tParam& a, const tCmdLine::tParam& b
255
256 return (a.ParamNumber < b.ParamNumber); 
257
258 
259 
260static bool OptionSortFnShort(const tCmdLine::tOption& a, const tCmdLine::tOption& b
261
262 return tStd::tStrcmp(a: a.ShortName.Pod(), b: b.ShortName.Pod()) < 0
263
264 
265 
266static bool OptionSortFnLong(const tCmdLine::tOption& a, const tCmdLine::tOption& b
267
268 return tStd::tStrcmp(a: a.LongName.Pod(), b: b.LongName.Pod()) < 0
269
270 
271 
272void tCmdLine::tParse(const char8_t* commandLine, bool fullCommandLine, bool sortOptions
273
274 // At this point the constructors for all tOptions and tParams will have been called and both Params and Options 
275 // lists are populated. Options can be specified in any order. By default we are going to order them alphabetically 
276 // by short flag name so they get printed nicely by tPrintUsage. Params must be printed in order based on their param num 
277 // so we'll do that sort here too. Param sorting cannot be disabled. 
278 Params.Sort(compare: ParamSortFn); 
279 if (sortOptions
280
281 Options.Sort(compare: OptionSortFnShort); 
282 Options.Sort(compare: OptionSortFnLong); 
283
284 
285 tString line(commandLine); 
286 
287 // Mark both kinds of escaped quotes that may be present. These may be found when the caller 
288 // wants a quote inside a string on the command line. 
289 line.Replace(search: u8"^'", replace: tStd::u8SeparatorAStr); // Replaces ^' 
290 line.Replace(search: u8"^\"", replace: tStd::u8SeparatorBStr); // Replaces ^" 
291 line.Replace(search: u8"^^", replace: tStd::u8SeparatorCStr); // Replaces ^^ 
292 
293 // Mark the spaces and hyphens inside normal (non escaped) quotes. 
294 bool inside = false
295 for (char8_t* ch = line.Text(); *ch; ch++) 
296
297 if ((*ch == '\'') || (*ch == '\"')) 
298 inside = !inside
299 
300 if (!inside
301 continue
302 
303 if (*ch == ' '
304 *ch = tStd::SeparatorD
305 
306 if (*ch == '-'
307 *ch = tStd::SeparatorE
308
309 
310 line.Remove(rem: '\''); 
311 line.Remove(rem: '\"'); 
312 
313 tList<tStringItem> args(tListMode::ListOwns); 
314 tStd::tExplode(components&: args, src: line, divider: ' '); 
315 
316 // Now that the arguments are exploded into separate elements we replace the separators with the correct characters. 
317 for (tStringItem* arg = args.First(); arg; arg = arg->Next()) 
318
319 arg->Replace(search: tStd::SeparatorA, replace: '\''); 
320 arg->Replace(search: tStd::SeparatorB, replace: '\"'); 
321 arg->Replace(search: tStd::SeparatorC, replace: '^'); 
322 arg->Replace(search: tStd::SeparatorD, replace: ' '); 
323
324 
325 // Set the program name as typed in the command line. 
326 if (fullCommandLine
327
328 tStringItem* prog = args.Remove(); 
329 Program.Set(prog->Chars()); 
330 delete prog
331
332 else 
333
334 Program.Clear(); 
335
336 
337 ExpandArgs(args); 
338 
339 // Process all options. If there is more than one tOption that uses the same names, they all need to 
340 // be populated. That's why we need to loop through all arguments for each tOption. 
341 for (tOption* option = Options.First(); option; option = option->Next()) 
342
343 for (tStringItem* arg = args.First(); arg; arg = arg->Next()) 
344
345 if ( (*arg == tString("--") + option->LongName) || (*arg == tString("-") + option->ShortName) ) 
346
347 option->Present = true
348 for (int optArgNum = 0; optArgNum < option->NumArgsPerOption; optArgNum++) 
349
350 arg = arg->Next(); 
351 tStringItem* argItem = new tStringItem(*arg); 
352 argItem->Replace(search: tStd::SeparatorE, replace: '-'); 
353 option->Args.Append(item: argItem); 
354
355
356
357
358 
359 // Now we're going to create a list of just the parameters by skipping any options as we encounter them. 
360 // For any option that we know about we'll also have to skip its option arguments. 
361 tList<tStringItem> commandLineParams(tListMode::ListOwns); 
362 for (tStringItem* arg = args.First(); arg; arg = arg->Next()) 
363
364 tStringItem* candidate = arg
365 
366 // This loop skips any options for the current arg. 
367 for (tOption* option = Options.First(); option; option = option->Next()) 
368
369 if (*(arg->Text()) == '-'
370
371 tString optArg = *arg
372 
373 // We only skip options we recognize. 
374 if ( (optArg == tString("--") + option->LongName) || (optArg == tString("-") + option->ShortName) ) 
375
376 candidate = nullptr
377 for (int optArgNum = 0; optArgNum < option->NumArgsPerOption; optArgNum++) 
378 arg = arg->Next(); 
379
380
381
382 
383 if (candidate
384
385 tStringItem* cmdArg = new tStringItem(*candidate); 
386 cmdArg->Replace(search: tStd::SeparatorE, replace: '-'); 
387 commandLineParams.Append(item: cmdArg); 
388
389
390 
391 // Process all parameters. Note again that similarly to tOptions, we need to loop through all commandLineParams 
392 // arguments for every tParam. This is because more than one tParam may need to collect the same arg. In fact some 
393 // tParams may have their param number set to 0, in which case they (all) need to collect all parameter arguments. 
394 for (tParam* param = Params.First(); param; param = param->Next()) 
395
396 int paramNumber = 1
397 for (tStringItem* arg = commandLineParams.First(); arg; arg = arg->Next(), paramNumber++) 
398
399 if ((param->ParamNumber == paramNumber) || (param->ParamNumber == 0)) 
400
401 param->Values.Append(item: new tStringItem(*arg)); 
402
403
404
405
406 
407 
408void tCmdLine::SyntaxInternal(tString* dest, int width
409
410 tString syntaxRaw
411R"SYNTAX(Command Line Syntax Guide 
412program [arg1 arg2 arg3 ...] 
413 
414ARGUMENTS: 
415Arguments are separated by spaces. An argument must be enclosed in quotes (single or double) if it has spaces in it or you want the argument to start with a hyphen literal. Hat (^) escape sequences can be used to put either type of quote inside. If you need to specify file paths you may use forward or back slashes. An ARGUMENT is either an OPTION or PARAMETER. 
416 
417OPTIONS: 
418An option is simply an argument that starts with a hyphen (-). An option has a short syntax and a long syntax. Short syntax is a - followed by a single non-hyphen character. The long form is -- followed by a word. All options support either long, short, or both forms. Options may have 0 or more arguments separated by spaces. Options can be specified in any order. Short form options may be combined: e.g. -al expands to -a -l. 
419 
420FLAGS: 
421If an option takes zero arguments it is called a flag. You can only test for a FLAG's presence or lack thereof. 
422 
423PARAMETERS: 
424A parameter is simply an argument that is not one of the available options. It can be read as a string and parsed however is needed (converted to an integer, float etc.) Order is important when specifying parameters. If you need a hyphen in a parameter at the start you will need put the parameter in quotes. For example, a filename _can_ start with -. Note that arguments that start with a hyphen but are not recognized as a valid option just get turned into parameters. This means interpreting a hyphen directly instead of as an option specifier will happen automatically if there are no options matching what comes after the hyphen. e.g. 'tool -.85 --add 33 -87.98 --notpresent' work just fine as long as there are no options that have a short form with digits or a decimal. In this example the -.85 will be the first parameter, --notpresent will be the second. The --add is assumed to take in two number arguments. 
425 
426ESCAPES: 
427Sometimes you need a particular character to appear inside an argument. For example you may need a single or double quote to apprear inside a parameter. The hat (^) followed by the character you need is used for this purpose. 
428e.g. ^^ yields ^ | ^' yields ' | ^" yields " 
429 
430VARIABLE ARGUMENTS: 
431A variable number of space-separated parameters may be specified if the tool supports them. The parsing system will collect them all up if the parameter number is unset (-1). 
432A variable number of option arguments is not directly supported due to the more complex parsing that would be needed. The same result is achieved by entering the same option more than once. Order is preserved. This can also be done with options that take more than one argument. 
433e.g. tool -I /patha/include/ -I /pathb/include 
434 
435EXAMPLE: 
436mycopy -R --overwrite fileA.txt -pat fileB.txt --log log.txt 
437 
438The fileA.txt and fileB.txt in the above example are parameters (assuming the overwrite option is a flag). fileA.txt is the first parameter and fileB.txt is the second. 
439 
440The '--log log.txt' is an option with a single argument, log.txt. Flags may be combined. The -pat in the example expands to -p -a -t. It is suggested only to combine flag (boolean) options as only the last option would get any arguments. 
441)SYNTAX"
442 
443 // Support column width by processing each line of syntaxRaw and adding to the final syntax string. 
444 tString syntax
445 while (syntaxRaw.IsValid()) 
446
447 tString line = syntaxRaw.ExtractLeft(divider: '\n'); 
448 if (line.IsEmpty()) 
449
450 syntax += "\n"
451 continue
452
453 
454 // The separator is so we can test if the word we're extracting is the last of the line. 
455 line += tStd::SeparatorAStr
456 line += " "
457 int col = 0
458 do 
459
460 tString word = line.ExtractLeft(divider: ' '); 
461 bool lastWord = false
462 if (word.FindChar(c: tStd::SeparatorA, reverse: true) != -1
463
464 word.RemoveLast(); 
465 lastWord = true
466
467 
468 int wlen = word.Length(); 
469 if (wlen > width
470
471 // If the word is super long we're stuck. Just add it and move on. We don't break single words. 
472 syntax += word
473 col = 0
474 continue
475
476 if (col+wlen <= width
477
478 syntax += word
479 col += wlen
480 
481 // We only add the space after the word if it's not the last one of the input line. 
482 if (!lastWord
483
484 syntax += " "
485 col++; 
486
487
488 else 
489
490 // No room on the current output line. PPlace the word on the next line and add a space. 
491 syntax.RemoveLast(); 
492 syntax += "\n"
493 syntax += word
494 syntax += " "
495 col = wlen+1
496
497
498 while (line.IsValid()); 
499 syntax += "\n"
500
501 
502 tsaPrintf(dest, format: "%s", syntax.Pod()); 
503
504 
505 
506void tCmdLine::tPrintSyntax(int width
507
508 SyntaxInternal(dest: nullptr, width); 
509
510 
511 
512void tCmdLine::tStringSyntax(tString& dest, int width
513
514 SyntaxInternal(dest: &dest, width); 
515
516 
517 
518void tCmdLine::VersionAuthorInternal(tString& verAuth, const char8_t* author, int major, int minor, int revision
519
520 tAssert(major >= 0); 
521 tAssert((minor >= 0) || (revision < 0)); // Not allowed a valid revision number if minor is not also valid. 
522 
523 verAuth.Clear(); 
524 tsaPrintf(dest&: verAuth, format: "Version %d", major); 
525 if (minor >= 0
526
527 tsaPrintf(dest&: verAuth, format: ".%d", minor); 
528 if (revision >= 0
529 tsaPrintf(dest&: verAuth, format: ".%d", revision); 
530
531 
532 if (author
533 tsaPrintf(dest&: verAuth, format: " by %s", author); 
534
535 
536 
537void tCmdLine::UsageInternal(tString* dest, bool doIndent, const char8_t* verAuth, const char8_t* desc
538
539 tString exeName = "program"
540 if (!tCmdLine::Program.IsEmpty()) 
541
542 exeName = tSystem::tGetFileName(file: tCmdLine::Program); 
543
544 else 
545
546 tString fullPath = tSystem::tGetProgramPath(); 
547 tString fileName = tSystem::tGetFileName(file: fullPath); 
548 if (fileName.IsValid()) 
549 exeName = fileName
550
551 
552 if (verAuth
553 tsaPrintf(dest, format: "%s %s\n\n", tPod(tSystem::tGetFileBaseName(exeName)), verAuth); 
554 
555 if (Options.IsEmpty()) 
556 tsaPrintf(dest, format: "USAGE: %s ", exeName.Pod()); 
557 else 
558 tsaPrintf(dest, format: "USAGE: %s [options] ", exeName.Pod()); 
559 
560 for (tParam* param = Params.First(); param; param = param->Next()) 
561
562 if (param->ExcludeFromUsage
563 continue
564 
565 if (!param->Name.IsEmpty() && (param->ParamNumber > 0)) 
566 tsaPrintf(dest, format: "%s ", param->Name.Pod()); 
567 else if (!param->Name.IsEmpty() && (param->ParamNumber == 0)) 
568 tsaPrintf(dest, format: "[%s] ", param->Name.Pod()); 
569 else if (param->ParamNumber > 0
570 tsaPrintf(dest, format: "param%d ", param->ParamNumber); 
571 else 
572 tsaPrintf(dest, format: "[params] "); 
573
574 
575 tsaPrintf(dest, format: "\n\n"); 
576 if (desc
577
578 tsaPrintf(dest, format: "%s", desc); 
579 tsaPrintf(dest, format: "\n\n"); 
580
581 
582 int indent = 0
583 int numUsageOptions = 0
584 int numUsageParams = 0
585 
586 // Loop through both options and parameters to figure out how far to indent. 
587 for (tOption* option = Options.First(); option; option = option->Next()) 
588
589 if (option->ExcludeFromUsage
590 continue
591 
592 int numPrint = 0
593 if (!option->LongName.IsEmpty()) 
594 numPrint += tcPrintf(format: "--%s ", option->LongName.Pod()); 
595 if (!option->ShortName.IsEmpty()) 
596 numPrint += tcPrintf(format: "-%s ", option->ShortName.Pod()); 
597 
598 if (option->NumArgsPerOption <= 2
599
600 for (int a = 0; a < option->NumArgsPerOption; a++) 
601 numPrint += tcPrintf(format: "arg%c ", '1'+a); 
602
603 else 
604
605 numPrint += tcPrintf(format: "[%d args] ", option->NumArgsPerOption); 
606
607 
608 if (doIndent
609 indent = tMath::tMax(a: indent, b: numPrint); 
610 numUsageOptions++; 
611
612 
613 for (tParam* param = Params.First(); param; param = param->Next()) 
614
615 if (param->ExcludeFromUsage
616 continue
617 
618 int numPrint = 0
619 if (!param->Name.IsEmpty() && (param->ParamNumber > 0)) 
620 numPrint = tcPrintf(format: "%s ", param->Name.Pod()); 
621 else if (!param->Name.IsEmpty() && (param->ParamNumber == 0)) 
622 numPrint = tcPrintf(format: "[%s] ", param->Name.Pod()); 
623 else if (param->ParamNumber > 0
624 numPrint = tcPrintf(format: "param%d ", param->ParamNumber); 
625 else 
626 numPrint = tcPrintf(format: "[params] "); 
627 
628 if (doIndent
629 indent = tMath::tMax(a: indent, b: numPrint); 
630 numUsageParams++; 
631
632 
633 if (numUsageOptions > 0
634
635 tsaPrintf(dest, format: "Options:\n"); 
636 for (tOption* option = Options.First(); option; option = option->Next()) 
637
638 if (option->ExcludeFromUsage
639 continue
640 
641 int numPrinted = 0
642 if (!option->LongName.IsEmpty()) 
643 numPrinted += tsaPrintf(dest, format: "--%s ", option->LongName.Pod()); 
644 if (!option->ShortName.IsEmpty()) 
645 numPrinted += tsaPrintf(dest, format: "-%s ", option->ShortName.Pod()); 
646 
647 if (option->NumArgsPerOption <= 2
648
649 for (int a = 0; a < option->NumArgsPerOption; a++) 
650 numPrinted += tsaPrintf(dest, format: "arg%c ", '1'+a); 
651
652 else 
653
654 numPrinted += tsaPrintf(dest, format: "[%d args] ", option->NumArgsPerOption); 
655
656 
657 if (doIndent
658 IndentSpaces(dest, numSpaces: indent-numPrinted); 
659 tsaPrintf(dest, format: ": %s\n", option->Description.Pod()); 
660
661 tsaPrintf(dest, format: "\n"); 
662
663 
664 if (numUsageParams > 0
665
666 tsaPrintf(dest, format: "Parameters:\n"); 
667 for (tParam* param = Params.First(); param; param = param->Next()) 
668
669 if (param->ExcludeFromUsage
670 continue
671 
672 int numPrinted = 0
673 if (!param->Name.IsEmpty() && (param->ParamNumber > 0)) 
674 numPrinted = tsaPrintf(dest, format: "%s ", param->Name.Pod()); 
675 else if (!param->Name.IsEmpty() && (param->ParamNumber == 0)) 
676 numPrinted = tsaPrintf(dest, format: "[%s] ", param->Name.Pod()); 
677 else if (param->ParamNumber > 0
678 numPrinted = tsaPrintf(dest, format: "param%d ", param->ParamNumber); 
679 else 
680 numPrinted = tsaPrintf(dest, format: "[params] "); 
681 
682 if (doIndent
683 IndentSpaces(dest, numSpaces: indent - numPrinted); 
684 
685 if (!param->Description.IsEmpty()) 
686 tsaPrintf(dest, format: ": %s", param->Description.Pod()); 
687 else 
688 tsaPrintf(dest, format: ": No description"); 
689 
690 tsaPrintf(dest, format: "\n"); 
691
692 tsaPrintf(dest, format: "\n"); 
693
694
695 
696 
697void tCmdLine::tPrintUsage(int major, int minor, int revision
698
699 tPrintUsage(author: nullptr, versionMajor: major, versionMinor: minor, versionRevision: revision); 
700
701 
702 
703void tCmdLine::tPrintUsage(const char8_t* author, int major, int minor, int revision
704
705 tPrintUsage(author, desc: nullptr, versionMajor: major, versionMinor: minor, versionRevision: revision); 
706
707 
708 
709void tCmdLine::tPrintUsage(const char8_t* author, const char8_t* desc, int major, int minor, int revision
710
711 tString verAuth
712 VersionAuthorInternal(verAuth, author, major, minor, revision); 
713 tPrintUsage(versionAuthor: verAuth.Pod(), desc); 
714
715 
716 
717void tCmdLine::tPrintUsage(const char8_t* verAuth, const char8_t* desc
718
719 UsageInternal(dest: nullptr, doIndent: true, verAuth, desc); 
720
721 
722 
723void tCmdLine::tStringUsage(tString& dest, int major, int minor, int revision
724
725 tStringUsage(dest, author: nullptr, versionMajor: major, versionMinor: minor, versionRevision: revision); 
726
727 
728 
729void tCmdLine::tStringUsage(tString& dest, const char8_t* author, int major, int minor, int revision
730
731 tStringUsage(dest, author, desc: nullptr, versionMajor: major, versionMinor: minor, versionRevision: revision); 
732
733 
734 
735void tCmdLine::tStringUsage(tString& dest, const char8_t* author, const char8_t* desc, int major, int minor, int revision
736
737 tString verAuth
738 VersionAuthorInternal(verAuth, author, major, minor, revision); 
739 tStringUsage(dest, versionAuthor: verAuth.Pod(), desc); 
740
741 
742 
743void tCmdLine::tStringUsage(tString& dest, const char8_t* verAuth, const char8_t* desc
744
745 UsageInternal(dest: &dest, doIndent: true, verAuth, desc); 
746
747 
748 
749void tCmdLine::tStringUsageNI(tString& dest, int major, int minor, int revision
750
751 tStringUsageNI(dest, author: nullptr, versionMajor: major, versionMinor: minor, versionRevision: revision); 
752
753 
754 
755void tCmdLine::tStringUsageNI(tString& dest, const char8_t* author, int major, int minor, int revision
756
757 tStringUsageNI(dest, author, desc: nullptr, versionMajor: major, versionMinor: minor, versionRevision: revision); 
758
759 
760 
761void tCmdLine::tStringUsageNI(tString& dest, const char8_t* author, const char8_t* desc, int major, int minor, int revision
762
763 tString verAuth
764 VersionAuthorInternal(verAuth, author, major, minor, revision); 
765 tStringUsageNI(dest, versionAuthor: verAuth.Pod(), desc); 
766
767 
768 
769void tCmdLine::tStringUsageNI(tString& dest, const char8_t* verAuth, const char8_t* desc
770
771 UsageInternal(dest: &dest, doIndent: false, verAuth, desc); 
772
773