/// <summary> /// Parses the command line arguments. /// </summary> public void Parse(int initPos, bool xlatStrings) { string MethodName = "CommandLine.Parse"; bool Done = false; char Ch; bool ParamIsOptional = false; int TemplPtr = 0; CmdLinePtr = initPos; LogWrite(MethodName + ": Parsing \"" + this.Text + "\" using template, \"" + Template + "\" starting at CharPos " + initPos.ToString() + "."); LogWrite(MethodName + ": IgnoringExtraneousText = " + IgnoringExtraneousText); LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); int State = 0; ToState(ref State, 1, MethodName); while (!Done) { switch (State) { case 1: Ch = GetNextChar(Template, ref TemplPtr); switch (Ch) { case 'n': ToState(ref State, 2, MethodName); break; case 's': ToState(ref State, 3, MethodName); break; case '[': ParamIsOptional = true; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); break; case ']': // do nothing break; case '.': ToState(ref State, 4, MethodName); break; case '/': // switch encountered ToState(ref State, 7, MethodName); break; case '\0': // end of template encountered ToState(ref State, 5, MethodName); break; case ' ': // Ignore and keep scanning... break; case '\t': // Ignore and keep scanning... break; default: // Invalid template character. throw new PipeWrenchEngineException("Template is invalid."); } break; case 2: // An integer is expected according to the template. Ch = GetNextChar(this.Text, ref iCmdLinePtr); if ("0123456789-+".IndexOf(Ch) != -1) { // Found an integer. Back up a character and parse it: CmdLinePtr--; string Token = ""; while ((CmdLinePtr < this.Text.Length) && (this.Text[CmdLinePtr] != ' ') && (this.Text[CmdLinePtr] != '\t')) { Token += this.Text[CmdLinePtr]; CmdLinePtr++; } try { // Convert the token to an integer value: int Value = Convert.ToInt32(Token); LogWrite(MethodName + ": IntArg value = " + Value.ToString()); Argument arg = new Argument(Value, CmdLinePtr); Args.Add(arg); } catch (Exception) { PipeWrenchCompileException ex = new PipeWrenchCompileException("Integer value is invalid."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } if (ParamIsOptional) { // Found first parameter in optional // sequence. Now all other parameters // to end of sequence are required. ParamIsOptional = false; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); } ToState(ref State, 1, MethodName); } else { if (Ch == '\'') { // String encountered instead of an integer. PipeWrenchCompileException ex = new PipeWrenchCompileException("Integer expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } else { if (Ch == '/') { // Switch encountered instead of an integer. if (ParamIsOptional) { ToState(ref State, 6, MethodName); } else { // Expected integer value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected an integer parameter but switch was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if (Ch == '\0') { // Reached the end of the command line. if (ParamIsOptional) { Done = true; } else { // Expected integer value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected an integer parameter but end-of-line was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if ((Ch == ' ') || (Ch == '\t')) { // It's a whitespace character. Just ignore it and keep scanning... } else { // Expected integer value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Integer expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } } } } break; case 3: // A string is expected according to the template. Ch = GetNextChar(this.Text, ref iCmdLinePtr); if (Ch == '\'') { // Found a string. Back up and parse it: CmdLinePtr--; string Token; if (xlatStrings) { string tempStr = GetString(this.Text, ref iCmdLinePtr, '\'', true); try { Token = XlatEscapes(tempStr); } catch (PipeWrenchCompileException ex) { ex.Data.Add("CmdLine", this.Text); ex.Data["CharPos"] = CmdLinePtr - tempStr.Length + (int) ex.Data["Offset"]; throw ex; } } else { Token = GetString(this.Text, ref iCmdLinePtr, '\'', false); } Argument arg = new Argument(Token, iCmdLinePtr); Args.Add(arg); if (ParamIsOptional) { // Found first parameter in optional sequence. Now all other // parameters to end of sequence are required. ParamIsOptional = false; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); } ToState(ref State, 1, MethodName); } else { if ("0123456789-+".IndexOf(Ch) != -1) { // An integer was encountered. PipeWrenchCompileException ex = new PipeWrenchCompileException("String expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } else { if (Ch == '/') { // A switch was encountered. if (ParamIsOptional) { ToState(ref State, 6, MethodName); } else { PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected a string parameter but switch was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if (Ch == '\0') { // Reached the end of the command line. if (ParamIsOptional) { Done = true; } else { // Expected string value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected a string parameter but end-of-line was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if ((Ch == ' ') || (Ch == '\t')) { // It's a whitespace character. Just ignore it and keep scanning... } else { // Expected string value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("String expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } } } } break; case 4: // Backtracking through optional sequence. int savedTemplPtr = TemplPtr; try { while (Template[TemplPtr] != '[') { TemplPtr--; } } catch (Exception) { PipeWrenchTemplateException ex = new PipeWrenchTemplateException( "Template error: Repeating group (...) is only allowed inside \"[]\"."); ex.Data.Add("CharPos", savedTemplPtr); ex.Data.Add("Template", Template); throw ex; } TemplPtr++; ParamIsOptional = true; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); ToState(ref State, 1, MethodName); break; case 5: // End of template encountered. if (!IgnoringExtraneousText) { // Be sure that remainder of command line is clear // as additional parameters, (including switches) // are not expected: Ch = GetNextChar(this.Text, ref iCmdLinePtr); if (Ch == '\0') { Done = true; } else { if ((Ch == ' ') || (Ch == '\t')) { // It's a whitespace character. Just ignore it and keep scanning... } else { PipeWrenchCompileException ex = new PipeWrenchCompileException("Parameter or switch is extraneous."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } } else { Done = true; } break; case 6: // Switch encountered. if (CmdLinePtr < this.Text.Length) { // Not yet to the end of the command line. string SwitchID = "/" + Char.ToUpper(this.Text[CmdLinePtr]); CmdLinePtr++; ParseSwitch(SwitchID, ref iCmdLinePtr, ref TemplPtr, xlatStrings); ToState(ref State, 7, MethodName); } else { // End of command line was encountered. Switch // character, (/) was found without a succeeding // ID character. PipeWrenchCompileException ex = new PipeWrenchCompileException("Switch is incomplete."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } break; case 7: // Locate the next switch on the command line: Ch = GetNextChar(this.Text, ref iCmdLinePtr); switch (Ch) { case '/': // Found it. ToState(ref State, 6, MethodName); break; case '\0': Done = true; break; case ' ': // Whitespace character. Just ignore it and keep scanning... break; case '\t': // Whitespace character. Just ignore it and keep scanning... break; default: PipeWrenchCompileException ex = new PipeWrenchCompileException("A switch was expected."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } break; default: throw new PipeWrenchEngineException(MethodName + ": Invalid state (" + State.ToString() + ")."); } } }
/// <summary> /// Parses the command line arguments. /// </summary> public void Parse(int initPos, bool xlatStrings) { string MethodName = "CommandLine.Parse"; bool Done = false; char Ch; bool ParamIsOptional = false; int TemplPtr = 0; CmdLinePtr = initPos; LogWrite(MethodName + ": Parsing \"" + this.Text + "\" using template, \"" + Template + "\" starting at CharPos " + initPos.ToString() + "."); LogWrite(MethodName + ": IgnoringExtraneousText = " + IgnoringExtraneousText); LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); int State = 0; ToState(ref State, 1, MethodName); while (!Done) { switch (State) { case 1: Ch = GetNextChar(Template, ref TemplPtr); switch (Ch) { case 'n': ToState(ref State, 2, MethodName); break; case 's': ToState(ref State, 3, MethodName); break; case '[': ParamIsOptional = true; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); break; case ']': // do nothing break; case '.': ToState(ref State, 4, MethodName); break; case '/': // switch encountered ToState(ref State, 7, MethodName); break; case '\0': // end of template encountered ToState(ref State, 5, MethodName); break; case ' ': // Ignore and keep scanning... break; case '\t': // Ignore and keep scanning... break; default: // Invalid template character. throw new PipeWrenchEngineException("Template is invalid."); } break; case 2: // An integer is expected according to the template. Ch = GetNextChar(this.Text, ref iCmdLinePtr); if ("0123456789-+".IndexOf(Ch) != -1) { // Found an integer. Back up a character and parse it: CmdLinePtr--; string Token = ""; while ((CmdLinePtr < this.Text.Length) && (this.Text[CmdLinePtr] != ' ') && (this.Text[CmdLinePtr] != '\t')) { Token += this.Text[CmdLinePtr]; CmdLinePtr++; } try { // Convert the token to an integer value: int Value = Convert.ToInt32(Token); LogWrite(MethodName + ": IntArg value = " + Value.ToString()); Argument arg = new Argument(Value, CmdLinePtr); Args.Add(arg); } catch (Exception) { PipeWrenchCompileException ex = new PipeWrenchCompileException("Integer value is invalid."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } if (ParamIsOptional) { // Found first parameter in optional // sequence. Now all other parameters // to end of sequence are required. ParamIsOptional = false; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); } ToState(ref State, 1, MethodName); } else { if (Ch == '\'') { // String encountered instead of an integer. PipeWrenchCompileException ex = new PipeWrenchCompileException("Integer expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } else { if (Ch == '/') { // Switch encountered instead of an integer. if (ParamIsOptional) { ToState(ref State, 6, MethodName); } else { // Expected integer value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected an integer parameter but switch was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if (Ch == '\0') { // Reached the end of the command line. if (ParamIsOptional) { Done = true; } else { // Expected integer value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected an integer parameter but end-of-line was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if ((Ch == ' ') || (Ch == '\t')) { // It's a whitespace character. Just ignore it and keep scanning... } else { // Expected integer value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Integer expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } } } } break; case 3: // A string is expected according to the template. Ch = GetNextChar(this.Text, ref iCmdLinePtr); if (Ch == '\'') { // Found a string. Back up and parse it: CmdLinePtr--; string Token; if (xlatStrings) { string tempStr = GetString(this.Text, ref iCmdLinePtr, '\'', true); try { Token = XlatEscapes(tempStr); } catch (PipeWrenchCompileException ex) { ex.Data.Add("CmdLine", this.Text); ex.Data["CharPos"] = CmdLinePtr - tempStr.Length + (int)ex.Data["Offset"]; throw ex; } } else { Token = GetString(this.Text, ref iCmdLinePtr, '\'', false); } Argument arg = new Argument(Token, iCmdLinePtr); Args.Add(arg); if (ParamIsOptional) { // Found first parameter in optional sequence. Now all other // parameters to end of sequence are required. ParamIsOptional = false; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); } ToState(ref State, 1, MethodName); } else { if ("0123456789-+".IndexOf(Ch) != -1) { // An integer was encountered. PipeWrenchCompileException ex = new PipeWrenchCompileException("String expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } else { if (Ch == '/') { // A switch was encountered. if (ParamIsOptional) { ToState(ref State, 6, MethodName); } else { PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected a string parameter but switch was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if (Ch == '\0') { // Reached the end of the command line. if (ParamIsOptional) { Done = true; } else { // Expected string value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("Expected a string parameter but end-of-line was encountered."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } else { if ((Ch == ' ') || (Ch == '\t')) { // It's a whitespace character. Just ignore it and keep scanning... } else { // Expected string value was not found. PipeWrenchCompileException ex = new PipeWrenchCompileException("String expected for this parameter."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } } } } break; case 4: // Backtracking through optional sequence. int savedTemplPtr = TemplPtr; try { while (Template[TemplPtr] != '[') { TemplPtr--; } } catch (Exception) { PipeWrenchTemplateException ex = new PipeWrenchTemplateException( "Template error: Repeating group (...) is only allowed inside \"[]\"."); ex.Data.Add("CharPos", savedTemplPtr); ex.Data.Add("Template", Template); throw ex; } TemplPtr++; ParamIsOptional = true; LogWrite(MethodName + ": ParamIsOptional = " + ParamIsOptional); ToState(ref State, 1, MethodName); break; case 5: // End of template encountered. if (!IgnoringExtraneousText) { // Be sure that remainder of command line is clear // as additional parameters, (including switches) // are not expected: Ch = GetNextChar(this.Text, ref iCmdLinePtr); if (Ch == '\0') { Done = true; } else { if ((Ch == ' ') || (Ch == '\t')) { // It's a whitespace character. Just ignore it and keep scanning... } else { PipeWrenchCompileException ex = new PipeWrenchCompileException("Parameter or switch is extraneous."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } } } else { Done = true; } break; case 6: // Switch encountered. if (CmdLinePtr < this.Text.Length) { // Not yet to the end of the command line. string SwitchID = "/" + Char.ToUpper(this.Text[CmdLinePtr]); CmdLinePtr++; ParseSwitch(SwitchID, ref iCmdLinePtr, ref TemplPtr, xlatStrings); ToState(ref State, 7, MethodName); } else { // End of command line was encountered. Switch // character, (/) was found without a succeeding // ID character. PipeWrenchCompileException ex = new PipeWrenchCompileException("Switch is incomplete."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } break; case 7: // Locate the next switch on the command line: Ch = GetNextChar(this.Text, ref iCmdLinePtr); switch (Ch) { case '/': // Found it. ToState(ref State, 6, MethodName); break; case '\0': Done = true; break; case ' ': // Whitespace character. Just ignore it and keep scanning... break; case '\t': // Whitespace character. Just ignore it and keep scanning... break; default: PipeWrenchCompileException ex = new PipeWrenchCompileException("A switch was expected."); ex.Data.Add("CharPos", CmdLinePtr); ex.Data.Add("CmdLine", this.Text); throw ex; } break; default: throw new PipeWrenchEngineException(MethodName + ": Invalid state (" + State.ToString() + ")."); } } }