<commit_before>#include <boost/property_tree/xml_parser.hpp>
#include"routes.h"
#include"except.h"

#define DBG_DISABLE
#include"dbg.h"

using namespace std;
using namespace boost;
using namespace boost::property_tree;

Route::Route(HttpVerb verb, regex urlRegex, map<int, string> matchParameters, string targetFilename) {
	this->verb = verb;
	this->urlRegex = urlRegex;
	this->matchParameters = matchParameters;
	this->targetFilename = targetFilename;
	this->defaultRoute = false;
}

Route::Route(HttpVerb verb, string targetFilename) {
	this->defaultRoute = true;

	this->verb = verb;
	this->targetFilename = targetFilename;
	this->matchParameters = map<int, string>();
}

bool Route::isMatch(HttpVerb verb, string url, map<string, string>& outParams) {
	outParams.clear();

	if (this->verb != HttpVerb::ANY && verb != this->verb) {
		return false;
	}
	if (defaultRoute) {
		return true;
	}

	cmatch matchVariables;
	if (regex_match(url.c_str(), matchVariables, urlRegex)) {
		for (auto paramRequest : matchParameters) {
			size_t paramIndex = paramRequest.first;
			string paramIdentifier = paramRequest.second;
			if (paramIndex >= matchVariables.size()) {
				BOOST_THROW_EXCEPTION(routeError() <<
				                      stringInfoFromFormat("Error matching URL %1% with route for regex %2%: argument %3% "
				                              "requires position %4%, but there are only %5% capture groups.",
				                              url, urlRegex.str(), paramIdentifier, paramIndex, matchVariables.size()));
			}

			outParams[paramIdentifier]  = string(matchVariables[paramIndex]);
		}

		return true;
	}

	return false;
}

Route Route::getRoute(ptree routePtree) {
	/*  <route>
	 *      <verb>...</verb>
	 *      <regex>...</regex>
	 *      <captures>
	 *          <capture idx="..." identifier="..."/>
	 *          <capture idx="..." identifier="..."/>
	 *      </captures>
	 *
	 *      <target>...</target>
	 *  </route>
	 *
	 * OR
	 *
	 *  <route default="true"/>
	 */

	map<string, HttpVerb> stringVerbMapping = {
		{string("ANY"), HttpVerb::ANY},
		{string("GET"), HttpVerb::GET},
		{string("HEAD"), HttpVerb::HEAD},
		{string("POST"), HttpVerb::POST},
		{string("PUT"), HttpVerb::PUT},
		{string("DELETE"), HttpVerb::DELETE},
		{string("CONNECT"), HttpVerb::CONNECT},
		{string("OPTIONS"), HttpVerb::OPTIONS},
		{string("TRACE"), HttpVerb::TRACE}
	};

	string verbStr = routePtree.get<string>("verb", "GET");
	DBG_FMT("got verb %1%", verbStr);

	auto verbValIt = stringVerbMapping.find(verbStr);
	if (verbValIt == stringVerbMapping.end()) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error: HTTP verb %1% not recognized.", verbStr));
	}

	string target = routePtree.get<string>("target", "$default$");
	DBG_FMT("got target %1%", target);

	string defaultValue = routePtree.get<string>("<xmlattr>.default", "false");
	DBG_FMT("got default %1%", defaultValue);

	if (defaultValue != "false") {
		if (defaultValue == "true") {
			return Route(verbValIt->second, target);
		}
		else {
			BOOST_THROW_EXCEPTION(routeParseError() <<
			                      stringInfo("Error: Found route with attribute 'default', but value is not 'true'."));
		}
	}
	try {
		string urlRegex = routePtree.get<string>("regex");
		DBG_FMT("got url %1%", urlRegex);

		auto captures = routePtree.get_child_optional("captures");
		map<int, string> matchParameters;
		DBG_FMT("got captures: %1%", bool(captures));
		if (captures) {
			for (ptree::value_type const & cap : *captures) {
				if (cap.first != "capture") {
					BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error: unexpected node '%1%' found.", cap.first));
				}
				int idx = cap.second.get<int>("<xmlattr>.idx");
				string identifier = cap.second.get<string>("<xmlattr>.identifier");

				matchParameters[idx] = identifier;
			}
		}

		DBG("got route okay");

		return Route(verbValIt->second, regex(urlRegex), matchParameters, target);
	}
	catch (ptree_bad_path& ex) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error: Could not find route parameter '%1%'.",
		                      ex.path<string>()));
	}
	catch (ptree_bad_data& ex) {
		BOOST_THROW_EXCEPTION(routeParseError() <<
		                      stringInfoFromFormat("Error: Could not convert data '%1%' to the expected type.", ex.data<string>()));
	}
}

vector<Route> getRoutesFromFile(string filename) {
	/*  <routes>
	 *      <route>...</route>
	 *      ...
	 *  </routes>
	 *
	 */
	ptree routesRoot;
	try {
		read_xml(filename, routesRoot);
	}
	catch (xml_parser_error& ex) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error parsing routes file '%1%. Additional data:\n%2%",
		                      filename, ex.what()));
	}

	try {
		routesRoot = routesRoot.get_child("routes");
	}
	catch (ptree_error&) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfo("Error parsing routes: root is not <routes>"));
	}

	vector<Route> results;
	for (ptree::value_type const & rt : routesRoot) {
		DBG("one route");
		if (rt.first != "route") {
			BOOST_THROW_EXCEPTION(routeParseError() <<
			                      stringInfo("Error parsing routes:: one of <routes>'s children is not <route>."));
		}
		try {
			results.push_back(Route::getRoute(rt.second));
		}
		catch (routeParseError) {
			throw;
		}
	}
	return results;
}

vector<Route> getDefaultRoutes(){
	return vector<Route> {Route(HttpVerb::GET, "$default$")};
}

Route& getRouteMatch(vector<Route> routes, HttpVerb verb, string url, map<string, string>& outParams) {
	for (Route & route : routes) {
		if (route.isMatch(verb, url, outParams)) {
			return route;
		}
	}
	BOOST_THROW_EXCEPTION(routeError() <<
	                      stringInfoFromFormat("Error: Could not match url %1% with any route! Is there no default route?", url));
}
<commit_msg>Added method info in 'no routes found' erorr message<commit_after>#include <boost/property_tree/xml_parser.hpp>
#include"routes.h"
#include"except.h"

#define DBG_DISABLE
#include"dbg.h"

using namespace std;
using namespace boost;
using namespace boost::property_tree;

Route::Route(HttpVerb verb, regex urlRegex, map<int, string> matchParameters, string targetFilename) {
	this->verb = verb;
	this->urlRegex = urlRegex;
	this->matchParameters = matchParameters;
	this->targetFilename = targetFilename;
	this->defaultRoute = false;
}

Route::Route(HttpVerb verb, string targetFilename) {
	this->defaultRoute = true;

	this->verb = verb;
	this->targetFilename = targetFilename;
	this->matchParameters = map<int, string>();
}

bool Route::isMatch(HttpVerb verb, string url, map<string, string>& outParams) {
	outParams.clear();

	if (this->verb != HttpVerb::ANY && verb != this->verb) {
		return false;
	}
	if (defaultRoute) {
		return true;
	}

	cmatch matchVariables;
	if (regex_match(url.c_str(), matchVariables, urlRegex)) {
		for (auto paramRequest : matchParameters) {
			size_t paramIndex = paramRequest.first;
			string paramIdentifier = paramRequest.second;
			if (paramIndex >= matchVariables.size()) {
				BOOST_THROW_EXCEPTION(routeError() <<
				                      stringInfoFromFormat("Error matching URL %1% with route for regex %2%: argument %3% "
				                              "requires position %4%, but there are only %5% capture groups.",
				                              url, urlRegex.str(), paramIdentifier, paramIndex, matchVariables.size()));
			}

			outParams[paramIdentifier]  = string(matchVariables[paramIndex]);
		}

		return true;
	}

	return false;
}

Route Route::getRoute(ptree routePtree) {
	/*  <route>
	 *      <verb>...</verb>
	 *      <regex>...</regex>
	 *      <captures>
	 *          <capture idx="..." identifier="..."/>
	 *          <capture idx="..." identifier="..."/>
	 *      </captures>
	 *
	 *      <target>...</target>
	 *  </route>
	 *
	 * OR
	 *
	 *  <route default="true"/>
	 */

	map<string, HttpVerb> stringVerbMapping = {
		{string("ANY"), HttpVerb::ANY},
		{string("GET"), HttpVerb::GET},
		{string("HEAD"), HttpVerb::HEAD},
		{string("POST"), HttpVerb::POST},
		{string("PUT"), HttpVerb::PUT},
		{string("DELETE"), HttpVerb::DELETE},
		{string("CONNECT"), HttpVerb::CONNECT},
		{string("OPTIONS"), HttpVerb::OPTIONS},
		{string("TRACE"), HttpVerb::TRACE}
	};

	string verbStr = routePtree.get<string>("verb", "GET");
	DBG_FMT("got verb %1%", verbStr);

	auto verbValIt = stringVerbMapping.find(verbStr);
	if (verbValIt == stringVerbMapping.end()) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error: HTTP verb %1% not recognized.", verbStr));
	}

	string target = routePtree.get<string>("target", "$default$");
	DBG_FMT("got target %1%", target);

	string defaultValue = routePtree.get<string>("<xmlattr>.default", "false");
	DBG_FMT("got default %1%", defaultValue);

	if (defaultValue != "false") {
		if (defaultValue == "true") {
			return Route(verbValIt->second, target);
		}
		else {
			BOOST_THROW_EXCEPTION(routeParseError() <<
			                      stringInfo("Error: Found route with attribute 'default', but value is not 'true'."));
		}
	}
	try {
		string urlRegex = routePtree.get<string>("regex");
		DBG_FMT("got url %1%", urlRegex);

		auto captures = routePtree.get_child_optional("captures");
		map<int, string> matchParameters;
		DBG_FMT("got captures: %1%", bool(captures));
		if (captures) {
			for (ptree::value_type const & cap : *captures) {
				if (cap.first != "capture") {
					BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error: unexpected node '%1%' found.", cap.first));
				}
				int idx = cap.second.get<int>("<xmlattr>.idx");
				string identifier = cap.second.get<string>("<xmlattr>.identifier");

				matchParameters[idx] = identifier;
			}
		}

		DBG("got route okay");

		return Route(verbValIt->second, regex(urlRegex), matchParameters, target);
	}
	catch (ptree_bad_path& ex) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error: Could not find route parameter '%1%'.",
		                      ex.path<string>()));
	}
	catch (ptree_bad_data& ex) {
		BOOST_THROW_EXCEPTION(routeParseError() <<
		                      stringInfoFromFormat("Error: Could not convert data '%1%' to the expected type.", ex.data<string>()));
	}
}

vector<Route> getRoutesFromFile(string filename) {
	/*  <routes>
	 *      <route>...</route>
	 *      ...
	 *  </routes>
	 *
	 */
	ptree routesRoot;
	try {
		read_xml(filename, routesRoot);
	}
	catch (xml_parser_error& ex) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfoFromFormat("Error parsing routes file '%1%. Additional data:\n%2%",
		                      filename, ex.what()));
	}

	try {
		routesRoot = routesRoot.get_child("routes");
	}
	catch (ptree_error&) {
		BOOST_THROW_EXCEPTION(routeParseError() << stringInfo("Error parsing routes: root is not <routes>"));
	}

	vector<Route> results;
	for (ptree::value_type const & rt : routesRoot) {
		DBG("one route");
		if (rt.first != "route") {
			BOOST_THROW_EXCEPTION(routeParseError() <<
			                      stringInfo("Error parsing routes:: one of <routes>'s children is not <route>."));
		}
		try {
			results.push_back(Route::getRoute(rt.second));
		}
		catch (routeParseError) {
			throw;
		}
	}
	return results;
}

vector<Route> getDefaultRoutes(){
	return vector<Route> {Route(HttpVerb::GET, "$default$")};
}

