1 // CommandLineParser.cs
2 
3 using System;
4 using System.Collections;
5 
6 namespace SevenZip.CommandLineParser
7 {
8 	public enum SwitchType
9 	{
10 		Simple,
11 		PostMinus,
12 		LimitedPostString,
13 		UnLimitedPostString,
14 		PostChar
15 	}
16 
17 	public class SwitchForm
18 	{
19 		public string IDString;
20 		public SwitchType Type;
21 		public bool Multi;
22 		public int MinLen;
23 		public int MaxLen;
24 		public string PostCharSet;
25 
SwitchForm(string idString, SwitchType type, bool multi, int minLen, int maxLen, string postCharSet)26 		public SwitchForm(string idString, SwitchType type, bool multi,
27 			int minLen, int maxLen, string postCharSet)
28 		{
29 			IDString = idString;
30 			Type = type;
31 			Multi = multi;
32 			MinLen = minLen;
33 			MaxLen = maxLen;
34 			PostCharSet = postCharSet;
35 		}
SwitchForm(string idString, SwitchType type, bool multi, int minLen)36 		public SwitchForm(string idString, SwitchType type, bool multi, int minLen):
37 			this(idString, type, multi, minLen, 0, "")
38 		{
39 		}
SwitchForm(string idString, SwitchType type, bool multi)40 		public SwitchForm(string idString, SwitchType type, bool multi):
41 			this(idString, type, multi, 0)
42 		{
43 		}
44 	}
45 
46 	public class SwitchResult
47 	{
48 		public bool ThereIs;
49 		public bool WithMinus;
50 		public ArrayList PostStrings = new ArrayList();
51 		public int PostCharIndex;
SwitchResult()52 		public SwitchResult()
53 		{
54 			ThereIs = false;
55 		}
56 	}
57 
58 	public class Parser
59 	{
60 		public ArrayList NonSwitchStrings = new ArrayList();
61 		SwitchResult[] _switches;
62 
Parser(int numSwitches)63 		public Parser(int numSwitches)
64 		{
65 			_switches = new SwitchResult[numSwitches];
66 			for (int i = 0; i < numSwitches; i++)
67 				_switches[i] = new SwitchResult();
68 		}
69 
ParseString(string srcString, SwitchForm[] switchForms)70 		bool ParseString(string srcString, SwitchForm[] switchForms)
71 		{
72 			int len = srcString.Length;
73 			if (len == 0)
74 				return false;
75 			int pos = 0;
76 			if (!IsItSwitchChar(srcString[pos]))
77 				return false;
78 			while (pos < len)
79 			{
80 				if (IsItSwitchChar(srcString[pos]))
81 					pos++;
82 				const int kNoLen = -1;
83 				int matchedSwitchIndex = 0;
84 				int maxLen = kNoLen;
85 				for (int switchIndex = 0; switchIndex < _switches.Length; switchIndex++)
86 				{
87 					int switchLen = switchForms[switchIndex].IDString.Length;
88 					if (switchLen <= maxLen || pos + switchLen > len)
89 						continue;
90 					if (String.Compare(switchForms[switchIndex].IDString, 0,
91 							srcString, pos, switchLen, true) == 0)
92 					{
93 						matchedSwitchIndex = switchIndex;
94 						maxLen = switchLen;
95 					}
96 				}
97 				if (maxLen == kNoLen)
98 					throw new Exception("maxLen == kNoLen");
99 				SwitchResult matchedSwitch = _switches[matchedSwitchIndex];
100 				SwitchForm switchForm = switchForms[matchedSwitchIndex];
101 				if ((!switchForm.Multi) && matchedSwitch.ThereIs)
102 					throw new Exception("switch must be single");
103 				matchedSwitch.ThereIs = true;
104 				pos += maxLen;
105 				int tailSize = len - pos;
106 				SwitchType type = switchForm.Type;
107 				switch (type)
108 				{
109 					case SwitchType.PostMinus:
110 						{
111 							if (tailSize == 0)
112 								matchedSwitch.WithMinus = false;
113 							else
114 							{
115 								matchedSwitch.WithMinus = (srcString[pos] == kSwitchMinus);
116 								if (matchedSwitch.WithMinus)
117 									pos++;
118 							}
119 							break;
120 						}
121 					case SwitchType.PostChar:
122 						{
123 							if (tailSize < switchForm.MinLen)
124 								throw new Exception("switch is not full");
125 							string charSet = switchForm.PostCharSet;
126 							const int kEmptyCharValue = -1;
127 							if (tailSize == 0)
128 								matchedSwitch.PostCharIndex = kEmptyCharValue;
129 							else
130 							{
131 								int index = charSet.IndexOf(srcString[pos]);
132 								if (index < 0)
133 									matchedSwitch.PostCharIndex = kEmptyCharValue;
134 								else
135 								{
136 									matchedSwitch.PostCharIndex = index;
137 									pos++;
138 								}
139 							}
140 							break;
141 						}
142 					case SwitchType.LimitedPostString:
143 					case SwitchType.UnLimitedPostString:
144 						{
145 							int minLen = switchForm.MinLen;
146 							if (tailSize < minLen)
147 								throw new Exception("switch is not full");
148 							if (type == SwitchType.UnLimitedPostString)
149 							{
150 								matchedSwitch.PostStrings.Add(srcString.Substring(pos));
151 								return true;
152 							}
153 							String stringSwitch = srcString.Substring(pos, minLen);
154 							pos += minLen;
155 							for (int i = minLen; i < switchForm.MaxLen && pos < len; i++, pos++)
156 							{
157 								char c = srcString[pos];
158 								if (IsItSwitchChar(c))
159 									break;
160 								stringSwitch += c;
161 							}
162 							matchedSwitch.PostStrings.Add(stringSwitch);
163 							break;
164 						}
165 				}
166 			}
167 			return true;
168 
169 		}
170 
ParseStrings(SwitchForm[] switchForms, string[] commandStrings)171 		public void ParseStrings(SwitchForm[] switchForms, string[] commandStrings)
172 		{
173 			int numCommandStrings = commandStrings.Length;
174 			bool stopSwitch = false;
175 			for (int i = 0; i < numCommandStrings; i++)
176 			{
177 				string s = commandStrings[i];
178 				if (stopSwitch)
179 					NonSwitchStrings.Add(s);
180 				else
181 					if (s == kStopSwitchParsing)
182 					stopSwitch = true;
183 				else
184 					if (!ParseString(s, switchForms))
185 					NonSwitchStrings.Add(s);
186 			}
187 		}
188 
189 		public SwitchResult this[int index] { get { return _switches[index]; } }
190 
ParseCommand(CommandForm[] commandForms, string commandString, out string postString)191 		public static int ParseCommand(CommandForm[] commandForms, string commandString,
192 			out string postString)
193 		{
194 			for (int i = 0; i < commandForms.Length; i++)
195 			{
196 				string id = commandForms[i].IDString;
197 				if (commandForms[i].PostStringMode)
198 				{
199 					if (commandString.IndexOf(id) == 0)
200 					{
201 						postString = commandString.Substring(id.Length);
202 						return i;
203 					}
204 				}
205 				else
206 					if (commandString == id)
207 				{
208 					postString = "";
209 					return i;
210 				}
211 			}
212 			postString = "";
213 			return -1;
214 		}
215 
ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms, string commandString, ArrayList indices)216 		static bool ParseSubCharsCommand(int numForms, CommandSubCharsSet[] forms,
217 			string commandString, ArrayList indices)
218 		{
219 			indices.Clear();
220 			int numUsedChars = 0;
221 			for (int i = 0; i < numForms; i++)
222 			{
223 				CommandSubCharsSet charsSet = forms[i];
224 				int currentIndex = -1;
225 				int len = charsSet.Chars.Length;
226 				for (int j = 0; j < len; j++)
227 				{
228 					char c = charsSet.Chars[j];
229 					int newIndex = commandString.IndexOf(c);
230 					if (newIndex >= 0)
231 					{
232 						if (currentIndex >= 0)
233 							return false;
234 						if (commandString.IndexOf(c, newIndex + 1) >= 0)
235 							return false;
236 						currentIndex = j;
237 						numUsedChars++;
238 					}
239 				}
240 				if (currentIndex == -1 && !charsSet.EmptyAllowed)
241 					return false;
242 				indices.Add(currentIndex);
243 			}
244 			return (numUsedChars == commandString.Length);
245 		}
246 		const char kSwitchID1 = '-';
247 		const char kSwitchID2 = '/';
248 
249 		const char kSwitchMinus = '-';
250 		const string kStopSwitchParsing = "--";
251 
IsItSwitchChar(char c)252 		static bool IsItSwitchChar(char c)
253 		{
254 			return (c == kSwitchID1 || c == kSwitchID2);
255 		}
256 	}
257 
258 	public class CommandForm
259 	{
260 		public string IDString = "";
261 		public bool PostStringMode = false;
CommandForm(string idString, bool postStringMode)262 		public CommandForm(string idString, bool postStringMode)
263 		{
264 			IDString = idString;
265 			PostStringMode = postStringMode;
266 		}
267 	}
268 
269 	class CommandSubCharsSet
270 	{
271 		public string Chars = "";
272 		public bool EmptyAllowed = false;
273 	}
274 }
275