1// tCommand.h 
2// 
3// Parses a command line. A command line takes the form: 
4// program.exe [arg1 arg2 arg3 ...] 
5// 
6// Arguments are separated by spaces. An argument must be enclosed in quotes 
7// (single or double) if it has a space or hyphen in it. Use escape sequences to 
8// put either type of quote inside. If you need to specify paths, it is suggested 
9// to use forward slashes, although backslashes will work so long as the filename 
10// does not have a single or double quote next. 
11// 
12// An argument may be an 'option' or a 'parameter'. 
13// 
14// Options: 
15// An option has a short syntax and a long syntax. Short syntax is a - followed by 
16// a single non-hyphen character. The long form is -- followed by a word. All 
17// options support either long, short, or both forms. Options may have 0 or more 
18// arguments. If an option takes zero arguments it is called a flag and you can 
19// only test for its presence or lack of. Options can be specified in any order. 
20// Short form options may be combined: Eg. -al expands to -a -l 
21// 
22// Parameters: 
23// A parameter is simply an argument that does not start with a - or --. It can be 
24// read as a string and parsed arbitrarily (converted to an integer or float etc.) 
25// Order is important when specifying parameters. 
26// 
27// Example: 
28// mycopy.exe -R --overwrite fileA.txt -pat fileB.txt --log log.txt 
29// 
30// The fileA.txt and fileB.txt in the above example are parameters (assuming 
31// the overwrite option is a flag). fileA.txt is the first parameter and 
32// fileB.txt is the second. 
33// 
34// The '--log log.txt' is an option with a single argument, log.txt. Flags may be 
35// combined. The -pat in the example expands to -p -a -t. It is suggested only to 
36// combine flag options as only the last option would get any arguments. 
37// 
38// If you wish to interpret a hyphen directly instead of as an option specifier 
39// this will happen automatically if there are no options matching what comes 
40// after the hyphen. Eg. 'tool.exe -.85 --add 33 -87.98 -notpresent' works just 
41// fine as long as there are no options that have a short form with digits or a 
42// decimal. In this example the -.85 will be the first parameter, --notpresent 
43// will be the second, and the --add takes in two number arguments. 
44// 
45// Variable argument options are not supported due to the extra syntax that would 
46// be needed. The same result is achieved by entering the same option more than 
47// once. Eg. tool.exe -I /patha/include/ -I /pathb/include 
48// 
49// A powerful feature of the design of this parsing system is separation of concerns. In a typical system the knowledge 
50// of all the different command line parameters and options is needed in a single place, often in main() where argc and 
51// argv are passed in. These values need to somehow be passed all over the place in a large system. With tCommand you 
52// specify which options and parameters you care about only in the cpp file you are working in. 
53// 
54// To use the command line class, you start by registering your options and parameters. This is done using the tOption 
55// and tParam types to create static objects. After main calls the parse function, your objects get populated 
56// appropriately. For example, 
57// 
58// FileA.cpp: 
59// tParam FromFile(1, "FromFile"); // The 1 means this is the first parameter. The description is optional. 
60// tParam ToFile(2, "ToFile"); // The 2 means this is the second parameter. The description is optional. 
61// tOption("log", 'l', 1, "Specify log file"); // The 1 means there is one option argument to --log or -l. 
62// 
63// FileB.cpp: 
64// tOption ProgramOption('p', 0, "Program mode."); 
65// tOption AllOption('a', "ALL", 0, "Include all widgets."); 
66// tOption TimeOption("time", 't', 0, "Print timestamp."); 
67// 
68// Main.cpp: 
69// tParse(argc, argv); 
70// 
71// Internal Processing. The first step is the expansion of combined single hyphen options. Next the parameters and 
72// options are parsed out. For each registered tOption and tParam object, its members are set to reflect the current 
73// command line when the tParse call is made. You may have more than one tOption that responds to the same option name. 
74// You may have more than one tParam that responds to the same parameter number. 
75// 
76// Copyright (c) 2017, 2020 Tristan Grimmer. 
77// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
78// granted, provided that the above copyright notice and this permission notice appear in all copies. 
79// 
80// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
81// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
82// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
83// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
84// PERFORMANCE OF THIS SOFTWARE. 
85 
86#pragma once 
87#include <Foundation/tList.h> 
88#include <Foundation/tString.h> 
89 
90 
91namespace tCommand 
92
93 struct tParam : public tLink<tParam
94
95 tParam(int paramNumber, const char* name = nullptr, const char* description = nullptr); 
96 tParam(const char* description, const char* paramName, int paramNumber); 
97 tString Get() const { return Param; } 
98 bool IsPresent() const { return !Param.IsEmpty(); } 
99 operator bool() const { return IsPresent(); } 
100 
101 int ParamNumber; // 1 based. 
102 tString Param
103 tString Name
104 tString Description
105 }; 
106 
107 struct tOption : public tLink<tOption
108
109 tOption(const char* description, char shortName, const char* longName, int numArgs = 0); 
110 tOption(const char* description, const char* longName, char shortName, int numArgs = 0); 
111 tOption(const char* description, char shortName, int numArgs = 0); 
112 tOption(const char* description, const char* longName, int numArgs = 0); 
113 
114 // These validity checking functions only return true if the option was found in the command line and all 
115 // arguments were successfully parsed. 
116 bool IsPresent() const { return Present; } 
117 operator bool() const { return IsPresent(); } 
118 
119 // These argument accessors all return a reference to a static empty string if 'n' is out of range or the 
120 // option is invalid. GetArgs will return false on invalid. 
121 const tString& Arg1() const { return ArgN(1); } 
122 const tString& Arg2() const { return ArgN(2); } 
123 const tString& Arg3() const { return ArgN(3); } 
124 const tString& Arg4() const { return ArgN(4); } 
125 const tString& ArgN(int n) const; // n must be >= 1. 
126 bool GetArgs(tList<tStringItem>& args) const
127 int GetNumArgs() const { return Args.Count(); } 
128 int GetNumFlagArgs() const { return NumFlagArgs; } 
129 
130 tString ShortName
131 tString LongName
132 tString Description
133 
134 // This is _not_ the number of args that necessarily gets collected in the Args list. It is the number of args 
135 // for each instance of the flag in the command line. 
136 int NumFlagArgs
137 
138 // Important note here. If you have an option that takes 1 argument and it is listed in the command line 
139 // multiple times like "-i fileA -i fileB", then they will collect in the Args list in multiples 
140 // of 1. In general the arguments collect in multiples of NumFlagArgs. 
141 tList<tStringItem> Args
142 bool Present
143 }; 
144 
145 void tParse(int argc, char** argv); 
146 void tParse(const char* commandLine, bool fullCommandLine = false); 
147 void tPrintUsage(int versionMajor, int versionMinor = -1, int revision = -1); 
148 void tPrintUsage(const char* author, int versionMajor, int versionMinor = -1, int revision = -1); 
149 void tPrintUsage(const char* author, const char* desc, int versionMajor, int versionMinor = -1, int revision = -1); 
150 void tPrintUsage(const char* versionAuthor = nullptr, const char* desc = nullptr); 
151 void tPrintSyntax(); 
152 
153 // Returns the program name assuming you have already called tParse. 
154 tString tGetProgram(); 
155
156 
157 
158// Implementation below this line. 
159 
160 
161inline tCommand::tParam::tParam(const char* description, const char* paramName, int paramNumber) : 
162 tParam(paramNumber, paramName, description
163
164
165 
166 
167inline bool tCommand::tOption::GetArgs(tList<tStringItem>& args) const 
168
169 if (!IsPresent()) 
170 return false
171 
172 for (tStringItem* srcArg = Args.First(); srcArg; srcArg = srcArg->Next()) 
173 args.Append(new tStringItem(*srcArg)); 
174 
175 return true
176
177