Route& getRouteMatch(vector<Route> routes, HttpVerb verb, string url, map<string, string>& outParams) {
	for (Route & route : routes) {
		if (route.isMatch(verb, url, outParams)) {
			return route;
		}
	}
	BOOST_THROW_EXCEPTION(routeError() <<
	                      stringInfoFromFormat("Error: Could not match url %1% on method %2% with any route. Is there no default route?",
						  					   url, httpVerbToString(verb)));
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <boost/tokenizer.hpp> //Boost tokenizer
#include <unistd.h> // Fork()
#include <sys/types.h> // Wait()
#include <sys/wait.h> // Wait()
#include <sys/stat.h>
#include <vector>
#include <stdio.h> //Perror()
#include <errno.h> // Perror()
#include <algorithm>
#include <utility>
#include <fcntl.h>

using namespace std;
using namespace boost;

// Chose to write function comparing c-strings rather than copy to string then compare
// Used for checking exit command
bool cStringEqual(char* c1, char* c2)
{
	int i;
	for(i = 0; c1[i] != '\0' && c2[i] != '\0'; ++i)
	{
		if(c1[i] != c2[i])
		{
			return false;
		}
	}
	if(c1[i] != '\0' || c2[i] != '\0')
	{
		return false;
	}
	return true;
}

// file is the file to be outputted to
// replaceFD is the file directory to copy to
// numBrackets == 1 if ">", numBrackets == 2 if ">>"
// used in child process; uses _exit() for that reason
void outputRedir(string file, int replaceFD, int numBrackets)
{
	int fdOpen;
	if(numBrackets == 1)
	{
		if(-1 == (fdOpen = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)))
		{
			perror("Open");
			_exit(1);
		}
	}
	if(numBrackets == 2)
	{
		if(-1 == (fdOpen = open(file.c_str(), O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)))
		{
			perror("Open at");
			_exit(1);
		}
	}

	if(-1 == dup2(fdOpen, replaceFD))
	{
		perror("Open");
		_exit(1);
	}

	if(-1 == close(fdOpen))
	{
		perror("Close");
		_exit(1);
	}
}

// file is the file to be outputted to
// replaceFD is the file directory to copy to
// numBrackets == 1 if "<", numBrackets == 3 if "<<<"
// Used in child process
void inputRedir(string file, int replaceFD, int numBrackets)
{
	int fdInput;
	if(numBrackets == 1)
	{
		if(-1 == (fdInput = open(file.c_str(), O_RDONLY)))
		{
			perror("Open");
			_exit(1);
		}
		if(-1 == dup2(fdInput, replaceFD))
		{
			perror("Open");
			_exit(1);
		}

		if(-1 == close(fdInput))
		{
			perror("Close");
			_exit(1);
		}
	}
	if(numBrackets == 3)
	{
		// In this case, file isn't actually a file; I just didn't rename it; in this case, file is the string to be redirected
		char buf[BUFSIZ];
		for(int i = 0; i < BUFSIZ; ++i)
		{
			buf[i] = file.at(i);
			file.erase(0, BUFSIZ);
		}
		while(file.size() > 0)
		{
			if(-1 == write(replaceFD, buf, BUFSIZ))
			{
				perror("Write");
			}
			for(int i = 0; i < BUFSIZ; ++i)
			{
				buf[i] = file.at(i);
				file.erase(0, BUFSIZ);
			}
		}
	}
}

int main()
{
	//Hostname/loginname won't change
	char* loginName = getlogin();
	if(!loginName)//Returns NULL if fails
	{
		perror("Login error");
		exit(1);
	}

	char hostName[64];
	int hostStatus = gethostname(hostName, sizeof(hostName));
	if(hostStatus == -1)// hostatus is -1 for error
	{
		perror("Hostname error");
		exit(1);
	}

	while(true) //Shell runs until the exit command
	{
		cout << loginName << "@" << hostName << " $ "; // Prints command prompt
		string commandLine;
		getline(cin, commandLine);
		if(commandLine.size() == 0)
		{
			continue;
		}

		// Accounts for comments by removing parts that are comments
		if(commandLine.find(" #") != string::npos)
		{
			commandLine = commandLine.substr(0, commandLine.find(" #"));
		}

		// Finds locations of connectors; a && b, && has a location of 3
		vector<unsigned int> connectorLocs;
		unsigned int marker = 0; // Marks location to start find() from
		while(commandLine.find("&&", marker) != string::npos)//loc != string::npos)
		{
			connectorLocs.push_back(commandLine.find("&&", marker));
			marker = commandLine.find("&&", marker) + 2;//loc + 2; // Starts searching after "&&"
		}
		marker = 0;
		while(commandLine.find("||", marker) != string::npos)
		{
			connectorLocs.push_back(commandLine.find("||", marker));
			marker = commandLine.find("||", marker) + 2; // Starts searching after "||"
		}
		marker = 0;
		while(commandLine.find(";", marker) != string::npos)
		{
			connectorLocs.push_back(commandLine.find(";", marker));
			marker =  commandLine.find(";", marker)+ 1; // Starts searching after ";"
		}
		connectorLocs.push_back(0); // Will be sorted and put in beginning
		sort(connectorLocs.begin(), connectorLocs.end()); // Sorted to find each subcommand substring
		connectorLocs.push_back(commandLine.size()); // One past end index will act like connector

		// Runs through subcommands and runs each one
		// Works for connectors with nothing between them (tokenizer will have "" => syntax error, which is expected)
		// # of subcommands == # of connectors - 1 (including 0, one-past-end)
		for(unsigned int i = 0; i < connectorLocs.size() - 1; ++i)
		{
			int offset = 0; // Tells how much offset for connectors (&&, ||, ;)
			if(commandLine.at(connectorLocs.at(i)) == '&' || commandLine.at(connectorLocs.at(i)) == '|')
			{
				offset = 2;
			}
			else
			{
				if(commandLine.at(connectorLocs.at(i)) == ';')
				{
					offset = 1;
				}
			}

			//cout << commandLine.at(connectorLocs.at(i)) << endl; // DEBUGGING
			//cout << offset << endl; // DEBUGGING

			// For parsing line of commands; delimiter is whitespace, each token will be a command or an argument
			vector<string> strArgs;
			vector<pair<string, string>> ioFiles; // First string is which io redirect (<, >, >>), second is file
			// Use this instead of map, since map doesn't allow duplicates for a key

			char_separator<char> sep(" \t");
			// FOLLOWING LINE WILL BREAK IF USED DIRECTLY IN TOKENIZER
			string subcommand = commandLine.substr(connectorLocs.at(i) + offset, connectorLocs.at(i+1) - connectorLocs.at(i) - offset);
			//typedef tokenizer<char_separator<char>> tokenizer; // Used to use this
			//cout << "sub: " << subcommand << endl; // DEBUGGING
			tokenizer<char_separator<char>> tok(subcommand, sep);
			// First token is the command, other tokens are the arguments
			for(auto iter = tok.begin(); iter != tok.end(); ++iter)
			{
				//cout << "tok: " << *iter << endl; // DEBUGGING
				string tokenString = *iter;

				//Token has no spaces
				//Output redirection
				string::size_type oRedir1 = 0;
				string::size_type oRedir2 = 0;
				// find(">") will not be npos if oRedir2 = tokenString.find(">>")
				while((oRedir1 = tokenString.find(">")) != string::npos)
				{
					oRedir2 = tokenString.find(">>");
					unsigned int offset = 0;
					if(oRedir1 < oRedir2)
					{
						offset = 1;
					}
					else
					{
						offset = 2;
					}

					string::size_type nextRedir;
					// Will include ">>" and "<<<"
					nextRedir = min(tokenString.find(">", oRedir1 + offset), tokenString.find("<", oRedir1 + offset));
					if(nextRedir == string::npos)
					{
						// For substring purposes
						nextRedir = tokenString.size();
					}

					string redirFile = tokenString.substr(oRedir1 + offset, nextRedir - oRedir1 - offset);

					pair<string, string> tempPair("", redirFile);
					if(offset == 1)
					{
						tempPair.first = ">";
					}
					else
					{
						tempPair.first = ">>";
					}
					ioFiles.push_back(tempPair);

					tokenString = tokenString.substr(0, oRedir1) + tokenString.substr(nextRedir, tokenString.size() - nextRedir);
				}

				//Input redirection
				string::size_type iRedir1 = 0;
				string::size_type iRedir3 = 0;
				while((iRedir1 = tokenString.find("<")) != string::npos)
				{
					iRedir3 = tokenString.find("<<<");
					unsigned int offset = 0;
					if(iRedir1 < iRedir3)
					{
						offset = 1;
					}
					else
					{
						offset = 3;
					}

					string::size_type nextRedir;
					// Will include ">>" and "<<<"
					nextRedir = min(tokenString.find("<", oRedir1 + offset), tokenString.find("<", oRedir1 + offset));
					if(nextRedir == string::npos)
					{
						// For substring purposes
						nextRedir = tokenString.size();
					}

					string redirFile = tokenString.substr(iRedir1 + offset, nextRedir - iRedir1 - offset);

					pair<string, string> tempPair("", redirFile);
					if(offset == 1)
					{
						tempPair.first = "<";
					}
					else
					{
						tempPair.first = "<<<";
					}
					ioFiles.push_back(tempPair);

					tokenString = tokenString.substr(0, iRedir1) + tokenString.substr(nextRedir, tokenString.size() - nextRedir);
				}
				if(tokenString.size() > 0)
				{
					strArgs.push_back(tokenString);
				}
			}

			// Copy strArgs to vector of c-strings
			// NEED TO DO IT THIS WAY OR THERE'S ISSUES WITH POINTERS
			vector<char*> args;
			for(auto str : strArgs)
			{
				args.push_back(const_cast<char*> (str.c_str()));
			}
			args.push_back(NULL); // NULL terminating at the end of vector/array

			//Blank command or consecutive connectors
			if(args.size() == 1 && ioFiles.size() == 0)
			{
				continue;
			}

			char* exitCString = const_cast<char*> ("exit");

			//cout << cStringEqual(args.at(0), exitCString) << endl; // DEBUGGING
			if(args.size() > 1 && cStringEqual(args.at(0), exitCString)) // if command is exit, exit shell
			{
				exit(0);
			}

			// Executes commands/takes care of errors
			int pid = fork();
			if(pid == -1) // If fork fails
			{
				perror("Fork error");
				exit(1);
			}
			else
			{
				if(pid == 0) // Child process
				{
					// Reminder: vector<pair<string, string>> ioFiles; First string is which io redirect (<, >, >>), second is file
					for(auto iter = ioFiles.begin(); iter != ioFiles.end(); ++iter)
					{
						if(iter->first == ">")
						{
							outputRedir(iter->second, 1, 1);
						}
						else if(iter->first == ">>")
						{
							outputRedir(iter->second, 1, 2);
						}
						else if(iter->first == "<")
						{
							inputRedir(iter->second, 0, 1);
						}
						else if(iter->first == "<<<")
						{
							inputRedir(iter->second, 0, 3);
						}
						else
						{
							// Shouldn't ever happen but just in case
							cerr << "IO redirection error" << endl;
							_exit(1);
						}
					}
					execvp(args.at(0), &(args[0]));
					// Following don't run if execvp succeeds
					perror("Command execution error");
					_exit(1);
				}
				else // Parent process
				{
					int status; // Status isn't used but might use in future?
					int waitVar = wait(&status);
					if(waitVar == -1) // If child process has error
					{
						perror("Child process error");
						// exits if next connector is && or one-past-end element
						// continues if next connector is ; or ||

					}
					else
					{
						int exitStatus = WEXITSTATUS(status); // Checks whether returns 0/1 when exiting
						if(exitStatus == 1) // If unsuccessful command
						{
							if(connectorLocs.at(i+1) < commandLine.size() &&
								commandLine.at(connectorLocs.at(i+1)) == '&')
							{
								//cout << commandLine.at(connectorLocs.at(i+1)) << endl; // DEBUGGING
								break;
							}
						}
						else
						{
							if(connectorLocs.at(i+1) < commandLine.size() &&
								commandLine.at(connectorLocs.at(i+1)) == '|')
							{
								//cout << commandLine.at(connectorLocs.at(i+1)) << endl; // DEBUGGING
								break;
							}
						}
					}
				}
			}
		}
	}
	return 0;
}
<commit_msg>Finished input redirection for files; need to fix for strings; fix parsing for io redirection<commit_after>#include <iostream>
#include <string>
#include <boost/tokenizer.hpp> //Boost tokenizer
#include <unistd.h> // Fork()
#include <sys/types.h> // Wait()
#include <sys/wait.h> // Wait()
#include <sys/stat.h>
#include <vector>
#include <stdio.h> //Perror()
#include <errno.h> // Perror()
#include <algorithm>
#include <utility>
#include <fcntl.h>

using namespace std;
using namespace boost;

