927 lines
25 KiB
C++
927 lines
25 KiB
C++
/*
|
||
* Copyright (C) 1999, 2000, 2001, 2002
|
||
* Heikki Tauriainen <Heikki.Tauriainen@hut.fi>
|
||
*
|
||
* This program is free software; you can redistribute it and/or
|
||
* modify it under the terms of the GNU General Public License
|
||
* as published by the Free Software Foundation; either version 2
|
||
* of the License, or (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program; if not, write to the Free Software
|
||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
*/
|
||
|
||
#ifdef __GNUC__
|
||
#pragma implementation
|
||
#endif /* __GNUC__ */
|
||
|
||
#include <config.h>
|
||
#include <csignal>
|
||
#include <cstdio>
|
||
#include <iostream>
|
||
#include <fstream>
|
||
#ifdef HAVE_SSTREAM
|
||
#include <sstream>
|
||
#else
|
||
#include <strstream>
|
||
#endif /* HAVE_SSTREAM */
|
||
#include "DispUtil.h"
|
||
#include "ProductAutomaton.h"
|
||
#include "SharedTestData.h"
|
||
#include "StatDisplay.h"
|
||
#include "StringUtil.h"
|
||
#include "TestRoundInfo.h"
|
||
#include "TestStatistics.h"
|
||
#include "TestOperations.h"
|
||
#include "UserCommandReader.h"
|
||
#include "UserCommands.h"
|
||
|
||
#ifdef HAVE_READLINE
|
||
#include <cstdio>
|
||
#include <cstdlib>
|
||
#include <cstring>
|
||
#include <readline/readline.h>
|
||
#include <readline/history.h>
|
||
#endif /* HAVE_READLINE */
|
||
|
||
|
||
/******************************************************************************
|
||
*
|
||
* Functions for reading and parsing user commands.
|
||
*
|
||
*****************************************************************************/
|
||
|
||
namespace UserCommandInterface
|
||
{
|
||
|
||
using namespace ::SharedTestData;
|
||
using namespace ::StatDisplay;
|
||
using namespace ::StringUtil;
|
||
using namespace ::UserCommands;
|
||
|
||
/* ========================================================================= */
|
||
void executeUserCommands()
|
||
/* ----------------------------------------------------------------------------
|
||
*
|
||
* Description: Loop for reading user commands and executing them after a
|
||
* test round.
|
||
*
|
||
* Arguments: None.
|
||
*
|
||
* Returns: Nothing. However, changes `round_info.current_round',
|
||
* `round_info.next_round_to_stop',
|
||
* `round_info.next_round_to_run' and `round_info.abort' as a
|
||
* side effect, depending on the user's wish to abort testing,
|
||
* skip some number of test rounds or continue testing for a
|
||
* number of rounds.
|
||
*
|
||
* ------------------------------------------------------------------------ */
|
||
{
|
||
string input_line;
|
||
vector<string, ALLOC(string) > input_tokens;
|
||
TokenType token;
|
||
|
||
bool formula_type = true;
|
||
|
||
pair<string, bool> redirection_info;
|
||
string external_command;
|
||
ofstream* output_file = 0;
|
||
#ifdef HAVE_SSTREAM
|
||
ostringstream* output_string = 0;
|
||
#else
|
||
ostrstream* output_string = 0;
|
||
#endif /* HAVE_SSTREAM */
|
||
ostream* output_stream = 0;
|
||
|
||
const string prompt = " ** [Round " + toString(round_info.current_round)
|
||
+ " of " + toString(configuration.global_options.
|
||
number_of_rounds)
|
||
+ "] >> ";
|
||
int indent;
|
||
|
||
#ifdef HAVE_READLINE
|
||
char* prompt_c_str = new char[prompt.length() + 1];
|
||
strcpy(prompt_c_str, prompt.c_str());
|
||
|
||
char* line;
|
||
|
||
try
|
||
{
|
||
#endif /* HAVE_READLINE */
|
||
|
||
ProductAutomaton* product_automaton = 0;
|
||
pair<unsigned long int, bool> last_computed_product_automaton;
|
||
|
||
signal(SIGPIPE, SIG_IGN);
|
||
|
||
while (1)
|
||
{
|
||
try
|
||
{
|
||
input_line = "";
|
||
#ifdef HAVE_READLINE
|
||
line = readline(prompt_c_str);
|
||
if (line != static_cast<char*>(0))
|
||
input_line = line;
|
||
else
|
||
{
|
||
round_info.cout << '\n';
|
||
round_info.cout.flush();
|
||
}
|
||
#else
|
||
round_info.cout << prompt;
|
||
round_info.cout.flush();
|
||
getline(cin, input_line, '\n');
|
||
if (cin.eof())
|
||
{
|
||
round_info.cout << '\n';
|
||
round_info.cout.flush();
|
||
cin.clear();
|
||
}
|
||
#endif /* HAVE_READLINE */
|
||
|
||
external_command = "";
|
||
string::size_type pipe_pos = input_line.find_first_of('|');
|
||
if (pipe_pos != string::npos)
|
||
{
|
||
string::size_type nonspace_pos
|
||
= input_line.find_first_not_of(" \t", pipe_pos + 1);
|
||
if (nonspace_pos != string::npos)
|
||
{
|
||
external_command = input_line.substr(nonspace_pos);
|
||
input_line = input_line.substr(0, pipe_pos);
|
||
}
|
||
}
|
||
|
||
sliceString(input_line, " \t", input_tokens);
|
||
|
||
user_break = false;
|
||
|
||
if (!input_tokens.empty())
|
||
{
|
||
#ifdef HAVE_READLINE
|
||
add_history(line);
|
||
free(line);
|
||
#endif /* HAVE_READLINE */
|
||
round_info.cout << '\n';
|
||
round_info.cout.flush();
|
||
|
||
token = parseCommand(input_tokens[0]);
|
||
|
||
if (token == CONTINUE || token == SKIP)
|
||
{
|
||
verifyArgumentCount(input_tokens, 0, 1);
|
||
|
||
unsigned long int rounds_to_continue;
|
||
|
||
rounds_to_continue = (input_tokens.size() > 1
|
||
? parseNumber(input_tokens[1])
|
||
: 1);
|
||
|
||
if (rounds_to_continue == 0)
|
||
throw CommandErrorException("Argument of the command must be "
|
||
"positive.");
|
||
|
||
if (token == CONTINUE)
|
||
{
|
||
bool all_algorithms_disabled = true;
|
||
|
||
for (vector<Configuration::AlgorithmInformation,
|
||
ALLOC(Configuration::AlgorithmInformation) >
|
||
::const_iterator
|
||
algorithm = configuration.algorithms.begin();
|
||
algorithm != configuration.algorithms.end();
|
||
++algorithm)
|
||
{
|
||
if (algorithm->enabled)
|
||
{
|
||
all_algorithms_disabled = false;
|
||
break;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Show a warning if the `continue' command would result in no
|
||
* further tests in the case that none of the implementations is
|
||
* enabled for testing.
|
||
*/
|
||
|
||
if (configuration.global_options.interactive
|
||
!= Configuration::ALWAYS
|
||
&& all_algorithms_disabled)
|
||
{
|
||
round_info.cout << " Warning! All algorithms are currently "
|
||
"disabled.\n Are you sure you wish to "
|
||
"continue? [y/n] ";
|
||
round_info.cout.flush();
|
||
|
||
input_line = "";
|
||
getline(cin, input_line, '\n');
|
||
sliceString(input_line, " \t", input_tokens);
|
||
|
||
if (!input_tokens.empty()
|
||
&& (input_tokens[0][0] == 'y' || input_tokens[0][0] == 'Y'))
|
||
round_info.next_round_to_stop
|
||
= configuration.global_options.number_of_rounds + 1;
|
||
else
|
||
{
|
||
round_info.cout << '\n';
|
||
round_info.cout.flush();
|
||
continue;
|
||
}
|
||
}
|
||
|
||
if (round_info.next_round_to_run == round_info.current_round)
|
||
round_info.next_round_to_run++;
|
||
|
||
if (configuration.global_options.interactive
|
||
== Configuration::ALWAYS
|
||
|| input_tokens.size() > 1)
|
||
round_info.next_round_to_stop
|
||
= round_info.current_round + rounds_to_continue;
|
||
}
|
||
else /* token == SKIP */
|
||
{
|
||
round_info.next_round_to_stop
|
||
= round_info.current_round + rounds_to_continue;
|
||
|
||
round_info.next_round_to_run = round_info.next_round_to_stop + 1;
|
||
}
|
||
|
||
break;
|
||
}
|
||
else if (token == ENABLE || token == DISABLE)
|
||
{
|
||
verifyArgumentCount(input_tokens, 0, 1);
|
||
changeAlgorithmState(input_tokens, token == ENABLE);
|
||
continue;
|
||
}
|
||
else if (token == QUIT)
|
||
{
|
||
verifyArgumentCount(input_tokens, 0, 0);
|
||
round_info.abort = true;
|
||
break;
|
||
}
|
||
else if (token == VERBOSITY)
|
||
{
|
||
verifyArgumentCount(input_tokens, 0, 1);
|
||
changeVerbosity(input_tokens);
|
||
continue;
|
||
}
|
||
|
||
if (round_info.skip
|
||
&& (token == BUCHI || token == BUCHIANALYZE
|
||
|| token == CONSISTENCYANALYSIS || token == EVALUATE
|
||
|| token == FORMULA || token == INCONSISTENCIES
|
||
|| token == RESULTANALYZE || token == STATESPACE))
|
||
throw CommandErrorException("This command is not available because "
|
||
"the current test round was skipped.");
|
||
|
||
/*
|
||
* If the command expects a formula identifier as a parameter,
|
||
* determine the type of the formula to which the command refers.
|
||
*/
|
||
|
||
if (token == BUCHI || token == EVALUATE
|
||
|| token == FORMULA || token == RESULTANALYZE)
|
||
formula_type = parseFormulaType(input_tokens);
|
||
|
||
if (!external_command.empty())
|
||
{
|
||
/*
|
||
* If the command output should be piped to an external program,
|
||
* prepare to collect the output into a string. In this case no
|
||
* output redirection (> or >>) is allowed.
|
||
*/
|
||
|
||
#ifdef HAVE_SSTREAM
|
||
output_string = new ostringstream();
|
||
#else
|
||
output_string = new ostrstream();
|
||
#endif /* HAVE_SSTREAM */
|
||
output_stream = output_string;
|
||
indent = 0;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Determine whether the output of the command should be saved or
|
||
* appended to a file, instead of displaying it on the console. If
|
||
* output redirection is required, open a file for output.
|
||
*/
|
||
|
||
redirection_info = parseRedirection(input_tokens);
|
||
|
||
if (redirection_info.first.empty())
|
||
{
|
||
output_stream = &cout;
|
||
indent = 2;
|
||
}
|
||
else
|
||
{
|
||
output_file = new ofstream();
|
||
|
||
try
|
||
{
|
||
TestOperations::openFile(redirection_info.first.c_str(),
|
||
*output_file,
|
||
ios::out | (redirection_info.second
|
||
? ios::app
|
||
: ios::trunc),
|
||
2);
|
||
}
|
||
catch (const IOException& e)
|
||
{
|
||
delete output_file;
|
||
output_file = 0;
|
||
throw CommandErrorException(e.what());
|
||
}
|
||
|
||
output_stream = output_file;
|
||
indent = 0;
|
||
}
|
||
}
|
||
|
||
switch (token)
|
||
{
|
||
case ALGORITHMS :
|
||
verifyArgumentCount(input_tokens, 0, 0);
|
||
printAlgorithmList(*output_stream, indent);
|
||
if (output_file != 0)
|
||
round_info.cout << " List of algorithms";
|
||
break;
|
||
|
||
case BUCHI :
|
||
{
|
||
bool use_dot = (input_tokens.size() == 3
|
||
&& input_tokens[2] == "dot");
|
||
|
||
verifyArgumentCount(input_tokens, 1, 2);
|
||
printBuchiAutomaton(*output_stream, indent,
|
||
formula_type,
|
||
input_tokens,
|
||
(use_dot ? Graph::DOT : Graph::NORMAL));
|
||
if (output_file != 0)
|
||
{
|
||
round_info.cout << " B<>chi automaton information";
|
||
if (use_dot)
|
||
round_info.cout << " (in dot format)";
|
||
}
|
||
}
|
||
break;
|
||
|
||
case BUCHIANALYZE :
|
||
verifyArgumentCount(input_tokens, 2, 2);
|
||
printAutomatonAnalysisResults(*output_stream, indent,
|
||
parseNumber(input_tokens[1]),
|
||
parseNumber(input_tokens[2]));
|
||
if (output_file != 0)
|
||
round_info.cout << " B<>chi automaton intersection emptiness "
|
||
"check analysis";
|
||
break;
|
||
|
||
case CONSISTENCYANALYSIS :
|
||
verifyArgumentCount(input_tokens, 1, 2);
|
||
printConsistencyAnalysisResults(*output_stream, indent,
|
||
input_tokens);
|
||
if (output_file != 0)
|
||
round_info.cout << " Consistency check result analysis";
|
||
break;
|
||
|
||
case EVALUATE :
|
||
verifyArgumentCount(input_tokens, 0, 2);
|
||
evaluateFormula(*output_stream, indent, formula_type,
|
||
input_tokens);
|
||
if (output_file != 0)
|
||
round_info.cout << " Formula acceptance information";
|
||
break;
|
||
|
||
case FORMULA :
|
||
verifyArgumentCount(input_tokens, 0, 0);
|
||
printFormula(*output_stream, indent, formula_type);
|
||
if (output_file != 0)
|
||
round_info.cout << string(" ") + (formula_type
|
||
? "Formula"
|
||
: "Negated formula");
|
||
break;
|
||
|
||
case HELP :
|
||
verifyArgumentCount(input_tokens, 0, 1);
|
||
printCommandHelp(*output_stream, indent, input_tokens);
|
||
if (output_file != 0)
|
||
round_info.cout << " Command help";
|
||
break;
|
||
|
||
case INCONSISTENCIES :
|
||
verifyArgumentCount(input_tokens, 0, 1);
|
||
printInconsistencies(*output_stream, indent, input_tokens);
|
||
if (output_file != 0)
|
||
round_info.cout << " Model checking result consistency check "
|
||
"results for round "
|
||
+ toString(round_info.current_round)
|
||
+ "\n ";
|
||
break;
|
||
|
||
case RESULTANALYZE :
|
||
verifyArgumentCount(input_tokens, 2, 3);
|
||
printCrossComparisonAnalysisResults
|
||
(*output_stream, indent, formula_type, input_tokens,
|
||
product_automaton, last_computed_product_automaton);
|
||
if (output_file != 0)
|
||
round_info.cout << " Model checking result cross-comparison "
|
||
"analysis";
|
||
break;
|
||
|
||
case RESULTS :
|
||
verifyArgumentCount(input_tokens, 0, 1);
|
||
printTestResults(*output_stream, indent, input_tokens);
|
||
if (output_file != 0)
|
||
round_info.cout << " Test results for round "
|
||
+ toString(round_info.current_round);
|
||
break;
|
||
|
||
case STATESPACE :
|
||
{
|
||
bool use_dot = (input_tokens.size() == 2
|
||
&& input_tokens[1] == "dot");
|
||
|
||
verifyArgumentCount(input_tokens, 0, 1);
|
||
printStateSpace(*output_stream, indent, input_tokens,
|
||
(use_dot ? Graph::DOT : Graph::NORMAL));
|
||
|
||
if (output_file != 0)
|
||
{
|
||
round_info.cout << " State space information";
|
||
if (use_dot)
|
||
round_info.cout << " (in dot format)";
|
||
}
|
||
}
|
||
break;
|
||
|
||
case STATISTICS :
|
||
verifyArgumentCount(input_tokens, 0, 0);
|
||
printCollectiveStats(*output_stream, indent);
|
||
|
||
if (output_file != 0)
|
||
round_info.cout << " Test statistics after round "
|
||
+ toString(round_info.current_round);
|
||
break;
|
||
|
||
default :
|
||
throw CommandErrorException("Unknown command (`"
|
||
+ input_tokens[0] + "').");
|
||
}
|
||
|
||
if (output_string != 0)
|
||
{
|
||
*output_stream << ends;
|
||
string outstring(output_string->str());
|
||
|
||
FILE* output_pipe = popen(external_command.c_str(), "w");
|
||
if (output_pipe == NULL)
|
||
throw ExecFailedException(external_command);
|
||
int status = fputs(outstring.c_str(), output_pipe);
|
||
if (status != EOF)
|
||
fflush(output_pipe);
|
||
pclose(output_pipe);
|
||
round_info.cout << '\n';
|
||
round_info.cout.flush();
|
||
if (status == EOF)
|
||
throw IOException("Error writing to pipe.");
|
||
}
|
||
else if (output_file != 0)
|
||
{
|
||
round_info.cout << string(redirection_info.second
|
||
? " appended"
|
||
: " written")
|
||
+ " to `" + redirection_info.first + "'.\n\n";
|
||
round_info.cout.flush();
|
||
}
|
||
}
|
||
else if (!external_command.empty())
|
||
{
|
||
system(external_command.c_str());
|
||
round_info.cout << '\n';
|
||
round_info.cout.flush();
|
||
}
|
||
}
|
||
catch (const Exception& e)
|
||
{
|
||
::DispUtil::printTextBlock(cout, 2, string("Error: ") + e.what() + '\n',
|
||
78);
|
||
}
|
||
|
||
if (output_string != 0)
|
||
{
|
||
#ifndef HAVE_SSTREAM
|
||
output_string->freeze(0);
|
||
#endif /* HAVE_SSTREAM */
|
||
delete output_string;
|
||
output_string = 0;
|
||
}
|
||
else if (output_file != 0)
|
||
{
|
||
output_file->close();
|
||
delete output_file;
|
||
output_file = 0;
|
||
}
|
||
}
|
||
|
||
if (product_automaton != 0)
|
||
{
|
||
::DispUtil::printText
|
||
("<cleaning up memory allocated for product automaton>", 4, 2);
|
||
|
||
delete product_automaton;
|
||
|
||
::DispUtil::printText(" ok\n", 4);
|
||
}
|
||
|
||
#ifdef HAVE_READLINE
|
||
}
|
||
catch (...)
|
||
{
|
||
delete[] prompt_c_str;
|
||
throw;
|
||
}
|
||
|
||
delete[] prompt_c_str;
|
||
#endif /* HAVE_READLINE */
|
||
|
||
signal(SIGPIPE, SIG_DFL);
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
TokenType parseCommand(const string& token)
|
||
/* ----------------------------------------------------------------------------
|
||
*
|
||
* Description: Parses a user command by translating a command name into
|
||
* its corresponding TokenType identifier.
|
||
*
|
||
* Argument: token -- A reference to a string containing the command.
|
||
*
|
||
* Returns: A command identifier of the enumerated type TokenType.
|
||
*
|
||
* ------------------------------------------------------------------------- */
|
||
{
|
||
|
||
/*
|
||
* gcc versions prior to version 3 do not conform to the C++ standard in their
|
||
* support for the string::compare functions. Use a macro to fix this if
|
||
* necessary.
|
||
*/
|
||
|
||
#ifdef __GNUC__
|
||
#if __GNUC__ < 3
|
||
#define compare(start,end,str,dummy) compare(str,start,end)
|
||
#endif
|
||
#endif
|
||
|
||
TokenType token_type = UNKNOWN;
|
||
string::size_type len = token.length();
|
||
bool ambiguous = false;
|
||
|
||
if (token.empty())
|
||
return token_type;
|
||
|
||
switch (token[0])
|
||
{
|
||
case 'a' :
|
||
if (token.compare(1, len - 1, "lgorithms", len - 1) == 0)
|
||
token_type = ALGORITHMS;
|
||
break;
|
||
|
||
case 'b' :
|
||
if (len < 2)
|
||
ambiguous = true;
|
||
else if (token[1] == 'u')
|
||
{
|
||
if (len < 3)
|
||
ambiguous = true;
|
||
else if (token[2] == 'c')
|
||
{
|
||
if (len < 4)
|
||
ambiguous = true;
|
||
else if (token[3] == 'h')
|
||
{
|
||
if (len < 5)
|
||
ambiguous = true;
|
||
else if (token[4] == 'i')
|
||
{
|
||
if (len < 6)
|
||
token_type = BUCHI;
|
||
else if (token[5] == 'a'
|
||
&& token.compare(6, len - 6, "nalysis", len - 6) == 0)
|
||
token_type = BUCHIANALYZE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'c' :
|
||
if (len < 2)
|
||
ambiguous = true;
|
||
else if (token[1] == 'o')
|
||
{
|
||
if (len < 3)
|
||
ambiguous = true;
|
||
else if (token[2] == 'n')
|
||
{
|
||
if (len < 4)
|
||
ambiguous = true;
|
||
else if (token[3] == 's'
|
||
&& token.compare(4, len - 4, "istencyanalysis", len - 4)
|
||
== 0)
|
||
token_type = CONSISTENCYANALYSIS;
|
||
else if (token[3] == 't'
|
||
&& token.compare(4, len - 4, "inue", len - 4) == 0)
|
||
token_type = CONTINUE;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'd' :
|
||
if (token.compare(1, len - 1, "isable", len - 1) == 0)
|
||
token_type = DISABLE;
|
||
break;
|
||
|
||
case 'e' :
|
||
if (len < 2)
|
||
ambiguous = true;
|
||
else if (token[1] == 'n')
|
||
{
|
||
if (token.compare(2, len - 2, "able", len - 2) == 0)
|
||
token_type = ENABLE;
|
||
}
|
||
else if (token[1] == 'v')
|
||
{
|
||
if (token.compare(2, len - 2, "aluate", len - 2) == 0)
|
||
token_type = EVALUATE;
|
||
}
|
||
|
||
break;
|
||
|
||
case 'f' :
|
||
if (token.compare(1, len - 1, "ormula", len - 1) == 0)
|
||
token_type = FORMULA;
|
||
break;
|
||
|
||
case 'h' :
|
||
if (token.compare(1, len - 1, "elp", len - 1) == 0)
|
||
token_type = HELP;
|
||
break;
|
||
|
||
case 'i' :
|
||
if (token.compare(1, len - 1, "nconsistencies", len - 1) == 0)
|
||
token_type = INCONSISTENCIES;
|
||
break;
|
||
|
||
case 'q' :
|
||
if (token.compare(1, len - 1, "uit", len - 1) == 0)
|
||
token_type = QUIT;
|
||
break;
|
||
|
||
case 'r' :
|
||
if (len < 2)
|
||
ambiguous = true;
|
||
else if (token[1] == 'e')
|
||
{
|
||
if (len < 3)
|
||
ambiguous = true;
|
||
else if (token[2] == 's')
|
||
{
|
||
if (len < 4)
|
||
ambiguous = true;
|
||
else if (token[3] == 'u')
|
||
{
|
||
if (len < 5)
|
||
ambiguous = true;
|
||
else if (token[4] == 'l')
|
||
{
|
||
if (len < 6)
|
||
ambiguous = true;
|
||
else if (token[5] == 't')
|
||
{
|
||
if (len < 7)
|
||
ambiguous = true;
|
||
else if (token[6] == 's' && len == 7)
|
||
token_type = RESULTS;
|
||
else if (token[6] == 'a')
|
||
{
|
||
if (token.compare(7, len - 7, "nalysis", len - 7) == 0)
|
||
token_type = RESULTANALYZE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 's' :
|
||
if (len < 2)
|
||
ambiguous = true;
|
||
else if (token[1] == 'k')
|
||
{
|
||
if (token.compare(2, len - 2, "ip", len - 2) == 0)
|
||
token_type = SKIP;
|
||
}
|
||
else if (token[1] == 't')
|
||
{
|
||
if (len < 3)
|
||
ambiguous = true;
|
||
else if (token[2] == 'a')
|
||
{
|
||
if (len < 4)
|
||
ambiguous = true;
|
||
else if (token[3] == 't')
|
||
{
|
||
if (len < 5)
|
||
ambiguous = true;
|
||
else if (token[4] == 'e')
|
||
{
|
||
if (token.compare(5, len - 5, "space", len - 5) == 0)
|
||
token_type = STATESPACE;
|
||
}
|
||
else if (token[4] == 'i')
|
||
{
|
||
if (token.compare(5, len - 5, "stics", len - 5) == 0)
|
||
token_type = STATISTICS;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
|
||
case 'v' :
|
||
if (token.compare(1, len - 1, "erbosity", len - 1) == 0)
|
||
token_type = VERBOSITY;
|
||
break;
|
||
}
|
||
|
||
if (ambiguous)
|
||
throw CommandErrorException("Ambiguous command.");
|
||
|
||
return token_type;
|
||
|
||
#ifdef __GNUC__
|
||
#if __GNUC__ < 3
|
||
#undef compare
|
||
#endif
|
||
#endif
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
void verifyArgumentCount
|
||
(const vector<string, ALLOC(string) >& command,
|
||
vector<string, ALLOC(string) >::size_type min_arg_count,
|
||
vector<string, ALLOC(string) >::size_type max_arg_count)
|
||
/* ----------------------------------------------------------------------------
|
||
*
|
||
* Description: Verifies that the number of arguments given for a user
|
||
* command is between a given interval.
|
||
*
|
||
* Arguments: command -- A reference to a constant vector of
|
||
* strings (the user command and its
|
||
* arguments).
|
||
* min_arg_count -- Smallest allowed number of arguments.
|
||
* max_arg_count -- Largest allowed number of arguments.
|
||
*
|
||
* Returns: Nothing.
|
||
*
|
||
* ------------------------------------------------------------------------- */
|
||
{
|
||
if (command.size() < min_arg_count + 1)
|
||
throw CommandErrorException("Too few arguments for command.");
|
||
else if (command.size() > max_arg_count + 1)
|
||
throw CommandErrorException("Too many arguments for command.");
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
pair<string, bool> parseRedirection
|
||
(vector<string, ALLOC(string) >& input_tokens)
|
||
/* ----------------------------------------------------------------------------
|
||
*
|
||
* Description: Tests whether the last argument to a user command specifies
|
||
* output redirection. If redirection is requested, the
|
||
* "argument" specifying the redirection is removed from the
|
||
* vector of strings forming the command.
|
||
*
|
||
* Argument: input_tokens -- A reference to a vector of strings giving
|
||
* the user command and its arguments.
|
||
*
|
||
* Returns: A pair whose first component is the name of the output file
|
||
* (or the empty string if no redirection was specified) and
|
||
* whose second component determines whether the output should
|
||
* be appended to the file instead of creating a new file.
|
||
*
|
||
* ------------------------------------------------------------------------- */
|
||
{
|
||
string filename;
|
||
bool append = false;
|
||
|
||
if (!input_tokens.empty())
|
||
{
|
||
string& token = input_tokens.back();
|
||
|
||
if (token[0] == '>')
|
||
{
|
||
if (token.length() > 1)
|
||
{
|
||
if (token[1] == '>')
|
||
{
|
||
if (token.length() > 2)
|
||
{
|
||
append = true;
|
||
filename = token.substr(2);
|
||
input_tokens.pop_back();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
filename = token.substr(1);
|
||
input_tokens.pop_back();
|
||
}
|
||
}
|
||
}
|
||
else if (input_tokens.size() >= 2)
|
||
{
|
||
string& token = *(input_tokens.rbegin() + 1);
|
||
|
||
if (token[0] == '>' && (token.length() == 1
|
||
|| (token.length() == 2 && token[1] == '>')))
|
||
{
|
||
filename = input_tokens.back();
|
||
append = (token.length() == 2);
|
||
input_tokens.pop_back();
|
||
input_tokens.pop_back();
|
||
}
|
||
}
|
||
}
|
||
|
||
return make_pair(filename, append);
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
bool parseFormulaType(vector<string, ALLOC(string) >& input_tokens)
|
||
/* ----------------------------------------------------------------------------
|
||
*
|
||
* Description: Tests whether the first argument of a command specifies a
|
||
* formula (i.e., whether the first argument of the command is
|
||
* either a `+' or a `-'). If it is, the argument is removed
|
||
* from the vector of strings forming the command.
|
||
*
|
||
* Argument: input_tokens -- A reference to a vector of strings giving
|
||
* the user command.
|
||
*
|
||
* Returns: A truth value according to whether a formula or its negation
|
||
* was specified; the effect of specifying no formula type is
|
||
* the same as giving a `+' as an argument (i.e. the formula
|
||
* type defaults to the positive formula).
|
||
*
|
||
* ------------------------------------------------------------------------- */
|
||
{
|
||
bool formula_type = true;
|
||
|
||
if (input_tokens.size() >= 2)
|
||
{
|
||
formula_type = (input_tokens[1] != "-");
|
||
|
||
if (input_tokens[1].length() == 1
|
||
&& (input_tokens[1][0] == '+' || input_tokens[1][0] == '-'))
|
||
input_tokens.erase(input_tokens.begin());
|
||
}
|
||
|
||
return formula_type;
|
||
}
|
||
|
||
/* ========================================================================= */
|
||
void verifyNumber
|
||
(unsigned long int number, unsigned long int max, const char* error_message)
|
||
/* ----------------------------------------------------------------------------
|
||
*
|
||
* Description: Checks that a given unsigned long integer is less than a
|
||
* given maximum value. Throws an exception with an error
|
||
* message if this is not the case.
|
||
*
|
||
* Argument: number -- Number to be tested.
|
||
* max -- Value the number is to be tested against.
|
||
* error_message -- Error message.
|
||
*
|
||
* Returns: Nothing. Throws an exception if the check fails.
|
||
*
|
||
* ------------------------------------------------------------------------- */
|
||
{
|
||
if (number >= max)
|
||
throw CommandErrorException(string(error_message) + " ("
|
||
+ toString(number) + ").");
|
||
}
|
||
|
||
}
|