// Chose to write function comparing c-strings rather than copy to string then compare
// Used for checking exit command
bool cStringEqual(char* c1, char* c2)
{
	int i;
	for(i = 0; c1[i] != '\0' && c2[i] != '\0'; ++i)
	{
		if(c1[i] != c2[i])
		{
			return false;
		}
	}
	if(c1[i] != '\0' || c2[i] != '\0')
	{
		return false;
	}
	return true;
}

// file is the file to be outputted to
// replaceFD is the file directory to copy to
// numBrackets == 1 if ">", numBrackets == 2 if ">>"
// used in child process; uses _exit() for that reason
void outputRedir(string file, int replaceFD, int numBrackets)
{
	int fdOpen;
	if(numBrackets == 1)
	{
		if(-1 == (fdOpen = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR)))
		{
			perror("Open");
			_exit(1);
		}
	}
	if(numBrackets == 2)
	{
		if(-1 == (fdOpen = open(file.c_str(), O_WRONLY | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR)))
		{
			perror("Open at");
			_exit(1);
		}
	}

	if(-1 == dup2(fdOpen, replaceFD))
	{
		perror("Open");
		_exit(1);
	}

	if(-1 == close(fdOpen))
	{
		perror("Close");
		_exit(1);
	}
}

// file is the file to be outputted to
// replaceFD is the file directory to copy to
// numBrackets == 1 if "<", numBrackets == 3 if "<<<"
// Used in child process
void inputRedir(string file, int replaceFD, int numBrackets)
{
	int fdInput;
	if(numBrackets == 1)
	{
		if(-1 == (fdInput = open(file.c_str(), O_RDONLY)))
		{
			perror("Open");
			_exit(1);
		}
		if(-1 == dup2(fdInput, replaceFD))
		{
			perror("Open");
			_exit(1);
		}

		if(-1 == close(fdInput))
		{
			perror("Close");
			_exit(1);
		}
	}
	if(numBrackets == 3)
	{
		// In this case, file isn't actually a file; I just didn't rename it; in this case, file is the string to be redirected
		char buf[BUFSIZ];
		for(unsigned int i = 0; i < BUFSIZ; ++i)
		{
			if(i < file.size())
			{
				buf[i] = file.at(i);
			}
			else
			{
				break;
			}
		}
		file.erase(0, BUFSIZ);
		while(file.size() > 0)
		{
			if(-1 == write(replaceFD, buf, BUFSIZ))
			{
				perror("Write");
			}
			for(unsigned int i = 0; i < BUFSIZ; ++i)
			{
				if(i < file.size())
				{
					buf[i] = file.at(i);
				}
				else
				{
					break;
				}
			}
			file.erase(0, BUFSIZ);
		}
	}
}

int main()
{
	//Hostname/loginname won't change
	char* loginName = getlogin();
	if(!loginName)//Returns NULL if fails
	{
		perror("Login error");
		exit(1);
	}

	char hostName[64];
	int hostStatus = gethostname(hostName, sizeof(hostName));
	if(hostStatus == -1)// hostatus is -1 for error
	{
		perror("Hostname error");
		exit(1);
	}

	while(true) //Shell runs until the exit command
	{
		cout << loginName << "@" << hostName << " $ "; // Prints command prompt
		string commandLine;
		getline(cin, commandLine);
		if(commandLine.size() == 0)
		{
			continue;
		}

		// Accounts for comments by removing parts that are comments
		if(commandLine.find(" #") != string::npos)
		{
			commandLine = commandLine.substr(0, commandLine.find(" #"));
		}

		// Finds locations of connectors; a && b, && has a location of 3
		vector<unsigned int> connectorLocs;
		unsigned int marker = 0; // Marks location to start find() from
		while(commandLine.find("&&", marker) != string::npos)//loc != string::npos)
		{
			connectorLocs.push_back(commandLine.find("&&", marker));
			marker = commandLine.find("&&", marker) + 2;//loc + 2; // Starts searching after "&&"
		}
		marker = 0;
		while(commandLine.find("||", marker) != string::npos)
		{
			connectorLocs.push_back(commandLine.find("||", marker));
			marker = commandLine.find("||", marker) + 2; // Starts searching after "||"
		}
		marker = 0;
		while(commandLine.find(";", marker) != string::npos)
		{
			connectorLocs.push_back(commandLine.find(";", marker));
			marker =  commandLine.find(";", marker)+ 1; // Starts searching after ";"
		}
		connectorLocs.push_back(0); // Will be sorted and put in beginning
		sort(connectorLocs.begin(), connectorLocs.end()); // Sorted to find each subcommand substring
		connectorLocs.push_back(commandLine.size()); // One past end index will act like connector

		// Runs through subcommands and runs each one
		// Works for connectors with nothing between them (tokenizer will have "" => syntax error, which is expected)
		// # of subcommands == # of connectors - 1 (including 0, one-past-end)
		for(unsigned int i = 0; i < connectorLocs.size() - 1; ++i)
		{
			int offset = 0; // Tells how much offset for connectors (&&, ||, ;)
			if(commandLine.at(connectorLocs.at(i)) == '&' || commandLine.at(connectorLocs.at(i)) == '|')
			{
				offset = 2;
			}
			else
			{
				if(commandLine.at(connectorLocs.at(i)) == ';')
				{
					offset = 1;
				}
			}

			//cout << commandLine.at(connectorLocs.at(i)) << endl; // DEBUGGING
			//cout << offset << endl; // DEBUGGING

			// For parsing line of commands; delimiter is whitespace, each token will be a command or an argument
			vector<string> strArgs;
			vector<pair<string, string>> ioFiles; // First string is which io redirect (<, >, >>), second is file
			// Use this instead of map, since map doesn't allow duplicates for a key

			char_separator<char> sep(" \t");
			// FOLLOWING LINE WILL BREAK IF USED DIRECTLY IN TOKENIZER
			string subcommand = commandLine.substr(connectorLocs.at(i) + offset, connectorLocs.at(i+1) - connectorLocs.at(i) - offset);
			//typedef tokenizer<char_separator<char>> tokenizer; // Used to use this
			//cout << "sub: " << subcommand << endl; // DEBUGGING
			tokenizer<char_separator<char>> tok(subcommand, sep);
			// First token is the command, other tokens are the arguments
			for(auto iter = tok.begin(); iter != tok.end(); ++iter)
			{
				//cout << "tok: " << *iter << endl; // DEBUGGING
				string tokenString = *iter;

				//Token has no spaces
				//Output redirection
				string::size_type oRedir1 = 0;
				string::size_type oRedir2 = 0;
				// find(">") will not be npos if oRedir2 = tokenString.find(">>")
				while((oRedir1 = tokenString.find(">")) != string::npos)
				{
					oRedir2 = tokenString.find(">>");
					unsigned int offset = 0;
					if(oRedir1 < oRedir2)
					{
						offset = 1;
					}
					else
					{
						offset = 2;
					}

					string::size_type nextRedir;
					// Will include ">>" and "<<<"
					nextRedir = min(tokenString.find(">", oRedir1 + offset), tokenString.find("<", oRedir1 + offset));
					if(nextRedir == string::npos)
					{
						// For substring purposes
						nextRedir = tokenString.size();
					}

					string redirFile = tokenString.substr(oRedir1 + offset, nextRedir - oRedir1 - offset);

					pair<string, string> tempPair("", redirFile);
					if(offset == 1)
					{
						tempPair.first = ">";
					}
					else
					{
						tempPair.first = ">>";
					}
					ioFiles.push_back(tempPair);

					tokenString = tokenString.substr(0, oRedir1) + tokenString.substr(nextRedir, tokenString.size() - nextRedir);
				}

				//Input redirection
				string::size_type iRedir1 = 0;
				string::size_type iRedir3 = 0;
				while((iRedir1 = tokenString.find("<")) != string::npos)
				{
					iRedir3 = tokenString.find("<<<");
					unsigned int offset = 0;
					if(iRedir1 < iRedir3)
					{
						offset = 1;
					}
					else
					{
						offset = 3;
					}

					string::size_type nextRedir;
					// Will include ">>" and "<<<"
					nextRedir = min(tokenString.find("<", iRedir1 + offset), tokenString.find("<", iRedir1 + offset));
					if(nextRedir == string::npos)
					{
						// For substring purposes
						nextRedir = tokenString.size();
					}

					string redirFile = tokenString.substr(iRedir1 + offset, nextRedir - iRedir1 - offset);

					pair<string, string> tempPair("", redirFile);
					if(offset == 1)
					{
						tempPair.first = "<";
					}
					else
					{
						tempPair.first = "<<<";
					}
					ioFiles.push_back(tempPair);

					tokenString = tokenString.substr(0, iRedir1) + tokenString.substr(nextRedir, tokenString.size() - nextRedir);
				}
				if(tokenString.size() > 0)
				{
					strArgs.push_back(tokenString);
				}
			}

			// Copy strArgs to vector of c-strings
			// NEED TO DO IT THIS WAY OR THERE'S ISSUES WITH POINTERS
			vector<char*> args;
			for(auto str : strArgs)
			{
				args.push_back(const_cast<char*> (str.c_str()));
			}
			args.push_back(NULL); // NULL terminating at the end of vector/array

			//Blank command or consecutive connectors
			if(args.size() == 1 && ioFiles.size() == 0)
			{
				continue;
			}

			char* exitCString = const_cast<char*> ("exit");

			//cout << cStringEqual(args.at(0), exitCString) << endl; // DEBUGGING
			if(args.size() > 1 && cStringEqual(args.at(0), exitCString)) // if command is exit, exit shell
			{
				exit(0);
			}

			// Executes commands/takes care of errors
			int pid = fork();
			if(pid == -1) // If fork fails
			{
				perror("Fork error");
				exit(1);
			}
			else
			{
				if(pid == 0) // Child process
				{
					// Reminder: vector<pair<string, string>> ioFiles; First string is which io redirect (<, >, >>), second is file
					for(auto iter = ioFiles.begin(); iter != ioFiles.end(); ++iter)
					{
						if(iter->first == ">")
						{
							outputRedir(iter->second, 1, 1);
						}
						else if(iter->first == ">>")
						{
							outputRedir(iter->second, 1, 2);
						}
						else if(iter->first == "<")
						{
							inputRedir(iter->second, 0, 1);
						}
						else if(iter->first == "<<<")
						{
							inputRedir(iter->second, 0, 3);
						}
						else
						{
							// Shouldn't ever happen but just in case
							cerr << "IO redirection error" << endl;
							_exit(1);
						}
					}
					execvp(args.at(0), &(args[0]));
					// Following don't run if execvp succeeds
					perror("Command execution error");
					_exit(1);
				}
				else // Parent process
				{
					int status; // Status isn't used but might use in future?
					int waitVar = wait(&status);
					if(waitVar == -1) // If child process has error
					{
						perror("Child process error");
						// exits if next connector is && or one-past-end element
						// continues if next connector is ; or ||

					}
					else
					{
						int exitStatus = WEXITSTATUS(status); // Checks whether returns 0/1 when exiting
						if(exitStatus == 1) // If unsuccessful command
						{
							if(connectorLocs.at(i+1) < commandLine.size() &&
								commandLine.at(connectorLocs.at(i+1)) == '&')
							{
								//cout << commandLine.at(connectorLocs.at(i+1)) << endl; // DEBUGGING
								break;
							}
						}
						else
						{
							if(connectorLocs.at(i+1) < commandLine.size() &&
								commandLine.at(connectorLocs.at(i+1)) == '|')
							{
								//cout << commandLine.at(connectorLocs.at(i+1)) << endl; // DEBUGGING
								break;
							}
						}
					}
				}
			}
		}
	}
	return 0;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <cstring>
#include <vector>
#include <boost/algorithm/string/trim.hpp>

using namespace std;
using namespace boost;

bool done = false;
void onlyright(string command)
{
	char *token;
	char* cmd = new char[command.size()];
	strcpy(cmd, command.c_str());
	token = strtok(cmd, ">");
	vector<string> holder;
	while(token != NULL)
	{
		holder.push_back(string(token));
		token = strtok(NULL, ">");
	}
}
void orfinder(string filter, vector<string> &take)
{
	unsigned int start = 0;
	unsigned int cut = 0;
	bool finished = false;
	bool complete = false;
	bool over = false;
	string copy = filter;
	while(!finished)
	{
		for(unsigned int i = start; i <= filter.size()-2 && !complete; ++i)
		{
			if((filter.at(i) == '|') && (filter.at(i+1) == '|'))
			{
				copy = copy.substr(start, cut);
				trim(copy);
				take.push_back(copy);
				while(i < filter.size() && filter.at(i) == '|')
				{
					++i;
				}
				if(i == filter.size())
				{
					over = true;
				}
				else
				{
					start = i;
				}
				complete = true;
			}
			else
			{
				cut++;
			}
		}
		copy = filter;
		if(!complete || over)
		{
			copy = copy.substr(start, copy.size()-start);
			trim(copy);
			take.push_back(copy);
			finished = true;
		}
		else
		{
			cut = 0;
			complete = false;
			over = false;
		}
	}
}

bool charfinder(const char element, const string search)
{
	char last = 0;
	bool condition = false;
	for(unsigned int i = 0; i <= search.size()-2; ++i)
	{
		if(search.at(i) == element && last != element && search.at(i+1) != element)
		{
			condition = true;
		}
		if(i == search.size()-2)
		{
			last = search.at(i);
			if(search.at(i+1) == element && last != element)
			{
				condition = true;
			}
		}
		last = search.at(i);
	}
	return condition;
}

void finder(const string search, bool &haspipe, bool &hasleft, bool &has2right, bool &hasright)
{
	bool complete = false;
	for(unsigned int i = 0; i <= search.size()-2 && !complete; ++i)
	{
		if((search.at(i) == '>') && (search.at(i+1) == '>'))
		{
			has2right = true;
			complete = true;
		}
	}
	hasleft = charfinder('<', search);
	hasright = charfinder('>', search);
	haspipe = charfinder('|', search);
}

void normalBash(string command)
{
	char *token;
	vector<string> sc_cmd;		//sc_cmd stands for semicolon_commands - this holds the commands in string form
	vector<string> copy;		//this will hold the new commands from sc_cmd without any comments for a short while
	vector<string> and_cmd;		//this will hold the string of single commands combined by &&
	vector<string> or_cmd;		//this will hold the string of commands combined by ||
	char* cmd = new char[command.size()];
	if(command.size() > 0)
	{
		copy.push_back(command);
	}
	if(copy.size() > 0)
	{
		strcpy(cmd, (copy.at(0)).c_str());
		copy.clear();
		token = strtok(cmd, ";");
		while(token != NULL)
		{
			sc_cmd.push_back(string(token)); //parse the string, looking for semicolons to split the commands
			token = strtok(NULL, ";");
		}
		for(unsigned int i = 0; i < sc_cmd.size(); ++i)
		{
			strcpy(cmd, (sc_cmd.at(i)).c_str());
			token = strtok(cmd, "#");		//parse the string looking for the start of a comment
			if(token != NULL)		//then only add the beginning part of the command and throw away everything after the #
			{
				if(string(token).find_first_not_of(' ') != std::string::npos) //if the parsed string is only white spaces
				{									//then dont add it list of commands
					copy.push_back(string(token));
				}
			}
		}
		sc_cmd.clear();
		for(unsigned int i = 0; i < copy.size(); ++i)
		{
			sc_cmd.push_back(copy.at(i));	//move the new filtered commands into the proper vector
		}
		copy.clear();
		for(unsigned int i = 0; i < sc_cmd.size(); ++i)	//keep executing these commands regardless of result from previous command
		{
			trim(sc_cmd.at(i));		//remove all the unnecessary white spaces around the string if in there
			orfinder(sc_cmd.at(i), or_cmd);
			unsigned int fails = 0;
			for(unsigned int i = 0; i < or_cmd.size(); ++i)	//keep doing || commands as long as the last command failed
			{
				trim(or_cmd.at(i));
				if(fails == i)
				{
					strcpy(cmd, (or_cmd.at(i)).c_str());
					token = strtok(cmd, "&&");
					while(token != NULL)	//parse the string command for && and split the code down to single commands
					{
						and_cmd.push_back(string(token));
						token = strtok(NULL, "&&");
					}
					unsigned int trues = 0;
					for(unsigned int i = 0; i < and_cmd.size(); ++i) //keep doing && commands as long as the last command succeeded
					{
						trim(and_cmd.at(i));
						bool haspipe = false;
						bool hasleft = false;
						bool has2right = false;
						bool hasright = false;
						finder(and_cmd.at(i), haspipe, hasleft, has2right, hasright);
						if(trues == i)
						{
							strcpy(cmd, (and_cmd.at(i)).c_str());
							token = strtok(cmd, " ");
							while(token != NULL)	//break down the command into strings
							{
								copy.push_back(string(token));
								token = strtok(NULL, " ");
							}
							char **argv = new char*[copy.size()+1];	//create an array of char pointers
							for(unsigned int j = 0; j < copy.size(); ++j)
							{
								trim(copy.at(j));	//add char pointers into an array of pointers
								argv[j] = const_cast<char*>((copy.at(j)).c_str());	//this will allow us to use execvp
							}
							argv[copy.size()] = 0;
							int pid = fork();
							if(pid == -1)
							{
								perror("There was an error with fork() ");
							}
							else if(pid == 0)
							{
								if(and_cmd.at(i) != "exit")	//only move along to execvp if the command is not exit
								{
									if(haspipe || hasleft || has2right || hasright)
									{
										cout << "I/O redirection" << endl;
										_exit(0);
									}
									else
									{
										if(-1 == execvp((copy.at(0)).c_str(), argv))
										{
											perror("There was an error with execvp() ");
											_exit(1);	//if the command failed, then kill the child process and exit
										}
									}
								}
								else //if the command is to exit, dont do anything, simply kill the child process and exit
								{
									_exit(1);
								}
							}
							else
							{
								int status;
								wait(&status);
								if(status == -1)
								{
									perror("There was an error with wait() ");
								}
								else if(status == 0)	//if the last command was executed successfully, increase the counter
								{
									++trues;
								}
								if(and_cmd.at(i) == "exit")	//if the user entered the exit command, end the shell and program
								{
									delete []argv;		//since we are done with the program early, we must delete
									delete []cmd;		//dynamic memory from leaking out
									copy.clear();
									and_cmd.clear();
									or_cmd.clear();
									sc_cmd.clear();
									done = true;
									return;
								}
							}
							delete []argv;	//make sure to delete the dynamically allocated memory
						}
						copy.clear();	//clear out the vector to be used again
					}	//end of and_cmd
					if(trues != and_cmd.size())
					{
						fails++;   //if the amount of commands that were successfully executed is not the same as the
							//number of commands executed, then increase the number of commands failed
					}
				}
				and_cmd.clear();	//clear out the commands in and vector since we are done
			}   //end of or_cmd
			or_cmd.clear();	 //clear out the commands in the or vector since we are done
		}	//end of sc_cmd
		sc_cmd.clear();	//clear out the commands in the semicolon vector since we are done
	}
	delete []cmd;//make sure to delete the dynamically allocated memory
}

int main()
{
	char *user = getlogin();	//gets the user name
	string command;
	if(user == NULL)
	{
		perror("There was an error with getlogin() ");
	}
	char name[100];				//initialize empty string to be used to get the host name
	int check = gethostname(name, 100);		//gets the host's name and stores it in the char array variable name
	if(check == -1)
	{
		perror("There was en error with gethostname() ");
	}
	while(!done)		//loop until user enters exit and the loop is terminated
	{
		if((user != NULL) && (check == 0))
		{
			cout << user << "@" << name << " $ ";	//output user name, @ symbol, the host name, followed by $
		}			// output at this point should result in: [user]@[host]$
		else
		{
			cout << "$ ";
		}
		getline(cin, command);	//get the entire line of command from user
		unsigned int loc = command.find_first_of("#");	//keep only everything before the first comment mark
		command = command.substr(0, loc);
		if(command == "exit")
		{
			done = true;	//if the command they type in is initially exit
					//then there is no need to continue traversing the program, simply end
		}
		else
		{
			normalBash(command);
		}
	}
	return 0;
}
<commit_msg>completed full > implementation<commit_after>#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <string.h>
#include <cstring>
#include <vector>
#include <fcntl.h>
#include <cstdlib>
#include <errno.h>
#include <sys/stat.h>
#include <boost/algorithm/string/trim.hpp>

using namespace std;
using namespace boost;

bool done = false;

void onlyright(string command)
{
	char *token;
	char* cmd = new char[command.size()];
	strcpy(cmd, command.c_str());
	token = strtok(cmd, ">");
	vector<string> holder;
	while(token != NULL)
	{
		holder.push_back(string(token));
		token = strtok(NULL, ">");
	}
	int savestdout;
	if(-1 == (savestdout = dup(1)))
	{
		perror("There was an error with dup(). ");
		exit(1);
	}
	if(-1 == close(1))
	{
		perror("There was an error with close(). ");
		exit(1);
	}
	vector<vector<string> > all_cmd;
	vector<string> to_use;
	for(unsigned int i = 0; i < holder.size(); ++i)
	{
		trim(holder.at(i));
		strcpy(cmd, (holder.at(i)).c_str());
		token = strtok(cmd, " ");
		while(token != NULL)
		{
			to_use.push_back(string(token));
			token = strtok(NULL, " ");
		}
		all_cmd.push_back(to_use);
		to_use.clear();
	}
	if(all_cmd.size() == 1)
	{
		cerr << "rshell: syntax error near unexpected token `newline' " << endl;
		exit(1);
	}
	string master = all_cmd.at(all_cmd.size()-1).at(0);
	int write_to;
	if(-1 == (write_to = open(master.c_str(), O_CREAT | O_TRUNC | O_APPEND | O_WRONLY)))
	{
		perror("There was an error with open() .");
		exit(1);
	}
	int savestdin;
	if(-1 == (savestdin = dup(0)))
	{
		perror("There was an error with dup(). ");
		exit(1);
	}
	if(-1 == close(0))
	{
		perror("There was an error with close(). ");
		exit(1);
	}
	to_use.clear();
	bool special_case = false;
	bool single_special = false;
	unsigned int determine = 0;
	for(unsigned int i = 0; i < all_cmd.size(); ++i)
	{
		to_use = all_cmd.at(i);
		if(i == 0)
		{
			if((to_use.size() == 1) && (to_use.at(i) == "cat"))
			{
				determine++;
				single_special = true;
			}
		}
		else
		{
			if(to_use.size() == 1)
			{
				determine++;
			}
		}
		to_use.clear();
	}
	if(determine == all_cmd.size())
	{
		special_case = true;
	}
	for(unsigned int i = 0; i < all_cmd.size(); ++i)
	{
		to_use.clear();
		if(i == 0)
		{
			to_use = all_cmd.at(i);
			if(special_case == true)
			{
				string inputs;
				if(-1 == (dup2(savestdin, 0)))
				{
					perror("There was an error with dup2(). ");
					exit(1);
				}
				while(special_case)
				{
					getline(cin, inputs);
					int size;
					char c[BUFSIZ];
					if(-1 == (size = read(0, &c, BUFSIZ)))
					{
						perror("There was an error with read(). ");
						exit(1);
					}
					while(size > 0)
					{
						if(-1 == write(write_to, c, size))
						{
							perror("There was an error with write(). ");
							exit(1);
						}
						if(-1 == (size = read(0, &c, BUFSIZ)))
						{
							perror("There was an error with read(). ");
							exit(1);
						}
					}
				}
				if(-1 == (savestdin = dup(0)))
				{
					perror("There was an error with dup(). ");
					exit(1);
				}
				if(-1 == close(0))
				{
					perror("There was an error with close(). ");
					exit(1);
				}
			}
			else if(single_special == true)
			{
				//do nothing
			}
			else
			{
				char **argv = new char*[to_use.size()+1];	//create an array of char pointers
				for(unsigned int j = 0; j < to_use.size(); ++j)
				{
					trim(to_use.at(j));	//add char pointers into an array of pointers
					argv[j] = const_cast<char*>((to_use.at(j)).c_str());	//this will allow us to use execvp
				}
				argv[to_use.size()] = 0;
				int pid = fork();
				if(pid == -1)
				{
					perror("There was an error with fork(). ");
					exit(1);
				}
				else if(pid == 0)
				{
					if(-1 == execvp((to_use.at(0)).c_str(), argv))
					{
						perror("There was an error with execvp(). ");
						_exit(1);
					}
				}
				else
				{
					int status;
					wait(&status);
					if(status == -1)
					{
						perror("There was an error with wait(). ");
						exit(1);
					}
				}
				delete []argv;
			}
		}
		else
		{
			to_use = all_cmd.at(i);
			int start = (i == all_cmd.size()-1) ? 1 : 0;
			for(unsigned int k = start; k < to_use.size(); ++k)
			{
				int read_from;
				if(k == 0)
				{
					if(-1 == (read_from = open((to_use.at(k)).c_str(), O_CREAT | O_TRUNC)))
					{
						perror("There was an error with open(). ");
						exit(1);
					}
					if(-1 == close(read_from))
					{
						perror("There was an error with close(). ");
						exit(1);
					}
				}
				else
				{
					if(-1 == (read_from = open((to_use.at(k)).c_str(), O_CREAT | O_RDONLY)))
					{
						perror("There was an error with open(). ");
						exit(1);
					}
					int size;
					char c[BUFSIZ];
					if(-1 == (size = read(read_from, &c, BUFSIZ)))
					{
						perror("There was an error with read(). ");
						exit(1);
					}
					while(size > 0)
					{
						if(-1 == write(write_to, c, size))
						{
							perror("There was an error with write(). ");
							exit(1);
						}
						if(-1 == (size = read(read_from, &c, BUFSIZ)))
						{
							perror("There was an error with read(). ");
							exit(1);
						}
					}
					if(-1 == close(read_from))
					{
						perror("There was an error with close(). ");
						exit(1);
					}
				}
			}
		}
	}
	delete []cmd;
	if(-1 == close(write_to))
	{
		perror("There was an error with close(). ");
		exit(1);
	}
	if((-1 == dup2(savestdin, 0)) || (-1 == dup2(savestdout, 1)))
	{
		perror("There was an error with dup2(). ");
		exit(1);
	}
}

void orfinder(string filter, vector<string> &take)
{
	unsigned int start = 0;
	unsigned int cut = 0;
	bool finished = false;
	bool complete = false;
	bool over = false;
	string copy = filter;
	while(!finished)
	{
		for(unsigned int i = start; i <= filter.size()-2 && !complete; ++i)
		{
			if((filter.at(i) == '|') && (filter.at(i+1) == '|'))
			{
				copy = copy.substr(start, cut);
				trim(copy);
				take.push_back(copy);
				while(i < filter.size() && filter.at(i) == '|')
				{
					++i;
				}
				if(i == filter.size())
				{
					over = true;
				}
				else
				{
					start = i;
				}
				complete = true;
			}
			else
			{
				cut++;
			}
		}
		copy = filter;
		if(!complete || over)
		{
			copy = copy.substr(start, copy.size()-start);
			trim(copy);
			take.push_back(copy);
			finished = true;
		}
		else
		{
			cut = 0;
			complete = false;
			over = false;
		}
	}
}

bool charfinder(const char element, const string search)
{
	char last = 0;
	bool condition = false;
	for(unsigned int i = 0; i <= search.size()-2; ++i)
	{
		if(search.at(i) == element && last != element && search.at(i+1) != element)
		{
			condition = true;
		}
		if(i == search.size()-2)
		{
			last = search.at(i);
			if(search.at(i+1) == element && last != element)
			{
				condition = true;
			}
		}
		last = search.at(i);
	}
	return condition;
}

void finder(const string search, bool &haspipe, bool &hasleft, bool &has2right, bool &hasright)
{
	bool complete = false;
	for(unsigned int i = 0; i <= search.size()-2 && !complete; ++i)
	{
		if((search.at(i) == '>') && (search.at(i+1) == '>'))
		{
			has2right = true;
			complete = true;
		}
	}
	hasleft = charfinder('<', search);
	hasright = charfinder('>', search);
	haspipe = charfinder('|', search);
}

void normalBash(string command)
{
	char *token;
	vector<string> sc_cmd;		//sc_cmd stands for semicolon_commands - this holds the commands in string form
	vector<string> copy;		//this will hold the new commands from sc_cmd without any comments for a short while
	vector<string> and_cmd;		//this will hold the string of single commands combined by &&
	vector<string> or_cmd;		//this will hold the string of commands combined by ||
	char* cmd = new char[command.size()];
	if(command.size() > 0)
	{
		copy.push_back(command);
	}
	if(copy.size() > 0)
	{
		strcpy(cmd, (copy.at(0)).c_str());
		copy.clear();
		token = strtok(cmd, ";");
		while(token != NULL)
		{
			sc_cmd.push_back(string(token)); //parse the string, looking for semicolons to split the commands
			token = strtok(NULL, ";");
		}
		for(unsigned int i = 0; i < sc_cmd.size(); ++i)
		{
			strcpy(cmd, (sc_cmd.at(i)).c_str());
			token = strtok(cmd, "#");		//parse the string looking for the start of a comment
			if(token != NULL)		//then only add the beginning part of the command and throw away everything after the #
			{
				if(string(token).find_first_not_of(' ') != std::string::npos) //if the parsed string is only white spaces
				{									//then dont add it list of commands
					copy.push_back(string(token));
				}
			}
		}
		sc_cmd.clear();
		for(unsigned int i = 0; i < copy.size(); ++i)
		{
			sc_cmd.push_back(copy.at(i));	//move the new filtered commands into the proper vector
		}
		copy.clear();
		for(unsigned int i = 0; i < sc_cmd.size(); ++i)	//keep executing these commands regardless of result from previous command
		{
			trim(sc_cmd.at(i));		//remove all the unnecessary white spaces around the string if in there
			orfinder(sc_cmd.at(i), or_cmd);
			unsigned int fails = 0;
			for(unsigned int i = 0; i < or_cmd.size(); ++i)	//keep doing || commands as long as the last command failed
			{
				trim(or_cmd.at(i));
				if(fails == i)
				{
					strcpy(cmd, (or_cmd.at(i)).c_str());
					token = strtok(cmd, "&&");
					while(token != NULL)	//parse the string command for && and split the code down to single commands
					{
						and_cmd.push_back(string(token));
						token = strtok(NULL, "&&");
					}
					unsigned int trues = 0;
					for(unsigned int i = 0; i < and_cmd.size(); ++i) //keep doing && commands as long as the last command succeeded
					{
						trim(and_cmd.at(i));
						bool haspipe = false;
						bool hasleft = false;
						bool has2right = false;
						bool hasright = false;
						finder(and_cmd.at(i), haspipe, hasleft, has2right, hasright);
						if(trues == i)
						{
							strcpy(cmd, (and_cmd.at(i)).c_str());
							token = strtok(cmd, " ");
							while(token != NULL)	//break down the command into strings
							{
								copy.push_back(string(token));
								token = strtok(NULL, " ");
							}
							char **argv = new char*[copy.size()+1];	//create an array of char pointers
							for(unsigned int j = 0; j < copy.size(); ++j)
							{
								trim(copy.at(j));	//add char pointers into an array of pointers
								argv[j] = const_cast<char*>((copy.at(j)).c_str());	//this will allow us to use execvp
							}
							argv[copy.size()] = 0;
							int pid = fork();
							if(pid == -1)
							{
								perror("There was an error with fork() ");
							}
							else if(pid == 0)
							{
								if(and_cmd.at(i) != "exit")	//only move along to execvp if the command is not exit
								{
									if(haspipe || hasleft || has2right || hasright)
									{
										cout << "I/O redirection" << endl;
										if(hasright && !haspipe && !hasleft && !has2right)
										{
											onlyright(and_cmd.at(i));
										}
										_exit(0);
									}
									else
									{
										if(-1 == execvp((copy.at(0)).c_str(), argv))
										{
											perror("There was an error with execvp() ");
											_exit(1);	//if the command failed, then kill the child process and exit
										}
									}
								}
								else //if the command is to exit, dont do anything, simply kill the child process and exit
								{
									_exit(1);
								}
							}
							else
							{
								int status;
								wait(&status);
								if(status == -1)
								{
									perror("There was an error with wait() ");
								}
								else if(status == 0)	//if the last command was executed successfully, increase the counter
								{
									++trues;
								}
								if(and_cmd.at(i) == "exit")	//if the user entered the exit command, end the shell and program
								{
									delete []argv;		//since we are done with the program early, we must delete
									delete []cmd;		//dynamic memory from leaking out
									copy.clear();
									and_cmd.clear();
									or_cmd.clear();
									sc_cmd.clear();
									done = true;
									return;
								}
							}
							delete []argv;	//make sure to delete the dynamically allocated memory
						}
						copy.clear();	//clear out the vector to be used again
					}	//end of and_cmd
					if(trues != and_cmd.size())
					{
						fails++;   //if the amount of commands that were successfully executed is not the same as the
							//number of commands executed, then increase the number of commands failed
					}
				}
				and_cmd.clear();	//clear out the commands in and vector since we are done
			}   //end of or_cmd
			or_cmd.clear();	 //clear out the commands in the or vector since we are done
		}	//end of sc_cmd
		sc_cmd.clear();	//clear out the commands in the semicolon vector since we are done
	}
	delete []cmd;//make sure to delete the dynamically allocated memory
}

int main()
{
	char *user = getlogin();	//gets the user name
	string command;
	if(user == NULL)
	{
		perror("There was an error with getlogin() ");
	}
	char name[100];				//initialize empty string to be used to get the host name
	int check = gethostname(name, 100);		//gets the host's name and stores it in the char array variable name
	if(check == -1)
	{
		perror("There was en error with gethostname() ");
	}
	while(!done)		//loop until user enters exit and the loop is terminated
	{
		if((user != NULL) && (check == 0))
		{
			cout << user << "@" << name << " $ ";	//output user name, @ symbol, the host name, followed by $
		}			// output at this point should result in: [user]@[host]$
		else
		{
			cout << "$ ";
		}
		getline(cin, command);	//get the entire line of command from user
		unsigned int loc = command.find_first_of("#");	//keep only everything before the first comment mark
		command = command.substr(0, loc);
		if(command == "exit")
		{
			done = true;	//if the command they type in is initially exit
					//then there is no need to continue traversing the program, simply end
		}
		else
		{
			normalBash(command);
		}
	}
	return 0;
}
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs        */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "Log.h"

#include <string.h>
#include <stdlib.h>
#include <stdexcept>
#include <sstream>
#include <iostream>

#include <sys/types.h>
#include <unistd.h>

#include <syslog.h>

#include "log4cpp/Category.hh"
#include "log4cpp/CategoryStream.hh"
#include "log4cpp/Appender.hh"
#include "log4cpp/SyslogAppender.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/PatternLayout.hh"
#include "log4cpp/Priority.hh"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

const char Log::error_names[] ={ 'E', 'W', 'I', 'D' };

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

FileLog::FileLog(const string&   file_name,
                 const MessageType   level,
                 ios_base::openmode  mode)
        :Log(level), log_file(0)
{
    ofstream    file;

    log_file = strdup(file_name.c_str());

    file.open(log_file, mode);

    if (file.fail() == true)
    {
        free(log_file);

        throw runtime_error("Could not open log file");
    }

    if ( file.is_open() == true )
    {
        file.close();
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

FileLog::~FileLog()
{
    if ( log_file != 0 )
    {
        free(log_file);
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void FileLog::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    char        str[26];
    time_t      the_time;
    ofstream    file;

    if( type <= log_level)
    {
        file.open(log_file, ios_base::app);

        if (file.fail() == true)
        {
            return;
        }

        the_time = time(NULL);

#ifdef SOLARIS
        ctime_r(&(the_time),str,sizeof(char)*26);
#else
        ctime_r(&(the_time),str);
#endif
        // Get rid of final enter character
        str[24] = '\0';

        file << str << " ";
        file << "[" << module << "]";
        file << "[" << error_names[type] << "]: ";
        file << message;
        file << endl;

        file.flush();

        file.close();
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void CerrLog::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    char        str[26];
    time_t      the_time;
    ofstream    file;

    if( type <= log_level)
    {
        the_time = time(NULL);

#ifdef SOLARIS
        ctime_r(&(the_time),str,sizeof(char)*26);
#else
        ctime_r(&(the_time),str);
#endif
        // Get rid of final enter character
        str[24] = '\0';

        cerr << str << " ";
        cerr << "[" << module << "]";
        cerr << "[" << error_names[type] << "]: ";
        cerr << message;
        cerr << endl;

        cerr.flush();
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLog::init(
    Log::MessageType clevel,
    string name,
    string _label)
{
    ostringstream oss_label;
    string label;

    oss_label << _label << "[" << getpid() << "]";
    label = oss_label.str();

    log4cpp::Appender *syslog_appender;
    syslog_appender = new log4cpp::SyslogAppender(name,label,LOG_DAEMON);
    syslog_appender->setLayout(new log4cpp::PatternLayout());
    log4cpp::Category& root = log4cpp::Category::getRoot();
    root.setPriority(SysLog::get_priority_level(clevel));
    root.addAppender(syslog_appender);
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLog::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    log4cpp::Category& root = log4cpp::Category::getRoot();

    log4cpp::Priority::PriorityLevel level = get_priority_level(type);

    root << level   << "[" << module << "]"
                    << "[" << error_names[type] << "]: "
                    << message;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

log4cpp::Priority::PriorityLevel SysLog::get_priority_level(
    const MessageType level)
{
    log4cpp::Priority::PriorityLevel priority_level;

    switch (level)
    {
    case Log::ERROR:
        priority_level = log4cpp::Priority::ERROR;
        break;

    case Log::WARNING:
        priority_level = log4cpp::Priority::WARN;
        break;

    case Log::INFO:
        priority_level = log4cpp::Priority::INFO;
        break;

    case Log::DEBUG:
    case Log::DDEBUG:
    case Log::DDDEBUG:
        priority_level = log4cpp::Priority::DEBUG;
        break;

    default:
        priority_level = log4cpp::Priority::NOTSET;
        break;
    }

    return priority_level;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string SysLogResource::name;

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLogResource::init(
    Log::MessageType clevel,
    string name,
    string _label)
{
    SysLogResource::name = name;

    ostringstream oss_label;
    string label;

    oss_label << _label << "[" << getpid() << "]";
    label = oss_label.str();

    log4cpp::Appender *resource_appender;
    resource_appender = new log4cpp::SyslogAppender(name,label,LOG_DAEMON);

    resource_appender->setLayout(new log4cpp::PatternLayout());
    log4cpp::Category& res = log4cpp::Category::getInstance(name);
    res.addAppender(resource_appender);
    res.setPriority(SysLog::get_priority_level(clevel));
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

SysLogResource::SysLogResource(
    int                             oid,
    const PoolObjectSQL::ObjectType obj_type,
    const MessageType               clevel = WARNING)
    :SysLog(clevel)
{
    ostringstream oss_label;
    string obj_type_str = PoolObjectSQL::type_to_str(obj_type);

    oss_label << "[" << obj_type_str << " " << oid << "]";
    obj_label = oss_label.str();
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLogResource::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    log4cpp::Category& res = log4cpp::Category::getInstance(
                                                        SysLogResource::name);

    log4cpp::Priority::PriorityLevel level = get_priority_level(type);

    res << level    << obj_label
                    << "[" << module << "]"
                    << "[" << error_names[type] << "]: "
                    << message;
}
<commit_msg>Feature #1658: split log messages with newlines into separate messages and log them<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2013, OpenNebula Project (OpenNebula.org), C12G Labs        */
/*                                                                            */
/* Licensed under the Apache License, Version 2.0 (the "License"); you may    */
/* not use this file except in compliance with the License. You may obtain    */
/* a copy of the License at                                                   */
/*                                                                            */
/* http://www.apache.org/licenses/LICENSE-2.0                                 */
/*                                                                            */
/* Unless required by applicable law or agreed to in writing, software        */
/* distributed under the License is distributed on an "AS IS" BASIS,          */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   */
/* See the License for the specific language governing permissions and        */
/* limitations under the License.                                             */
/* -------------------------------------------------------------------------- */

#include "Log.h"

#include <string.h>
#include <stdlib.h>
#include <stdexcept>
#include <sstream>
#include <iostream>

#include <sys/types.h>
#include <unistd.h>

#include <syslog.h>

#include "log4cpp/Category.hh"
#include "log4cpp/CategoryStream.hh"
#include "log4cpp/Appender.hh"
#include "log4cpp/SyslogAppender.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/PatternLayout.hh"
#include "log4cpp/Priority.hh"

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

const char Log::error_names[] ={ 'E', 'W', 'I', 'D' };

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

FileLog::FileLog(const string&   file_name,
                 const MessageType   level,
                 ios_base::openmode  mode)
        :Log(level), log_file(0)
{
    ofstream    file;

    log_file = strdup(file_name.c_str());

    file.open(log_file, mode);

    if (file.fail() == true)
    {
        free(log_file);

        throw runtime_error("Could not open log file");
    }

    if ( file.is_open() == true )
    {
        file.close();
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

FileLog::~FileLog()
{
    if ( log_file != 0 )
    {
        free(log_file);
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void FileLog::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    char        str[26];
    time_t      the_time;
    ofstream    file;

    if( type <= log_level)
    {
        file.open(log_file, ios_base::app);

        if (file.fail() == true)
        {
            return;
        }

        the_time = time(NULL);

#ifdef SOLARIS
        ctime_r(&(the_time),str,sizeof(char)*26);
#else
        ctime_r(&(the_time),str);
#endif
        // Get rid of final enter character
        str[24] = '\0';

        file << str << " ";
        file << "[" << module << "]";
        file << "[" << error_names[type] << "]: ";
        file << message;
        file << endl;

        file.flush();

        file.close();
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void CerrLog::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    char        str[26];
    time_t      the_time;
    ofstream    file;

    if( type <= log_level)
    {
        the_time = time(NULL);

#ifdef SOLARIS
        ctime_r(&(the_time),str,sizeof(char)*26);
#else
        ctime_r(&(the_time),str);
#endif
        // Get rid of final enter character
        str[24] = '\0';

        cerr << str << " ";
        cerr << "[" << module << "]";
        cerr << "[" << error_names[type] << "]: ";
        cerr << message;
        cerr << endl;

        cerr.flush();
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLog::init(
    Log::MessageType clevel,
    string name,
    string _label)
{
    ostringstream oss_label;
    string label;

    oss_label << _label << "[" << getpid() << "]";
    label = oss_label.str();

    log4cpp::Appender *syslog_appender;
    syslog_appender = new log4cpp::SyslogAppender(name,label,LOG_DAEMON);
    syslog_appender->setLayout(new log4cpp::PatternLayout());
    log4cpp::Category& root = log4cpp::Category::getRoot();
    root.setPriority(SysLog::get_priority_level(clevel));
    root.addAppender(syslog_appender);
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLog::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    log4cpp::Category& root = log4cpp::Category::getRoot();
    log4cpp::Priority::PriorityLevel level = get_priority_level(type);

    istringstream   smessage;
    string          line;

    smessage.str(message);

    while ( getline(smessage, line) )
    {
        root << level   << "[" << module << "]"
                        << "[" << error_names[type] << "]: "
                        << line;
    }
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

log4cpp::Priority::PriorityLevel SysLog::get_priority_level(
    const MessageType level)
{
    log4cpp::Priority::PriorityLevel priority_level;

    switch (level)
    {
    case Log::ERROR:
        priority_level = log4cpp::Priority::ERROR;
        break;

    case Log::WARNING:
        priority_level = log4cpp::Priority::WARN;
        break;

    case Log::INFO:
        priority_level = log4cpp::Priority::INFO;
        break;

    case Log::DEBUG:
    case Log::DDEBUG:
    case Log::DDDEBUG:
        priority_level = log4cpp::Priority::DEBUG;
        break;

    default:
        priority_level = log4cpp::Priority::NOTSET;
        break;
    }

    return priority_level;
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

string SysLogResource::name;

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLogResource::init(
    Log::MessageType clevel,
    string name,
    string _label)
{
    SysLogResource::name = name;

    ostringstream oss_label;
    string label;

    oss_label << _label << "[" << getpid() << "]";
    label = oss_label.str();

    log4cpp::Appender *resource_appender;
    resource_appender = new log4cpp::SyslogAppender(name,label,LOG_DAEMON);

    resource_appender->setLayout(new log4cpp::PatternLayout());
    log4cpp::Category& res = log4cpp::Category::getInstance(name);
    res.addAppender(resource_appender);
    res.setPriority(SysLog::get_priority_level(clevel));
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

SysLogResource::SysLogResource(
    int                             oid,
    const PoolObjectSQL::ObjectType obj_type,
    const MessageType               clevel = WARNING)
    :SysLog(clevel)
{
    ostringstream oss_label;
    string obj_type_str = PoolObjectSQL::type_to_str(obj_type);

    oss_label << "[" << obj_type_str << " " << oid << "]";
    obj_label = oss_label.str();
};

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */

void SysLogResource::log(
    const char *            module,
    const MessageType       type,
    const char *            message)
{
    log4cpp::Category& res = log4cpp::Category::getInstance(
                                                        SysLogResource::name);
    log4cpp::Priority::PriorityLevel level = get_priority_level(type);

    istringstream   smessage;
    string          line;

    smessage.str(message);

    while ( getline(smessage, line) )
    {
        res << level    << obj_label
                        << "[" << module << "]"
                        << "[" << error_names[type] << "]: "
                        << line;
    }
}
<|endoftext|>


<commit_before>/*
*	Car.cpp
*/
#include "Smartcar.h"
/* --- CAR --- */

const unsigned short Car::DEFAULT_PID_LOOP_INTERVAL = 80;
const unsigned short Car::STANDARD = 1;
const unsigned short Car::INVERTED = 0;
const float Car::DEFAULT_KP = 5.0;
const float Car::DEFAULT_KI = 0.0;
const float Car::DEFAULT_KD = 10.0;

const unsigned short BACKWARD = 0;
const unsigned short FORWARD = 1;
const unsigned short IDLE = 2;

const int IDLE_RAW_SPEED = 0;
const int MAX_FRONT_RAW_SPEED = 255;
const int MAX_BACK_RAW_SPEED = -255;
const float MAX_BACK_CRUISE_SPEED = -3.0;
const float MAX_FRONT_CRUISE_SPEED = 3.0;
const int STRAIGHT_WHEELS = 90;
const int STRAIGHT_ANGLE = 0;
const int MAX_RIGHT_DEGREES = 180;
const int MAX_LEFT_DEGREES = 0;
const float GO_CRUISE_SPEED = 1.3;
const int GO_RAW_SPEED = 200;
const int MAX_RIGHT_STEERING_ANGLE = MAX_RIGHT_DEGREES - STRAIGHT_WHEELS;
const int MAX_LEFT_STEERING_ANGLE = MAX_LEFT_DEGREES - STRAIGHT_WHEELS;

Car::Car(const unsigned short setup){
	_cruiseControl = false;
	_speed = IDLE_RAW_SPEED;
	_angle = STRAIGHT_WHEELS;
	_numOfEncoders = 0;
	_gyroAttached = false;
	if (setup == STANDARD){ //the default motor setup, where right is right
		MOTOR_LEFT1_PIN = 8;
		MOTOR_LEFT_EN_PIN = 9;
		MOTOR_LEFT2_PIN = 10;
		MOTOR_RIGHT_EN_PIN = 11;
		MOTOR_RIGHT1_PIN = 12;
		MOTOR_RIGHT2_PIN = 13;
	}else{ //the reversed motor setup, where right is left
		MOTOR_RIGHT1_PIN = 8;
		MOTOR_RIGHT_EN_PIN = 9;
		MOTOR_RIGHT2_PIN = 10;
		MOTOR_LEFT_EN_PIN = 11;
		MOTOR_LEFT1_PIN = 12;
		MOTOR_LEFT2_PIN = 13;
	}
}

void Car::begin(Gyroscope gyro){
	begin(0,0,gyro); //pass 0 as a value that indicates null
}

void Car::begin(Odometer encoder, Gyroscope gyro){
	begin(encoder,0,gyro); //pass 0 as a value that indicates null
}

void Car::begin(Odometer encoder1, Odometer encoder2, Gyroscope gyro){
	if (encoder1.isInstanciated()){
		_encoders[0] = encoder1;
		_numOfEncoders++;
	}
	if (encoder2.isInstanciated()){
		_encoders[1] = encoder2;
		_numOfEncoders++;
	}
	if (gyro.isInstanciated()) {
		_gyro = gyro;
		_gyroAttached = true;
	}
	for (uint8_t i = MOTOR_LEFT1_PIN; i <= MOTOR_RIGHT2_PIN; i++) {
		pinMode(i, OUTPUT); //declare pins as outputs
	}
	setSpeed(IDLE_RAW_SPEED);
	setAngle(STRAIGHT_ANGLE);
}

void Car::setSpeed(float newSpeed){
	if (cruiseControlEnabled()){
		if (_speed && (_speed != IDLE_RAW_SPEED) && (newSpeed * _speed) <= 0) stop(); //if the speeds are signed differently, stop the car and then set the new speed. Ignore this if the speed is already 0 and if speed is at the idle raw speed i.e. leftovers from non-cruise control mode (if IDLE_RAW_SPEED is not 0, it makes sense)
		_speed = constrain(newSpeed, MAX_BACK_CRUISE_SPEED, MAX_FRONT_CRUISE_SPEED);
	}else{
		_speed = constrain(int(newSpeed), MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED);
		setMotors(_speed, getAngle());
	}
}

void Car::updateMotors(){
	if (cruiseControlEnabled() && (millis() > _lastMotorUpdate + _pidLoopInterval)){
		if (_speed){ //if _speed is 0, we have already made sure the car is stopped. don't try to adjust if car is just drifting
			float measuredSpeed = getGroundSpeed(); //speed in cm/milliseconds
			measuredSpeed *= 10; //transform it into m/seconds, divide by 100 to turn cm into m and multiply by 1000, to turn ms to sec
			if (_speed < 0) measuredSpeed *= -1; //if we are going reverse, illustrate that in the value of measuredSpeed
			int controlledSpeed = motorPIDcontrol(_previousControlledSpeed, _speed, measuredSpeed);
			setMotors(controlledSpeed, getAngle());
			_previousControlledSpeed = controlledSpeed;
		}
		_lastMotorUpdate = millis();
	}
}

int Car::motorPIDcontrol(const int previousSpeed, const float targetSpeed, const float actualSpeed){
	float correction = 0;
	float error = targetSpeed - actualSpeed;
	_integratedError += error;
	correction = (_Kp * error) + (_Ki * _integratedError) + (_Kd * (error - _previousError));
	_previousError = error;
	return constrain(previousSpeed + int(correction), MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED);
}

float Car::getGroundSpeed(){
	unsigned long currentDistance = getEncoderDistance();
	unsigned long dX = currentDistance - _previousDistance;
	_previousDistance = currentDistance;
	unsigned long dT = millis() - _lastMotorUpdate;
	float velocity = float(dX)/ float(dT);
	return velocity;
}

void Car::setMotors(int rawSpeed, int angle){ //platform specific method
	if (rawSpeed < 0){
		setDirection(BACKWARD);
	}else if (rawSpeed > 0){
		setDirection(FORWARD);
	}else{
		setDirection(IDLE);
	}
	rawSpeed = abs(rawSpeed);
	float ratio = (STRAIGHT_WHEELS - abs(angle));
	ratio /= STRAIGHT_WHEELS;
	if (angle>0){ //going to the right
		analogWrite(MOTOR_LEFT_EN_PIN, rawSpeed);
		analogWrite(MOTOR_RIGHT_EN_PIN, int(rawSpeed * ratio));
	}else{ //going to the left or straight
		analogWrite(MOTOR_RIGHT_EN_PIN, rawSpeed);
		analogWrite(MOTOR_LEFT_EN_PIN, int(rawSpeed * ratio));
	}

}

void Car::setDirection(const unsigned short direction){ //platform specific method
	if (direction == BACKWARD){
		digitalWrite(MOTOR_RIGHT1_PIN, LOW);
		digitalWrite(MOTOR_RIGHT2_PIN, HIGH);
		digitalWrite(MOTOR_LEFT1_PIN, LOW);
		digitalWrite(MOTOR_LEFT2_PIN, HIGH);
	}else if (direction == FORWARD){
		digitalWrite(MOTOR_RIGHT1_PIN, HIGH);
		digitalWrite(MOTOR_RIGHT2_PIN, LOW);
		digitalWrite(MOTOR_LEFT1_PIN, HIGH);
		digitalWrite(MOTOR_LEFT2_PIN, LOW);
	}else{
		digitalWrite(MOTOR_RIGHT1_PIN, LOW);
		digitalWrite(MOTOR_RIGHT2_PIN, LOW);
		digitalWrite(MOTOR_LEFT1_PIN, LOW);
		digitalWrite(MOTOR_LEFT2_PIN, LOW);
	}
}

void Car::setAngle(int angle){ //platform specific method
	_angle = constrain(angle, MAX_LEFT_DEGREES - STRAIGHT_WHEELS, MAX_RIGHT_DEGREES - STRAIGHT_WHEELS); //constrain the value to the permitted valued the user is allowed to supply
	setMotors(getSpeed(), getAngle()); //pass the human-readable value that was just supplied
}

void Car::stop(){ //platform specific method
	if (getSpeed()>0){
		setMotors(-100, getAngle());
	}else if(getSpeed()<0){
		setMotors(100, getAngle());
	}
	delay(80); //go the opposite direction for a few milliseconds, to make sure we are stopped
	if (cruiseControlEnabled()){
		setMotors(0,getAngle()); //shut the engines down, we should be stopped by now
		enableCruiseControl(_Kp,_Ki,_Kd); //re-initialize the cruise control, se we get rid of previous error and pid output
		_speed = 0.0;
	}else{
		setSpeed(0); //no circular dependency between stop and setSpeed due to the cruiseControlEnabled switching between who uses what
	}
}

float Car::getSpeed(){
	if (cruiseControlEnabled()) return getGroundSpeed(); //return speed in meters per second
	return _speed; //return speed in PWM output
}

int Car::getAngle(){
	return _angle; //return what the user supplied
}

void Car::enableCruiseControl(float Kp, float Ki, float Kd, unsigned short pidLoopInterval){
	_cruiseControl = true;
	_pidLoopInterval = pidLoopInterval;
	_lastMotorUpdate = 0;
	_previousControlledSpeed = IDLE_RAW_SPEED;
	_previousDistance = getEncoderDistance();
	_integratedError = 0;
	_Kp = Kp;
	_Ki = Ki;
	_Kd = Kd;
}

void Car::disableCruiseControl(){
	_cruiseControl = false;
	_speed = _previousControlledSpeed; //update the speed with the PWM equivalent
}

boolean Car::cruiseControlEnabled(){
	return _cruiseControl;
}

unsigned long Car::getEncoderDistance(){
	unsigned long averageDistance = 0;
	for (int i = 0; i < _numOfEncoders; i++){
		averageDistance += _encoders[i].getDistance() / _numOfEncoders;
	}
	return averageDistance;
}

void Car::initializeEncoders(){
	for (int i = 0; i < _numOfEncoders; i++){
		_encoders[i].begin();
	}
}

void Car::go(int centimeters){
	if (!_numOfEncoders) return; //continue only if at least one encoder was attached
	unsigned long initialDistance = getEncoderDistance(); //save the current distance the car has covered
	float initialSpeed = getSpeed(); //save the current speed (so we restore it later)
	int initialAngle = getAngle(); //save the current angle (so we restore it later)
	unsigned long targetDistance = initialDistance + abs(centimeters); //how much the car should move, always a positive number
	if (initialDistance > targetDistance){ //this will occur only if the unsigned long variable overflows
		initializeEncoders(); //reinitialize the encoders counter to 0
		targetDistance = abs(centimeters); //now the new target is just the centimeters (since the encoder distance is 0)
	}
	setAngle(STRAIGHT_ANGLE); //set the angle straight
	short direction = 1;
	if (centimeters < 0) direction = -1; //if the user supplied a negative argument, means they want to move backwards
	if (cruiseControlEnabled()){ //depending on whether the cruise control is enabled set the speed
		setSpeed(direction * GO_CRUISE_SPEED);
	}else{
		setSpeed(direction * GO_RAW_SPEED);
	}
	while (getEncoderDistance() < targetDistance){ //while we haven't reached the target distance, keep moving
		if (cruiseControlEnabled()) updateMotors(); //otherwise the pid for the motors won't work
	}
	setSpeed(initialSpeed); //restore to the initial speed
	setAngle(initialAngle); //restore to the inital angle
}
void Car::rotate(int targetDegrees){
	if (!_gyroAttached) return; //continue only if gyroscope is attached
	_gyro.begin(); //initialize the gyro
	float initialSpeed = getSpeed(); //save the current speed (so we restore it later)
	int initialAngle = getAngle(); //save the current angle (so we restore it later)
	if (targetDegrees > 0){ //we should turn to the right (clockwise)
		setAngle(MAX_RIGHT_STEERING_ANGLE); //turn right as steep as you can
	}else{ //turn left (counterclockwise)
		setAngle(MAX_LEFT_STEERING_ANGLE); //turn left as steep as you can
	}
	if (cruiseControlEnabled()){ //depending on whether the cruise control is enabled set the speed
		setSpeed(GO_CRUISE_SPEED);
	}else{
		setSpeed(GO_RAW_SPEED);
	}
	while (abs(_gyro.getAngularDisplacement()) < abs(targetDegrees)){ //while the absolute displacement hasn't reached the (absolute) target, keep turning
		if (cruiseControlEnabled()) updateMotors(); //otherwise the pid for the motors won't work
		_gyro.update(); //update to integrate the latest gyroscope readings
	}
	setSpeed(initialSpeed); //restore to the initial speed
	setAngle(initialAngle); //restore to the inital angle
}

void Car::setMotorSpeed(int leftMotorSpeed, int rightMotorSpeed){ //will set the speed manual to each motor, use with caution
	if (cruiseControlEnabled()) return; //we don't want to be manually messing with the speed, during cruise control
	leftMotorSpeed = constrain(leftMotorSpeed, MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED); //constrain the speed within the allowed limits
	rightMotorSpeed = constrain(rightMotorSpeed, MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED);
	if (leftMotorSpeed<0){ //set left direction backwards
		digitalWrite(MOTOR_LEFT1_PIN, LOW);
		digitalWrite(MOTOR_LEFT2_PIN, HIGH);
	}else{ //front
		digitalWrite(MOTOR_LEFT1_PIN, HIGH);
		digitalWrite(MOTOR_LEFT2_PIN, LOW);
	}
		analogWrite(MOTOR_LEFT_EN_PIN, abs(leftMotorSpeed));//write left speed
	if (rightMotorSpeed<0){ //set right direction  backwards
		digitalWrite(MOTOR_RIGHT1_PIN, LOW);
		digitalWrite(MOTOR_RIGHT2_PIN, HIGH);
	}else{ //front
		digitalWrite(MOTOR_RIGHT1_PIN, HIGH);
		digitalWrite(MOTOR_RIGHT2_PIN, LOW);
	}
		analogWrite(MOTOR_RIGHT_EN_PIN, abs(rightMotorSpeed));//write right speed
}
<commit_msg>go to cruise mode only if at least one encoder was attached<commit_after>/*
*	Car.cpp
*/
#include "Smartcar.h"
/* --- CAR --- */

const unsigned short Car::DEFAULT_PID_LOOP_INTERVAL = 80;
const unsigned short Car::STANDARD = 1;
const unsigned short Car::INVERTED = 0;
const float Car::DEFAULT_KP = 5.0;
const float Car::DEFAULT_KI = 0.0;
const float Car::DEFAULT_KD = 10.0;

const unsigned short BACKWARD = 0;
const unsigned short FORWARD = 1;
const unsigned short IDLE = 2;

const int IDLE_RAW_SPEED = 0;
const int MAX_FRONT_RAW_SPEED = 255;
const int MAX_BACK_RAW_SPEED = -255;
const float MAX_BACK_CRUISE_SPEED = -3.0;
const float MAX_FRONT_CRUISE_SPEED = 3.0;
const int STRAIGHT_WHEELS = 90;
const int STRAIGHT_ANGLE = 0;
const int MAX_RIGHT_DEGREES = 180;
const int MAX_LEFT_DEGREES = 0;
const float GO_CRUISE_SPEED = 1.3;
const int GO_RAW_SPEED = 200;
const int MAX_RIGHT_STEERING_ANGLE = MAX_RIGHT_DEGREES - STRAIGHT_WHEELS;
const int MAX_LEFT_STEERING_ANGLE = MAX_LEFT_DEGREES - STRAIGHT_WHEELS;

Car::Car(const unsigned short setup){
	_cruiseControl = false;
	_speed = IDLE_RAW_SPEED;
	_angle = STRAIGHT_WHEELS;
	_numOfEncoders = 0;
	_gyroAttached = false;
	if (setup == STANDARD){ //the default motor setup, where right is right
		MOTOR_LEFT1_PIN = 8;
		MOTOR_LEFT_EN_PIN = 9;
		MOTOR_LEFT2_PIN = 10;
		MOTOR_RIGHT_EN_PIN = 11;
		MOTOR_RIGHT1_PIN = 12;
		MOTOR_RIGHT2_PIN = 13;
	}else{ //the reversed motor setup, where right is left
		MOTOR_RIGHT1_PIN = 8;
		MOTOR_RIGHT_EN_PIN = 9;
		MOTOR_RIGHT2_PIN = 10;
		MOTOR_LEFT_EN_PIN = 11;
		MOTOR_LEFT1_PIN = 12;
		MOTOR_LEFT2_PIN = 13;
	}
}

void Car::begin(Gyroscope gyro){
	begin(0,0,gyro); //pass 0 as a value that indicates null
}

void Car::begin(Odometer encoder, Gyroscope gyro){
	begin(encoder,0,gyro); //pass 0 as a value that indicates null
}

void Car::begin(Odometer encoder1, Odometer encoder2, Gyroscope gyro){
	if (encoder1.isInstanciated()){
		_encoders[0] = encoder1;
		_numOfEncoders++;
	}
	if (encoder2.isInstanciated()){
		_encoders[1] = encoder2;
		_numOfEncoders++;
	}
	if (gyro.isInstanciated()) {
		_gyro = gyro;
		_gyroAttached = true;
	}
	for (uint8_t i = MOTOR_LEFT1_PIN; i <= MOTOR_RIGHT2_PIN; i++) {
		pinMode(i, OUTPUT); //declare pins as outputs
	}
	setSpeed(IDLE_RAW_SPEED);
	setAngle(STRAIGHT_ANGLE);
}

void Car::setSpeed(float newSpeed){
	if (cruiseControlEnabled()){
		if (_speed && (_speed != IDLE_RAW_SPEED) && (newSpeed * _speed) <= 0) stop(); //if the speeds are signed differently, stop the car and then set the new speed. Ignore this if the speed is already 0 and if speed is at the idle raw speed i.e. leftovers from non-cruise control mode (if IDLE_RAW_SPEED is not 0, it makes sense)
		_speed = constrain(newSpeed, MAX_BACK_CRUISE_SPEED, MAX_FRONT_CRUISE_SPEED);
	}else{
		_speed = constrain(int(newSpeed), MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED);
		setMotors(_speed, getAngle());
	}
}

void Car::updateMotors(){
	if (cruiseControlEnabled() && (millis() > _lastMotorUpdate + _pidLoopInterval)){
		if (_speed){ //if _speed is 0, we have already made sure the car is stopped. don't try to adjust if car is just drifting
			float measuredSpeed = getGroundSpeed(); //speed in cm/milliseconds
			measuredSpeed *= 10; //transform it into m/seconds, divide by 100 to turn cm into m and multiply by 1000, to turn ms to sec
			if (_speed < 0) measuredSpeed *= -1; //if we are going reverse, illustrate that in the value of measuredSpeed
			int controlledSpeed = motorPIDcontrol(_previousControlledSpeed, _speed, measuredSpeed);
			setMotors(controlledSpeed, getAngle());
			_previousControlledSpeed = controlledSpeed;
		}
		_lastMotorUpdate = millis();
	}
}

int Car::motorPIDcontrol(const int previousSpeed, const float targetSpeed, const float actualSpeed){
	float correction = 0;
	float error = targetSpeed - actualSpeed;
	_integratedError += error;
	correction = (_Kp * error) + (_Ki * _integratedError) + (_Kd * (error - _previousError));
	_previousError = error;
	return constrain(previousSpeed + int(correction), MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED);
}

float Car::getGroundSpeed(){
	unsigned long currentDistance = getEncoderDistance();
	unsigned long dX = currentDistance - _previousDistance;
	_previousDistance = currentDistance;
	unsigned long dT = millis() - _lastMotorUpdate;
	float velocity = float(dX)/ float(dT);
	return velocity;
}

void Car::setMotors(int rawSpeed, int angle){ //platform specific method
	if (rawSpeed < 0){
		setDirection(BACKWARD);
	}else if (rawSpeed > 0){
		setDirection(FORWARD);
	}else{
		setDirection(IDLE);
	}
	rawSpeed = abs(rawSpeed);
	float ratio = (STRAIGHT_WHEELS - abs(angle));
	ratio /= STRAIGHT_WHEELS;
	if (angle>0){ //going to the right
		analogWrite(MOTOR_LEFT_EN_PIN, rawSpeed);
		analogWrite(MOTOR_RIGHT_EN_PIN, int(rawSpeed * ratio));
	}else{ //going to the left or straight
		analogWrite(MOTOR_RIGHT_EN_PIN, rawSpeed);
		analogWrite(MOTOR_LEFT_EN_PIN, int(rawSpeed * ratio));
	}

}

void Car::setDirection(const unsigned short direction){ //platform specific method
	if (direction == BACKWARD){
		digitalWrite(MOTOR_RIGHT1_PIN, LOW);
		digitalWrite(MOTOR_RIGHT2_PIN, HIGH);
		digitalWrite(MOTOR_LEFT1_PIN, LOW);
		digitalWrite(MOTOR_LEFT2_PIN, HIGH);
	}else if (direction == FORWARD){
		digitalWrite(MOTOR_RIGHT1_PIN, HIGH);
		digitalWrite(MOTOR_RIGHT2_PIN, LOW);
		digitalWrite(MOTOR_LEFT1_PIN, HIGH);
		digitalWrite(MOTOR_LEFT2_PIN, LOW);
	}else{
		digitalWrite(MOTOR_RIGHT1_PIN, LOW);
		digitalWrite(MOTOR_RIGHT2_PIN, LOW);
		digitalWrite(MOTOR_LEFT1_PIN, LOW);
		digitalWrite(MOTOR_LEFT2_PIN, LOW);
	}
}

void Car::setAngle(int angle){ //platform specific method
	_angle = constrain(angle, MAX_LEFT_DEGREES - STRAIGHT_WHEELS, MAX_RIGHT_DEGREES - STRAIGHT_WHEELS); //constrain the value to the permitted valued the user is allowed to supply
	setMotors(getSpeed(), getAngle()); //pass the human-readable value that was just supplied
}

void Car::stop(){ //platform specific method
	if (getSpeed()>0){
		setMotors(-100, getAngle());
	}else if(getSpeed()<0){
		setMotors(100, getAngle());
	}
	delay(80); //go the opposite direction for a few milliseconds, to make sure we are stopped
	if (cruiseControlEnabled()){
		setMotors(0,getAngle()); //shut the engines down, we should be stopped by now
		enableCruiseControl(_Kp,_Ki,_Kd); //re-initialize the cruise control, se we get rid of previous error and pid output
		_speed = 0.0;
	}else{
		setSpeed(0); //no circular dependency between stop and setSpeed due to the cruiseControlEnabled switching between who uses what
	}
}

float Car::getSpeed(){
	if (cruiseControlEnabled()) return getGroundSpeed(); //return speed in meters per second
	return _speed; //return speed in PWM output
}

int Car::getAngle(){
	return _angle; //return what the user supplied
}

void Car::enableCruiseControl(float Kp, float Ki, float Kd, unsigned short pidLoopInterval){
	if (!_numOfEncoders) return; //continue only if at least one encoder was attached
	_cruiseControl = true;
	_pidLoopInterval = pidLoopInterval;
	_lastMotorUpdate = 0;
	_previousControlledSpeed = IDLE_RAW_SPEED;
	_previousDistance = getEncoderDistance();
	_integratedError = 0;
	_Kp = Kp;
	_Ki = Ki;
	_Kd = Kd;
}

void Car::disableCruiseControl(){
	_cruiseControl = false;
	_speed = _previousControlledSpeed; //update the speed with the PWM equivalent
}

boolean Car::cruiseControlEnabled(){
	return _cruiseControl;
}

unsigned long Car::getEncoderDistance(){
	unsigned long averageDistance = 0;
	for (int i = 0; i < _numOfEncoders; i++){
		averageDistance += _encoders[i].getDistance() / _numOfEncoders;
	}
	return averageDistance;
}

void Car::initializeEncoders(){
	for (int i = 0; i < _numOfEncoders; i++){
		_encoders[i].begin();
	}
}

void Car::go(int centimeters){
	if (!_numOfEncoders) return; //continue only if at least one encoder was attached
	unsigned long initialDistance = getEncoderDistance(); //save the current distance the car has covered
	float initialSpeed = getSpeed(); //save the current speed (so we restore it later)
	int initialAngle = getAngle(); //save the current angle (so we restore it later)
	unsigned long targetDistance = initialDistance + abs(centimeters); //how much the car should move, always a positive number
	if (initialDistance > targetDistance){ //this will occur only if the unsigned long variable overflows
		initializeEncoders(); //reinitialize the encoders counter to 0
		targetDistance = abs(centimeters); //now the new target is just the centimeters (since the encoder distance is 0)
	}
	setAngle(STRAIGHT_ANGLE); //set the angle straight
	short direction = 1;
	if (centimeters < 0) direction = -1; //if the user supplied a negative argument, means they want to move backwards
	if (cruiseControlEnabled()){ //depending on whether the cruise control is enabled set the speed
		setSpeed(direction * GO_CRUISE_SPEED);
	}else{
		setSpeed(direction * GO_RAW_SPEED);
	}
	while (getEncoderDistance() < targetDistance){ //while we haven't reached the target distance, keep moving
		if (cruiseControlEnabled()) updateMotors(); //otherwise the pid for the motors won't work
	}
	setSpeed(initialSpeed); //restore to the initial speed
	setAngle(initialAngle); //restore to the inital angle
}
void Car::rotate(int targetDegrees){
	if (!_gyroAttached) return; //continue only if gyroscope is attached
	_gyro.begin(); //initialize the gyro
	float initialSpeed = getSpeed(); //save the current speed (so we restore it later)
	int initialAngle = getAngle(); //save the current angle (so we restore it later)
	if (targetDegrees > 0){ //we should turn to the right (clockwise)
		setAngle(MAX_RIGHT_STEERING_ANGLE); //turn right as steep as you can
	}else{ //turn left (counterclockwise)
		setAngle(MAX_LEFT_STEERING_ANGLE); //turn left as steep as you can
	}
	if (cruiseControlEnabled()){ //depending on whether the cruise control is enabled set the speed
		setSpeed(GO_CRUISE_SPEED);
	}else{
		setSpeed(GO_RAW_SPEED);
	}
	while (abs(_gyro.getAngularDisplacement()) < abs(targetDegrees)){ //while the absolute displacement hasn't reached the (absolute) target, keep turning
		if (cruiseControlEnabled()) updateMotors(); //otherwise the pid for the motors won't work
		_gyro.update(); //update to integrate the latest gyroscope readings
	}
	setSpeed(initialSpeed); //restore to the initial speed
	setAngle(initialAngle); //restore to the inital angle
}

void Car::setMotorSpeed(int leftMotorSpeed, int rightMotorSpeed){ //will set the speed manual to each motor, use with caution
	if (cruiseControlEnabled()) return; //we don't want to be manually messing with the speed, during cruise control
	leftMotorSpeed = constrain(leftMotorSpeed, MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED); //constrain the speed within the allowed limits
	rightMotorSpeed = constrain(rightMotorSpeed, MAX_BACK_RAW_SPEED, MAX_FRONT_RAW_SPEED);
	if (leftMotorSpeed<0){ //set left direction backwards
		digitalWrite(MOTOR_LEFT1_PIN, LOW);
		digitalWrite(MOTOR_LEFT2_PIN, HIGH);
	}else{ //front
		digitalWrite(MOTOR_LEFT1_PIN, HIGH);
		digitalWrite(MOTOR_LEFT2_PIN, LOW);
	}
		analogWrite(MOTOR_LEFT_EN_PIN, abs(leftMotorSpeed));//write left speed
	if (rightMotorSpeed<0){ //set right direction  backwards
		digitalWrite(MOTOR_RIGHT1_PIN, LOW);
		digitalWrite(MOTOR_RIGHT2_PIN, HIGH);
	}else{ //front
		digitalWrite(MOTOR_RIGHT1_PIN, HIGH);
		digitalWrite(MOTOR_RIGHT2_PIN, LOW);
	}
		analogWrite(MOTOR_RIGHT_EN_PIN, abs(rightMotorSpeed));//write right speed
}
<|endoftext|>