<commit_before>#include <exception>
#include <iostream>
#include <vector>

#include "execution_step.h"
#include "parser.h"

#define STATE_INIT 0
#define STATE_PIPE 1

std::vector<ExecutionStep*> parse_tokens(std::vector<std::string> tokens) throw (std::string) {

  std::vector<ExecutionStep*> ret;

  ExecutionStep* head = NULL;
  ExecutionStep* prev = NULL;
  int executionState = STATE_INIT;

  std::vector<std::string> execTokens;

  for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); it++) {
    std::string token = *it;

    if (token == ";" || token == "\n") {

      if (execTokens.size() == 0) {
        continue;
      }

      ExecutionStep* end = new ExecutionStep(new Program(execTokens));
      if (executionState == STATE_INIT) {
        head = end;
      } else if (executionState == STATE_PIPE) {
        prev->setPipe(end);
      }
      ret.push_back(head);
      executionState = STATE_INIT;
      execTokens = std::vector<std::string>();

    } else if (token == "|") {

      ExecutionStep* step = new ExecutionStep(new Program(execTokens));
      if (executionState == STATE_INIT) {
        head = step;
      }
      if (head == NULL) {
        throw std::string("Invalid pipe -- no step before specified.");
      }
      if (executionState == STATE_PIPE) {
        prev->setPipe(step);
      }
      prev = step;
      execTokens = std::vector<std::string>();
      executionState = STATE_PIPE;

    } else if (token == ">" || token == ">>") {

      if (execTokens.size() == 0) {
        throw std::string("No tokens.");
      }
      ExecutionStep* step = new ExecutionStep(new Program(execTokens));
      if (executionState == STATE_INIT) {
        head = step;
      }
      if (head == NULL) {
        throw std::string("Invalid out redirect -- no step before specified.");
      }
      if (executionState == STATE_PIPE) {
        prev->setPipe(step);
      }
      it++;
      step->setOutfile(*it);
      if (token == ">>") {
        step->setOutappend(true);
      }
      execTokens = std::vector<std::string>();
      prev = step;
      executionState = STATE_PIPE;

    } else {

      execTokens.push_back(token);

    }
  }

  if (execTokens.size() > 0) {
    ExecutionStep* end = new ExecutionStep(new Program(execTokens));
    if (executionState == STATE_INIT) {
      head = end;
    } else if (executionState == STATE_PIPE) {
      prev->setPipe(end);
    }
  }
  ret.push_back(head);

  return ret;
}
<commit_msg>remove executionState<commit_after>#include <exception>
#include <iostream>
#include <vector>

#include "execution_step.h"
#include "parser.h"

std::vector<ExecutionStep*> parse_tokens(std::vector<std::string> tokens) throw (std::string) {

  std::vector<ExecutionStep*> ret;

  ExecutionStep* head = NULL;
  ExecutionStep* prev = NULL;
  ExecutionStep* cur = NULL;
  std::vector<std::string> execTokens;

  for (std::vector<std::string>::iterator it = tokens.begin(); it != tokens.end(); it++) {
    std::string token = *it;

    if (token == ";" || token == "\n") {

      if (execTokens.size() != 0) {
        cur = new ExecutionStep(new Program(execTokens));
        if (prev != NULL) {
          prev->setPipe(cur);
        } else {
          head = cur;
        }
      }

      ret.push_back(head);
      execTokens = std::vector<std::string>();

    } else if (token == "|") {

      if (cur == NULL) {
        if (execTokens.size() == 0) {
          throw std::string("No tokens.");
        }
        cur = new ExecutionStep(new Program(execTokens));
      }

      if (prev != NULL) {
        prev->setPipe(cur);
      } else {
        head = cur;
      }
      prev = cur;
      cur = NULL;
      execTokens = std::vector<std::string>();

    } else if (token == ">" || token == ">>") {

      if (cur == NULL) {
        if (execTokens.size() == 0) {
          throw std::string("No tokens.");
        }
        cur = new ExecutionStep(new Program(execTokens));
      }

      it++;
      cur->setOutfile(*it);
      if (token == ">>") {
        cur->setOutappend(true);
      }
      execTokens = std::vector<std::string>();

    } else {

      execTokens.push_back(token);

    }
  }

  if (cur != NULL) {
    if (prev != NULL) {
      prev->setPipe(cur);
    } else {
      head = cur;
    }
    cur = NULL;
  }

  if (execTokens.size() > 0 && cur == NULL) {
    cur = new ExecutionStep(new Program(execTokens));
    if (prev != NULL) {
      prev->setPipe(cur);
    } else {
      head = cur;
    }
  }
  ret.push_back(head);

  return ret;
}
<|endoftext|>


<commit_before>#include "parser.h"

using namespace std;

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque);
static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque);
static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque);
static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque);
static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque);
static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_linebreak(struct buf *ob, void *opaque);
static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque);
static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque);

struct mkd_renderer mkd_callbacks = {
	/* document-level callbacks */
	NULL,                 // prolog
	NULL,                 // epilogue

	/* block-level callbacks */
	rndr_blockcode,       // block code
	rndr_blockquote,      // block quote
	NULL,                 // block html
	rndr_header,          // header
	NULL,                 // hrule
	rndr_list,            // list
	rndr_listitem,        // listitem
	rndr_paragraph,       // paragraph
	NULL,                 // table
	NULL,                 // table cell
	NULL,                 // table row

	/* span-level callbacks */
	NULL,                 // autolink
	rndr_codespan,        // codespan
	rndr_double_emphasis, // double emphasis
	rndr_emphasis,        // emphasis
	NULL,                 // image
	rndr_linebreak,       // line break
	rndr_link,            // link
	NULL,                 // raw html tag
	rndr_triple_emphasis, // triple emphasis

	/* low-level callbacks */
	NULL,                 // entity
	rndr_normal_text,     // normal text

	/* renderer data */
	64, // max stack
	"*_",
	NULL // opaque
};

namespace Bypass {

	const static std::string TWO_SPACES = "  ";

	Parser::Parser()
	: elementSoup()
	{
		elementCount = 1;
	}

	Parser::~Parser() {

	}

	Document Parser::parse(const char* mkd) {
		document = Document();

		if (mkd) {
			struct buf *ib, *ob;

			ib = bufnew(INPUT_UNIT);
			bufputs(ib, mkd);

			ob = bufnew(OUTPUT_UNIT);

			mkd_callbacks.opaque = this;

			//parse and assemble document
			markdown(ob, ib, &mkd_callbacks);

			for (boost::unordered_map<std::string, Element>::iterator it = elementSoup.begin(); it != elementSoup.end(); ++it) {
				document.append(it->second);
			}

			bufrelease(ib);
			bufrelease(ob);
		}

		return document;
	}

	Document Parser::parse(const string& markdown) {
		return parse(markdown.c_str());
	}

	// Block Element Callbacks

	void Parser::handleBlock(Type type, struct buf *ob, struct buf *text) {
		Element block;
		block.setType(type);

		std::string textString(text->data);
		textString = textString.substr(0,text->size);
		std::vector<std::string> strs;
		boost::split(strs, textString, boost::is_any_of("|"));

		for(vector<std::string>::iterator it = strs.begin(); it != strs.end(); it++) {
			if (elementSoup.count(*it) > 0) {
				block.append(elementSoup.at(*it));
				elementSoup.erase(*it);
			}
		}

		elementCount++;

		std::ostringstream oss;
		oss << elementCount;
		elementSoup[oss.str()] = block;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	void Parser::parsedBlockcode(struct buf *ob, struct buf *text) {

	}

	void Parser::parsedBlockquote(struct buf *ob, struct buf *text) {

	}

	void Parser::parsedHeader(struct buf *ob, struct buf *text, int level) {

	}

	void Parser::parsedList(struct buf *ob, struct buf *text, int flags) {

	}

	void Parser::parsedListItem(struct buf *ob, struct buf *text, int flags) {

	}

	void Parser::parsedParagraph(struct buf *ob, struct buf *text) {
		handleBlock(PARAGRAPH, ob, text);
	}

	// Span Element Callbacks

	void Parser::handleSpan(Type type, struct buf *ob, struct buf *text, struct buf *extra) {

		std::string textString(text->data);
		textString = textString.substr(0, text->size);
		std::vector<std::string> strs;
		if (text)
			boost::split(strs, textString, boost::is_any_of("|"));
		if (strs.size() > 0) {
			Element element = elementSoup.at(strs[0]);
			element.setType(type);
			elementSoup.erase(strs[0]);
			elementSoup[strs[0]] = element;

			bufputs(ob, textString.c_str());
		}
		else {
			Element element;
			element.setType(type);

			createSpan(element, ob);
		}
	}

	void Parser::createSpan(Element element, struct buf *ob) {
		elementCount++;
		std::ostringstream oss;
		oss << elementCount;
		elementSoup[oss.str()] = element;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	int Parser::parsedDoubleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(DOUBLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedTripleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(TRIPLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedLink(struct buf *ob, struct buf *link, struct buf *title, struct buf *content) {
		handleSpan(LINK, ob, content, link);
		return 1;
	}

	int Parser::parsedCodeSpan(struct buf *ob, struct buf *text) {
		if (text && text->size > 0) {
			Element codeSpan;
			codeSpan.setType(CODE_SPAN);
			codeSpan.setText(std::string(text->data).substr(0, text->size));
			pendingSpanElements.push_back(codeSpan);
		}

		return 1;
	}

	int Parser::parsedLinebreak(struct buf *ob) {
		handleSpan(LINEBREAK, ob, NULL);
		return 1;
	}

	// Low Level Callbacks

	void Parser::parsedNormalText(struct buf *ob, struct buf *text) {
		// The parser will spuriously emit a text callback for an empty string
		// that butts up against a span-level element. This will ignore it.

		if (text && text->size > 0) {
			Element normalText;
			normalText.setType(TEXT);
			normalText.setText(std::string(text->data).substr(0, text->size));
			createSpan(normalText, ob);
		}
	}

}

// Block Element callbacks

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockcode(ob, text);
}

static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockquote(ob, text);
}

static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque) {
	((Bypass::Parser*) opaque)->parsedHeader(ob, text, level);
}

static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedList(ob, text, flags);
}

static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedListItem(ob, text, flags);
}

static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedParagraph(ob, text);
}

// Span Element callbacks

static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedCodeSpan(ob, text);
}

static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedDoubleEmphasis(ob, text, c);
}

static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedEmphasis(ob, text, c);
}

static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedTripleEmphasis(ob, text, c);
}

static int rndr_linebreak(struct buf *ob, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLinebreak(ob);
}

static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLink(ob, link, title, content);
}

//	Low Level Callbacks

static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedNormalText(ob, text);
}

<commit_msg>Process more block elements<commit_after>#include "parser.h"

using namespace std;

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque);
static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque);
static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque);
static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque);
static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque);
static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque);
static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque);
static int rndr_linebreak(struct buf *ob, void *opaque);
static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque);
static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque);

struct mkd_renderer mkd_callbacks = {
	/* document-level callbacks */
	NULL,                 // prolog
	NULL,                 // epilogue

	/* block-level callbacks */
	rndr_blockcode,       // block code
	rndr_blockquote,      // block quote
	NULL,                 // block html
	rndr_header,          // header
	NULL,                 // hrule
	rndr_list,            // list
	rndr_listitem,        // listitem
	rndr_paragraph,       // paragraph
	NULL,                 // table
	NULL,                 // table cell
	NULL,                 // table row

	/* span-level callbacks */
	NULL,                 // autolink
	rndr_codespan,        // codespan
	rndr_double_emphasis, // double emphasis
	rndr_emphasis,        // emphasis
	NULL,                 // image
	rndr_linebreak,       // line break
	rndr_link,            // link
	NULL,                 // raw html tag
	rndr_triple_emphasis, // triple emphasis

	/* low-level callbacks */
	NULL,                 // entity
	rndr_normal_text,     // normal text

	/* renderer data */
	64, // max stack
	"*_",
	NULL // opaque
};

namespace Bypass {

	const static std::string TWO_SPACES = "  ";

	Parser::Parser()
	: elementSoup()
	{
		elementCount = 1;
	}

	Parser::~Parser() {

	}

	Document Parser::parse(const char* mkd) {
		document = Document();

		if (mkd) {
			struct buf *ib, *ob;

			ib = bufnew(INPUT_UNIT);
			bufputs(ib, mkd);

			ob = bufnew(OUTPUT_UNIT);

			mkd_callbacks.opaque = this;

			//parse and assemble document
			markdown(ob, ib, &mkd_callbacks);

			for (boost::unordered_map<std::string, Element>::iterator it = elementSoup.begin(); it != elementSoup.end(); ++it) {
				document.append(it->second);
			}

			bufrelease(ib);
			bufrelease(ob);
		}

		return document;
	}

	Document Parser::parse(const string& markdown) {
		return parse(markdown.c_str());
	}

	// Block Element Callbacks

	void Parser::handleBlock(Type type, struct buf *ob, struct buf *text) {
		Element block;
		block.setType(type);

		std::string textString(text->data);
		textString = textString.substr(0,text->size);
		std::vector<std::string> strs;
		boost::split(strs, textString, boost::is_any_of("|"));

		for(vector<std::string>::iterator it = strs.begin(); it != strs.end(); it++) {
			if (elementSoup.count(*it) > 0) {
				block.append(elementSoup.at(*it));
				elementSoup.erase(*it);
			}
		}

		elementCount++;

		std::ostringstream oss;
		oss << elementCount;
		elementSoup[oss.str()] = block;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	void Parser::parsedBlockcode(struct buf *ob, struct buf *text) {
		handleBlock(BLOCK_CODE, ob, text);
	}

	void Parser::parsedBlockquote(struct buf *ob, struct buf *text) {
		handleBlock(BLOCK_QUOTE, ob, text);
	}

	void Parser::parsedHeader(struct buf *ob, struct buf *text, int level) {
		handleBlock(HEADER, ob, text);
	}

	void Parser::parsedList(struct buf *ob, struct buf *text, int flags) {
		handleBlock(LIST, ob, text);
	}

	void Parser::parsedListItem(struct buf *ob, struct buf *text, int flags) {
		handleBlock(LIST_ITEM, ob, text);
	}

	void Parser::parsedParagraph(struct buf *ob, struct buf *text) {
		handleBlock(PARAGRAPH, ob, text);
	}

	// Span Element Callbacks

	void Parser::handleSpan(Type type, struct buf *ob, struct buf *text, struct buf *extra) {

		std::string textString(text->data);
		textString = textString.substr(0, text->size);
		std::vector<std::string> strs;
		if (text)
			boost::split(strs, textString, boost::is_any_of("|"));
		if (strs.size() > 0) {
			Element element = elementSoup.at(strs[0]);
			element.setType(type);
			elementSoup.erase(strs[0]);
			elementSoup[strs[0]] = element;

			bufputs(ob, textString.c_str());
		}
		else {
			Element element;
			element.setType(type);

			createSpan(element, ob);
		}
	}

	void Parser::createSpan(Element element, struct buf *ob) {
		elementCount++;
		std::ostringstream oss;
		oss << elementCount;
		elementSoup[oss.str()] = element;
		oss << '|';
		bufputs(ob, oss.str().c_str());
	}

	int Parser::parsedDoubleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(DOUBLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedTripleEmphasis(struct buf *ob, struct buf *text, char c) {
		handleSpan(TRIPLE_EMPHASIS, ob, text);
		return 1;
	}

	int Parser::parsedLink(struct buf *ob, struct buf *link, struct buf *title, struct buf *content) {
		handleSpan(LINK, ob, content, link);
		return 1;
	}

	int Parser::parsedCodeSpan(struct buf *ob, struct buf *text) {
		handleBlock(CODE_SPAN, ob, text);
		return 1;
	}

	int Parser::parsedLinebreak(struct buf *ob) {
		handleSpan(LINEBREAK, ob, NULL);
		return 1;
	}

	// Low Level Callbacks

	void Parser::parsedNormalText(struct buf *ob, struct buf *text) {
		// The parser will spuriously emit a text callback for an empty string
		// that butts up against a span-level element. This will ignore it.

		if (text && text->size > 0) {
			Element normalText;
			normalText.setType(TEXT);
			normalText.setText(std::string(text->data).substr(0, text->size));
			createSpan(normalText, ob);
		}
	}

}

// Block Element callbacks

static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockcode(ob, text);
}

static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedBlockquote(ob, text);
}

static void rndr_header(struct buf *ob, struct buf *text, int level, void *opaque) {
	((Bypass::Parser*) opaque)->parsedHeader(ob, text, level);
}

static void rndr_list(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedList(ob, text, flags);
}

static void rndr_listitem(struct buf *ob, struct buf *text, int flags, void *opaque) {
	((Bypass::Parser*) opaque)->parsedListItem(ob, text, flags);
}

static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) {
	((Bypass::Parser*) opaque)->parsedParagraph(ob, text);
}

// Span Element callbacks

static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedCodeSpan(ob, text);
}

static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedDoubleEmphasis(ob, text, c);
}

static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedEmphasis(ob, text, c);
}

static int rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedTripleEmphasis(ob, text, c);
}

static int rndr_linebreak(struct buf *ob, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLinebreak(ob);
}

static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedLink(ob, link, title, content);
}

//	Low Level Callbacks

static void rndr_normal_text(struct buf *ob, struct buf *text, void *opaque) {
	return ((Bypass::Parser*) opaque)->parsedNormalText(ob, text);
}

<|endoftext|>


<commit_before>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <iostream>

#include "libtorrent/policy.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/peer_connection.hpp"

namespace
{
	enum
	{
		// we try to maintain 4 requested blocks in the download
		// queue
		request_queue = 16
	};

	using namespace libtorrent;

	// TODO: replace these two functions with std::find_first_of
	template<class It1, class It2>
	bool has_intersection(It1 start1, It1 end1, It2 start2, It2 end2)
	{
		for (;start1 != end1; ++start1)
			for (;start2 != end2; ++start2)
				if (*start1 == *start2) return true;
		return false;
	}

	piece_block find_first_common(const std::vector<piece_block>& queue,
		const std::vector<piece_block>& busy)
	{
		for (std::vector<piece_block>::const_reverse_iterator i
			= queue.rbegin();
			i != queue.rend();
			++i)
		{
			for (std::vector<piece_block>::const_iterator j
				= busy.begin();
				j != busy.end();
				++j)
			{
				if ((*j) == (*i)) return *i;
			}
		}
		assert(false);
	}

	void request_a_block(torrent& t, peer_connection& c)
	{
		int num_requests = request_queue - c.download_queue().size();

		// if our request queue is already full, we
		// don't have to make any new requests yet
		if (num_requests <= 0) return;

		piece_picker& p = t.picker();
		std::vector<piece_block> interesting_pieces;
		interesting_pieces.reserve(100);

		// picks the interesting pieces from this peer
		// the integer is the number of pieces that
		// should be guaranteed to be available for download
		// (if this number is too big, too many pieces are
		// picked and cpu-time is wasted)
		p.pick_pieces(c.get_bitfield(), interesting_pieces, num_requests);

		// this vector is filled with the interestin pieces
		// that some other peer is currently downloading
		// we should then compare this peer's download speed
		// with the other's, to see if we should abort another
		// peer_connection in favour of this one
		std::vector<piece_block> busy_pieces;
		busy_pieces.reserve(10);

		for (std::vector<piece_block>::iterator i = interesting_pieces.begin();
			i != interesting_pieces.end();
			++i)
		{
			if (p.is_downloading(*i))
			{
				busy_pieces.push_back(*i);
				continue;
			}

			// ok, we found a piece that's not being downloaded
			// by somebody else. request it from this peer
			c.request_block(*i);
			num_requests--;
			if (num_requests <= 0) return;
		}

		if (busy_pieces.empty()) return;

		// first look for blocks that are just queued
		// and not actually sent to us yet
		// (then we can cancel those and request them
		// from this peer instead)

		peer_connection* peer = 0;
		float down_speed = 0.f;
		// find the peer with the lowest download
		// speed that also has a piece thatt this
		// peer could send us
		for (torrent::peer_iterator i = t.begin();
			i != t.end();
			++i)
		{
			const std::vector<piece_block>& queue = (*i)->download_queue();
			if ((*i)->statistics().down_peak() > down_speed
				&& has_intersection(busy_pieces.begin(),
					busy_pieces.end(),
					queue.begin(),
					queue.end()))
			{
				peer = *i;
				down_speed = (*i)->statistics().down_peak();
			}
		}

		assert(peer != 0);

		// this peer doesn't have a faster connection than the
		// slowest peer. Don't take over any blocks
		if (c.statistics().down_peak() <= down_speed) return;

		// find a suitable block to take over from this peer
		piece_block block = find_first_common(peer->download_queue(), busy_pieces);
		peer->cancel_block(block);
		c.request_block(block);

		// the one we interrupted may need to request a new piece
		request_a_block(t, *peer);

		num_requests--;
	}
}

namespace libtorrent
{
/*
	TODO: make two proxy classes that filter out
	all unneccesary members from torrent and peer_connection
	to make it easier to use them in the policy

	useful member functions:

	void torrent::connect_to_peer(address, peer_id);
	piece_picker& torrent::picker();
	std::vector<peer_connection*>::const_iterator torrent::begin() const
	std::vector<peer_connection*>::const_iterator torrent::end() const

	void peer_connection::interested();
	void peer_connection::not_interested();
	void peer_connection::choke();
	void peer_connection::unchoke();
	void peer_connection::request_piece(int index);
	const std::vector<int>& peer_connection::download_queue();
*/

	policy::policy(torrent* t)
		: m_num_peers(0)
		, m_torrent(t)
	{}

	// this is called when a connection is made, before any
	// handshake (it's possible to ban certain ip:s).
	bool policy::accept_connection(const address& remote)
	{
		m_num_peers++;
		return true;
	}

	void policy::peer_from_tracker(const address& remote, const peer_id& id)
	{
		try
		{
			m_torrent->connect_to_peer(remote, id);
			m_num_peers++;
		}
		catch(network_error&) {}
	}

	// this is called when we are choked by a peer
	// i.e. a peer lets us know that we will not receive
	// anything for a while
	void policy::choked(peer_connection& c)
	{
		c.choke();
	}

	void policy::piece_finished(peer_connection& c, int index, bool successfully_verified)
	{
		// TODO: if verification failed, mark the peers that were involved
		// in some way
	}

	void policy::block_finished(peer_connection& c, piece_block b)
	{
		if (c.has_peer_choked()) return;
		request_a_block(*m_torrent, c);
	}

	// this is called when we are unchoked by a peer
	// i.e. a peer lets us know that we will receive
	// data from now on
	void policy::unchoked(peer_connection& c)
	{
		c.unchoke();
		if (c.is_interesting()) request_a_block(*m_torrent, c);
	}

	void policy::interested(peer_connection& c)
	{
		c.unchoke();
	}

	void policy::not_interested(peer_connection& c)
	{
	}

	void policy::connection_closed(const peer_connection& c)
	{
	}

	void policy::peer_is_interesting(peer_connection& c)
	{
		c.interested();
		if (c.has_peer_choked()) return;
		request_a_block(*m_torrent, c);
	}
}
<commit_msg>bugfix<commit_after>/*

Copyright (c) 2003, Arvid Norberg
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in
      the documentation and/or other materials provided with the distribution.
    * Neither the name of the author nor the names of its
      contributors may be used to endorse or promote products derived
      from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

*/

#include <iostream>

#include "libtorrent/policy.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/peer_connection.hpp"

namespace
{
	enum
	{
		// we try to maintain 4 requested blocks in the download
		// queue
		request_queue = 16
	};

	using namespace libtorrent;

	// TODO: replace these two functions with std::find_first_of
	template<class It1, class It2>
	bool has_intersection(It1 start1, It1 end1, It2 start2, It2 end2)
	{
		for (;start1 != end1; ++start1)
			for (;start2 != end2; ++start2)
				if (*start1 == *start2) return true;
		return false;
	}

	piece_block find_first_common(const std::vector<piece_block>& queue,
		const std::vector<piece_block>& busy)
	{
		for (std::vector<piece_block>::const_reverse_iterator i
			= queue.rbegin();
			i != queue.rend();
			++i)
		{
			for (std::vector<piece_block>::const_iterator j
				= busy.begin();
				j != busy.end();
				++j)
			{
				if ((*j) == (*i)) return *i;
			}
		}
		assert(false);
	}

	void request_a_block(torrent& t, peer_connection& c)
	{
		int num_requests = request_queue - c.download_queue().size();

		// if our request queue is already full, we
		// don't have to make any new requests yet
		if (num_requests <= 0) return;

		piece_picker& p = t.picker();
		std::vector<piece_block> interesting_pieces;
		interesting_pieces.reserve(100);

		// picks the interesting pieces from this peer
		// the integer is the number of pieces that
		// should be guaranteed to be available for download
		// (if this number is too big, too many pieces are
		// picked and cpu-time is wasted)
		p.pick_pieces(c.get_bitfield(), interesting_pieces, num_requests);

		// this vector is filled with the interestin pieces
		// that some other peer is currently downloading
		// we should then compare this peer's download speed
		// with the other's, to see if we should abort another
		// peer_connection in favour of this one
		std::vector<piece_block> busy_pieces;
		busy_pieces.reserve(10);

		for (std::vector<piece_block>::iterator i = interesting_pieces.begin();
			i != interesting_pieces.end();
			++i)
		{
			if (p.is_downloading(*i))
			{
				busy_pieces.push_back(*i);
				continue;
			}

			// ok, we found a piece that's not being downloaded
			// by somebody else. request it from this peer
			c.request_block(*i);
			num_requests--;
			if (num_requests <= 0) return;
		}

		if (busy_pieces.empty()) return;

		// first look for blocks that are just queued
		// and not actually sent to us yet
		// (then we can cancel those and request them
		// from this peer instead)

		peer_connection* peer = 0;
		float down_speed = -1.f;
		// find the peer with the lowest download
		// speed that also has a piece thatt this
		// peer could send us
		for (torrent::peer_iterator i = t.begin();
			i != t.end();
			++i)
		{
			const std::vector<piece_block>& queue = (*i)->download_queue();
			if ((*i)->statistics().down_peak() > down_speed
				&& has_intersection(busy_pieces.begin(),
					busy_pieces.end(),
					queue.begin(),
					queue.end()))
			{
				peer = *i;
				down_speed = (*i)->statistics().down_peak();
			}
		}

		assert(peer != 0);

		// this peer doesn't have a faster connection than the
		// slowest peer. Don't take over any blocks
		if (c.statistics().down_peak() <= down_speed) return;

		// find a suitable block to take over from this peer
		piece_block block = find_first_common(peer->download_queue(), busy_pieces);
		peer->cancel_block(block);
		c.request_block(block);

		// the one we interrupted may need to request a new piece
		request_a_block(t, *peer);

		num_requests--;
	}
}

namespace libtorrent
{
/*
	TODO: make two proxy classes that filter out
	all unneccesary members from torrent and peer_connection
	to make it easier to use them in the policy

	useful member functions:

	void torrent::connect_to_peer(address, peer_id);
	piece_picker& torrent::picker();
	std::vector<peer_connection*>::const_iterator torrent::begin() const
	std::vector<peer_connection*>::const_iterator torrent::end() const

	void peer_connection::interested();
	void peer_connection::not_interested();
	void peer_connection::choke();
	void peer_connection::unchoke();
	void peer_connection::request_piece(int index);
	const std::vector<int>& peer_connection::download_queue();
*/

	policy::policy(torrent* t)
		: m_num_peers(0)
		, m_torrent(t)
	{}

	// this is called when a connection is made, before any
	// handshake (it's possible to ban certain ip:s).
	bool policy::accept_connection(const address& remote)
	{
		m_num_peers++;
		return true;
	}

	void policy::peer_from_tracker(const address& remote, const peer_id& id)
	{
		try
		{
			m_torrent->connect_to_peer(remote, id);
			m_num_peers++;
		}
		catch(network_error&) {}
	}

	// this is called when we are choked by a peer
	// i.e. a peer lets us know that we will not receive
	// anything for a while
	void policy::choked(peer_connection& c)
	{
		c.choke();
	}

	void policy::piece_finished(peer_connection& c, int index, bool successfully_verified)
	{
		// TODO: if verification failed, mark the peers that were involved
		// in some way
	}

	void policy::block_finished(peer_connection& c, piece_block b)
	{
		if (c.has_peer_choked()) return;
		request_a_block(*m_torrent, c);
	}

	// this is called when we are unchoked by a peer
	// i.e. a peer lets us know that we will receive
	// data from now on
	void policy::unchoked(peer_connection& c)
	{
		c.unchoke();
		if (c.is_interesting()) request_a_block(*m_torrent, c);
	}

	void policy::interested(peer_connection& c)
	{
		c.unchoke();
	}

	void policy::not_interested(peer_connection& c)
	{
	}

	void policy::connection_closed(const peer_connection& c)
	{
	}

	void policy::peer_is_interesting(peer_connection& c)
	{
		c.interested();
		if (c.has_peer_choked()) return;
		request_a_block(*m_torrent, c);
	}
}
<|endoftext|>


<commit_before>// -*- mode: c++, encoding: utf-8 -*-
/**
 * tbrpg – Text based roll playing game
 *
 * Copyright © 2012  Mattias Andrée (maandree@kth.se)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdlib.h>
#include <iostream>
#include <vector>

#include "cleaner.hpp"
#include "prompter.hpp"
#include "Senario.hpp"
#include "CharacterCreator.hpp"
#include "GamePlay.hpp"

#include "BasicSenario.hpp"

/**
 * Text based roll playing game
 *
 * DD2387 Program construction with C++
 * Laboration 3
 *
 * @author  Mattias Andrée <maandree@kth.se>
 */
namespace tbrpg
{
  /**
   * Create a party
   *
   * @param   senario  The game senario
   * @param   sheets   Solts for the character sheets of the party
   * @return           Whether the party creation was completed
   */
  bool createParty(const Senario& senario, std::vector<CharacterSheet*>* sheets);

  /**
   * Fixes character attributes and add them to the party member list
   *
   * @param  senario  The game senario
   * @param  sheets   The character sheets of the party
   */
  void fixCharacters(Senario& senario, std::vector<CharacterSheet*>* sheets);

  /**
   * This the main entry point of the program
   *
   * @param   argc  The number of elements in `argv`
   * @param   argv  Command line arguments, including the execute file
   * @return        Exit value, 0 if successful
   */
  int __main__(int argc, char** argv)
  {
    (void) argc;
    (void) argv;

    /** BEGIN initialise random **/
    unsigned a, d;
    asm("cpuid");
    asm volatile("rdtsc" : "=a" (a), "=d" (d));
    srand(((long long)a) | (((long long)d) << 32LL));
    /** END initialise random **/

    std::vector<std::string> senarioTitles = {BasicSenario::getTitle()};

    long senarioIndex = promptDialogue(4, "Welcome to tbrpg!",
		 "Select a game senario."                        "\n"
		                                                 "\n"
		 "Use the up and down arrow keys to nagivate,"   "\n"
		 "and press enter, spacebar or the right arrow"  "\n"
		 "key to start the select senario. You can"      "\n"
		 "also use the letter in front of the senario."  "\n"
		                                                 "\n"
		 "You should play with the manual by your side," "\n"
		 "run `info tbrpg` or open a graphical if you"   "\n"
		 "have one installed, jfbview can view them in"  "\n"
		 "the TTY."                                      "\n"
		                                                 "\n"
		 "If you want to exit, press <control>g."        "\n",
		 senarioTitles, 0);

    Senario* _senario = nullptr;
    if (senarioIndex == 0)
      _senario = new BasicSenario();
    if (_senario == nullptr)
      {
	cleaner::getInstance().clean();
	return 0;
      }

    cleaner::getInstance().enqueueDelete(_senario);
    Senario& senario = *_senario;
    std::vector<CharacterSheet*>* sheets = new std::vector<CharacterSheet*>(senario.rules.party_start_size);

    if (createParty(senario, sheets) == false)
      {
	delete sheets;
        cleaner::getInstance().clean();
	return 0;
      }

    fixCharacters(senario, sheets);
    delete sheets;
    senario.partyFormed();
    senario.start();

    GamePlay game = GamePlay(senario);
    while (game.next())
      ;

    cleaner::getInstance().clean();
    return 0;
  }

  /**
   * Create a party
   *
   * @param   senario  The game senario
   * @param   sheets   Solts for the character sheets of the party
   * @return           Whether the party creation was completed
   */
  bool createParty(const Senario& senario, std::vector<CharacterSheet*>* sheets)
  {
    return promptSlots<CharacterSheet>("Create your party, you need at least one character.",
				       true, 1, sheets,
				       [&] () -> CharacterSheet* {
					 CharacterCreator cc = CharacterCreator(senario.rules);
					 return cc.create();
				       },
				       [] (CharacterSheet* sheet, bool selected) -> void {
					 if (sheet == nullptr)
					   std::cout << (selected ? "\033[01m(empty)\033[21m" : "(empty)");
					 else
					   {
					     std::cout << (selected ? "\033[01;3" : "\033[3") << (int)(sheet->colour) << "m";
					     std::cout << sheet->name << "\033[21;39m";
					   }
				       });
  }

  /**
   * Fixes character attributes and add them to the party member list
   *
   * @param  senario  The game senario
   * @param  sheets   The character sheets of the party
   */
  void fixCharacters(Senario& senario, std::vector<CharacterSheet*>* sheets)
  {
    for (CharacterSheet* sheet : *sheets)
      if (sheet != nullptr)
	{
	  Character* character = new Character();
	  character->record = *sheet;
	  delete sheet;
	  character->protagonist = true;
	  character->hit_points = character->record.hit_points;

	  for (size_t i = 0, n = character->record.prestige.size(); i < n; i++)
	    if (character->record.prestige[i] >= PROTOTYPE(Warrior))
	      if (character->record.level[i] >= 7)
		character->extra_attacks++;

	  cleaner::getInstance().enqueueDelete(character);
	  senario.party.characters.push_back(character);
	}
  }

}

/**
 * This the main entry point of the program
 *
 * @param   argc  The number of elements in `argv`
 * @param   argv  Command line arguments, including the execute file
 * @return        Exit value, 0 if successful
 */
int main(int argc, char** argv)
{
  return tbrpg::__main__(argc, argv);
}

<commit_msg>extra_attacks should be half attacks<commit_after>// -*- mode: c++, encoding: utf-8 -*-
/**
 * tbrpg – Text based roll playing game
 *
 * Copyright © 2012  Mattias Andrée (maandree@kth.se)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdlib.h>
#include <iostream>
#include <vector>

#include "cleaner.hpp"
#include "prompter.hpp"
#include "Senario.hpp"
#include "CharacterCreator.hpp"
#include "GamePlay.hpp"

#include "BasicSenario.hpp"

/**
 * Text based roll playing game
 *
 * DD2387 Program construction with C++
 * Laboration 3
 *
 * @author  Mattias Andrée <maandree@kth.se>
 */
namespace tbrpg
{
  /**
   * Create a party
   *
   * @param   senario  The game senario
   * @param   sheets   Solts for the character sheets of the party
   * @return           Whether the party creation was completed
   */
  bool createParty(const Senario& senario, std::vector<CharacterSheet*>* sheets);

  /**
   * Fixes character attributes and add them to the party member list
   *
   * @param  senario  The game senario
   * @param  sheets   The character sheets of the party
   */
  void fixCharacters(Senario& senario, std::vector<CharacterSheet*>* sheets);

  /**
   * This the main entry point of the program
   *
   * @param   argc  The number of elements in `argv`
   * @param   argv  Command line arguments, including the execute file
   * @return        Exit value, 0 if successful
   */
  int __main__(int argc, char** argv)
  {
    (void) argc;
    (void) argv;

    /** BEGIN initialise random **/
    unsigned a, d;
    asm("cpuid");
    asm volatile("rdtsc" : "=a" (a), "=d" (d));
    srand(((long long)a) | (((long long)d) << 32LL));
    /** END initialise random **/

    std::vector<std::string> senarioTitles = {BasicSenario::getTitle()};

    long senarioIndex = promptDialogue(4, "Welcome to tbrpg!",
		 "Select a game senario."                        "\n"
		                                                 "\n"
		 "Use the up and down arrow keys to nagivate,"   "\n"
		 "and press enter, spacebar or the right arrow"  "\n"
		 "key to start the select senario. You can"      "\n"
		 "also use the letter in front of the senario."  "\n"
		                                                 "\n"
		 "You should play with the manual by your side," "\n"
		 "run `info tbrpg` or open a graphical if you"   "\n"
		 "have one installed, jfbview can view them in"  "\n"
		 "the TTY."                                      "\n"
		                                                 "\n"
		 "If you want to exit, press <control>g."        "\n",
		 senarioTitles, 0);

    Senario* _senario = nullptr;
    if (senarioIndex == 0)
      _senario = new BasicSenario();
    if (_senario == nullptr)
      {
	cleaner::getInstance().clean();
	return 0;
      }

    cleaner::getInstance().enqueueDelete(_senario);
    Senario& senario = *_senario;
    std::vector<CharacterSheet*>* sheets = new std::vector<CharacterSheet*>(senario.rules.party_start_size);

    if (createParty(senario, sheets) == false)
      {
	delete sheets;
        cleaner::getInstance().clean();
	return 0;
      }

    fixCharacters(senario, sheets);
    delete sheets;
    senario.partyFormed();
    senario.start();

    GamePlay game = GamePlay(senario);
    while (game.next())
      ;

    cleaner::getInstance().clean();
    return 0;
  }

  /**
   * Create a party
   *
   * @param   senario  The game senario
   * @param   sheets   Solts for the character sheets of the party
   * @return           Whether the party creation was completed
   */
  bool createParty(const Senario& senario, std::vector<CharacterSheet*>* sheets)
  {
    return promptSlots<CharacterSheet>("Create your party, you need at least one character.",
				       true, 1, sheets,
				       [&] () -> CharacterSheet* {
					 CharacterCreator cc = CharacterCreator(senario.rules);
					 return cc.create();
				       },
				       [] (CharacterSheet* sheet, bool selected) -> void {
					 if (sheet == nullptr)
					   std::cout << (selected ? "\033[01m(empty)\033[21m" : "(empty)");
					 else
					   {
					     std::cout << (selected ? "\033[01;3" : "\033[3") << (int)(sheet->colour) << "m";
					     std::cout << sheet->name << "\033[21;39m";
					   }
				       });
  }

  /**
   * Fixes character attributes and add them to the party member list
   *
   * @param  senario  The game senario
   * @param  sheets   The character sheets of the party
   */
  void fixCharacters(Senario& senario, std::vector<CharacterSheet*>* sheets)
  {
    for (CharacterSheet* sheet : *sheets)
      if (sheet != nullptr)
	{
	  Character* character = new Character();
	  character->record = *sheet;
	  delete sheet;
	  character->protagonist = true;
	  character->hit_points = character->record.hit_points;

	  for (size_t i = 0, n = character->record.prestige.size(); i < n; i++)
	    if (character->record.prestige[i] >= PROTOTYPE(Warrior))
	      if (character->record.level[i] >= 7)
		character->extra_attacks += 2;

	  cleaner::getInstance().enqueueDelete(character);
	  senario.party.characters.push_back(character);
	}
  }

}

/**
 * This the main entry point of the program
 *
 * @param   argc  The number of elements in `argv`
 * @param   argv  Command line arguments, including the execute file
 * @return        Exit value, 0 if successful
 */
int main(int argc, char** argv)
{
  return tbrpg::__main__(argc, argv);
}

<|endoftext|>


<commit_before>/*
 * Pi 2 is connected to the ImP/IMU via UART, Camera via CSI, Burn Wire Relay
 * via GPIO and Pi 1 via Ethernet and GPIO for LO, SOE and SODS signals.
 *
 * This program controls most of the main logic and for the PIOneERs mission
 * on board REXUS.
 */

#include <stdio.h>
#include <cstdint>
#include <unistd.h> //For sleep
#include <stdlib.h>
#include <iostream>
#include <signal.h>

#include "pins2.h"
#include "camera/camera.h"
#include "UART/UART.h"
#include "Ethernet/Ethernet.h"
#include "comms/pipes.h"
#include "comms/protocol.h"
#include "comms/packet.h"
#include "timing/timer.h"
#include "logger/logger.h"

#include <wiringPi.h>

Logger Log("/Docs/Logs/raspi2");

bool flight_mode = false;

// Global variable for the Camera and IMU
PiCamera Cam;

// Setup for the UART communications
int baud = 230400;
ImP IMP(baud);
comms::Pipe ImP_stream;

// Ethernet communication setup and variables (we are acting as client)
int port_no = 31415; // Random unused port for communication
Raspi2 raspi2(port_no);

/**
 * Checks whether input is activated
 * @param pin: GPIO to be checked
 * @return true or false
 */
bool poll_input(int pin) {
	int count = 0;
	for (int i = 0; i < 5; i++) {
		count += digitalRead(pin);
		delayMicroseconds(200);
	}
	return (count < 3) ? true : false;
}

void signal_handler(int s) {
	Log("FATAL") << "Exiting program after signal " << s;
	if (Cam.is_running()) {
		Cam.stopVideo();
		Log("INFO") << "Stopping camera process";
	} else {
		Log("ERROR") << "Camera process died prematurely or did not start";
	}

	if (raspi2.is_alive()) {
		raspi2.end();
		Log("INFO") << "Closed Ethernet communication";
	} else {
		Log("ERROR") << "Ethernet process died prematurely or did not start";
	}
	if (&ImP_stream != NULL) {
		ImP_stream.close_pipes();
		Log("INFO") << "Closed ImP communication";
	} else {
		Log("ERROR") << "ImP process died prematurely or did not start";
	}
	digitalWrite(BURNWIRE, 0);
	// TODO copy data to a further backup directory
	Log("INFO") << "Ending program, Pi rebooting";
	system("sudo reboot");
	exit(1); // This was an unexpected end so we will exit with an error!
}

int SODS_SIGNAL() {
	/*
	 * When the 'Start of Data Storage' signal is received all data recording
	 * is stopped (IMU and Camera) and power to the camera is cut off to stop
	 * shorting due to melting on re-entry. All data is copied into a backup
	 * directory.
	 */
	Log("INFO") << "SODS signal received";
	if (Cam.is_running()) {
		Cam.stopVideo();
		Log("INFO") << "Stopping camera process";
	} else {
		Log("ERROR") << "Camera process died prematurely or did not start";
	}

	if (raspi2.is_alive()) {
		raspi2.end();
		Log("INFO") << "Closed Ethernet communication";
	} else {
		Log("ERROR") << "Ethernet process died prematurely or did not start";
	}
	if (&ImP_stream != NULL) {
		ImP_stream.close_pipes();
		Log("INFO") << "Closed ImP communication";
	} else {
		Log("ERROR") << "ImP process died prematurely or did not start";
	}
	digitalWrite(BURNWIRE, 0);
	digitalWrite(BURNWIRE, 0);
	// TODO copy data to a further backup directory
	Log("INFO") << "Ending program, Pi rebooting";
	system("sudo reboot");
	return 0;
}

int SOE_SIGNAL() {
	/*
	 * When the 'Start of Experiment' signal is received the boom needs to be
	 * deployed and the ImP and IMU to start taking measurements. For boom
	 * deployment is there is no increase in the encoder count or ?? seconds
	 * have passed since start of deployment then it is assumed that either the
	 * boom has reached it's full length or something has gone wrong and the
	 * count of the encoder is sent to ground.
	 */
	Log("INFO") << "SOE signal received";
	// Setup the ImP and start requesting data
	ImP_stream = IMP.startDataCollection("Docs/Data/Pi2/test");
	Log("INFO") << "Started data collection from ImP";
	comms::Packet p; // Buffer for reading data from the IMU stream
	// Trigger the burn wire for 10 seconds!
	Log("INFO") << "Triggering burnwire";
	digitalWrite(BURNWIRE, 1);
	Timer tmr;
	Log("INFO") << "Burn wire triggered" << std::endl;
	while (tmr.elapsed() < 10000) {
		// Get ImP data
		int n = ImP_stream.binread(&p, sizeof (p));
		if (n > 0) {
			Log("DATA (ImP)") << p;
			raspi2.sendPacket(p);
		}

		n = raspi2.recvPacket(p);
		if (n > 0)
			Log("DATA (PI1)") << p;
		Timer::sleep_ms(10);
	}
	digitalWrite(BURNWIRE, 0);
	Log("INFO") << "Burn wire off after " << tmr.elapsed() << " ms";
	Log("INFO") << "Waiting for SODS";
	// Wait for the next signal to continue the program
	bool signal_received = false;
	while (!signal_received) {
		signal_received = poll_input(SODS);
		// Read data from IMU_data_stream and echo it to Ethernet
		int n = ImP_stream.binread(&p, sizeof (p));
		if (n > 0) {
			Log("DATA (ImP)") << p;
			raspi2.sendPacket(p);
		}

		n = raspi2.recvPacket(p);
		if (n > 0)
			Log("DATA (PI1)") << p;
		Timer::sleep_ms(10);
	}
	return SODS_SIGNAL();
}

int LO_SIGNAL() {
	/*
	 * When the 'Lift Off' signal is received from the REXUS Module the cameras
	 * are set to start recording video and we then wait to receive the 'Start
	 * of Experiment' signal (when the nose-cone is ejected)
	 */
	Log("INFO") << "LO signal received";
	Cam.startVideo("Docs/Video/rexus_video");
	Log("INFO") << "Camera started recording video";
	// Poll the SOE pin until signal is received
	Log("INFO") << "Waiting for SOE";
	bool signal_received = false;
	while (!signal_received) {
		Timer::sleep_ms(10);
		signal_received = poll_input(SOE);
		// TODO Implement communications with RXSM
	}
	return SOE_SIGNAL();
}

int main() {
	/*
	 * This part of the program is run before the Lift-Off. In effect it
	 * continually listens for commands from the ground station and runs any
	 * required tests, regularly reporting status until the LO Signal is
	 * received.
	 */
	signal(SIGINT, signal_handler);
	// Create necessary directories for saving files
	system("mkdir -p Docs/Data/Pi1 Docs/Data/Pi2 Docs/Data/test Docs/Video Docs/Logs");
	Log.start_log();
	Log("INFO") << "Pi2 is alive";
	wiringPiSetup();
	// Setup main signal pins
	pinMode(LO, INPUT);
	pullUpDnControl(LO, PUD_UP);
	pinMode(SOE, INPUT);
	pullUpDnControl(SOE, PUD_UP);
	pinMode(SODS, INPUT);
	pullUpDnControl(SODS, PUD_UP);
	pinMode(ALIVE, OUTPUT);
	Log("INFO") << "Main signal pins setup";
	// Setup pins and check whether we are in flight mode
	pinMode(LAUNCH_MODE, INPUT);
	pullUpDnControl(LAUNCH_MODE, PUD_UP);
	flight_mode = digitalRead(LAUNCH_MODE);
	Log("INFO") << (flight_mode ? "flight mode enabled" : "test mode enabled");

	// Setup Burn Wire
	pinMode(BURNWIRE, OUTPUT);

	// Setup server and wait for client
	digitalWrite(ALIVE, 1);
	Log("INFO") << "Waiting for connection from client on port " << port_no;
	try {
		raspi2.run("Docs/Data/Pi1/backup.txt");
	} catch (EthernetException e) {
		Log("FATAL") << "Unable to connect to pi 1";
		signal_handler(-5);
	}
	Log("INFO") << "Connection to Pi1 successfil";
	Log("INFO") << "Waiting for LO signal";
	// Check for LO signal.
	std::string msg;
	bool signal_received = false;
	comms::Packet p;
	comms::byte1_t id;
	comms::byte2_t index;
	char data[16];
	while (!signal_received) {
		Timer::sleep_ms(10);
		signal_received = poll_input(LO);
		// TODO Implement communications with Pi 1
		int n = raspi2.recvPacket(p);
		if (n > 0) {
			Log("PI1") << p;
			comms::Protocol::unpack(p, id, index, data);
			if (id == 0b11000000) {
				Log("RXSM") << "Received Command: " << data[0];
				switch (data[0]) {
					case 1: // restart
						Log("INFO") << "Rebooting...";
						system("sudo reboot now");
						exit(0);
					case 2: // shutdown
						Log("INFO") << "Shutting down...";
						system("sudo shutdown now");
						exit(0);
					case 3: // Toggle flight mode
						Log("INFO") << "Toggling flight mode";
						flight_mode = (flight_mode) ? false : true;
						Log("INFO") << (flight_mode ? "flight mode enabled" : "test mode enabled");
						if (flight_mode)
							REXUS.sendMsg("WARNING: Flight mode enabled");
						else
							REXUS.sendMsg("Entering test mode");
						break;
					case 4: // Run all tests
						tests::all_tests();
				}
			}
			Log("DATA (PI1)") << "Unpacked\n\t\"" << std::string(data) << "\"";
			//TODO handle incoming commands!
		}
	}
	LO_SIGNAL();
	system("sudo reboot");
	return 0;
}
<commit_msg>Debugging raspi2<commit_after>/*
 * Pi 2 is connected to the ImP/IMU via UART, Camera via CSI, Burn Wire Relay
 * via GPIO and Pi 1 via Ethernet and GPIO for LO, SOE and SODS signals.
 *
 * This program controls most of the main logic and for the PIOneERs mission
 * on board REXUS.
 */

#include <stdio.h>
#include <cstdint>
#include <unistd.h> //For sleep
#include <stdlib.h>
#include <iostream>
#include <signal.h>

#include "pins2.h"
#include "camera/camera.h"
#include "UART/UART.h"
#include "Ethernet/Ethernet.h"
#include "comms/pipes.h"
#include "comms/protocol.h"
#include "comms/packet.h"
#include "timing/timer.h"
#include "logger/logger.h"
#include "tests/tests.h"

#include <wiringPi.h>

Logger Log("/Docs/Logs/raspi2");

bool flight_mode = false;

// Global variable for the Camera and IMU
PiCamera Cam;

// Setup for the UART communications
int baud = 230400;
ImP IMP(baud);
comms::Pipe ImP_stream;

// Ethernet communication setup and variables (we are acting as client)
int port_no = 31415; // Random unused port for communication
Raspi2 raspi2(port_no);

/**
 * Checks whether input is activated
 * @param pin: GPIO to be checked
 * @return true or false
 */
bool poll_input(int pin) {
	int count = 0;
	for (int i = 0; i < 5; i++) {
		count += digitalRead(pin);
		delayMicroseconds(200);
	}
	return (count < 3) ? true : false;
}

void signal_handler(int s) {
	Log("FATAL") << "Exiting program after signal " << s;
	if (Cam.is_running()) {
		Cam.stopVideo();
		Log("INFO") << "Stopping camera process";
	} else {
		Log("ERROR") << "Camera process died prematurely or did not start";
	}

	if (raspi2.is_alive()) {
		raspi2.end();
		Log("INFO") << "Closed Ethernet communication";
	} else {
		Log("ERROR") << "Ethernet process died prematurely or did not start";
	}
	if (&ImP_stream != NULL) {
		ImP_stream.close_pipes();
		Log("INFO") << "Closed ImP communication";
	} else {
		Log("ERROR") << "ImP process died prematurely or did not start";
	}
	digitalWrite(BURNWIRE, 0);
	// TODO copy data to a further backup directory
	Log("INFO") << "Ending program, Pi rebooting";
	system("sudo reboot");
	exit(1); // This was an unexpected end so we will exit with an error!
}

int SODS_SIGNAL() {
	/*
	 * When the 'Start of Data Storage' signal is received all data recording
	 * is stopped (IMU and Camera) and power to the camera is cut off to stop
	 * shorting due to melting on re-entry. All data is copied into a backup
	 * directory.
	 */
	Log("INFO") << "SODS signal received";
	if (Cam.is_running()) {
		Cam.stopVideo();
		Log("INFO") << "Stopping camera process";
	} else {
		Log("ERROR") << "Camera process died prematurely or did not start";
	}

	if (raspi2.is_alive()) {
		raspi2.end();
		Log("INFO") << "Closed Ethernet communication";
	} else {
		Log("ERROR") << "Ethernet process died prematurely or did not start";
	}
	if (&ImP_stream != NULL) {
		ImP_stream.close_pipes();
		Log("INFO") << "Closed ImP communication";
	} else {
		Log("ERROR") << "ImP process died prematurely or did not start";
	}
	digitalWrite(BURNWIRE, 0);
	digitalWrite(BURNWIRE, 0);
	// TODO copy data to a further backup directory
	Log("INFO") << "Ending program, Pi rebooting";
	system("sudo reboot");
	return 0;
}

int SOE_SIGNAL() {
	/*
	 * When the 'Start of Experiment' signal is received the boom needs to be
	 * deployed and the ImP and IMU to start taking measurements. For boom
	 * deployment is there is no increase in the encoder count or ?? seconds
	 * have passed since start of deployment then it is assumed that either the
	 * boom has reached it's full length or something has gone wrong and the
	 * count of the encoder is sent to ground.
	 */
	Log("INFO") << "SOE signal received";
	// Setup the ImP and start requesting data
	ImP_stream = IMP.startDataCollection("Docs/Data/Pi2/test");
	Log("INFO") << "Started data collection from ImP";
	comms::Packet p; // Buffer for reading data from the IMU stream
	// Trigger the burn wire for 10 seconds!
	Log("INFO") << "Triggering burnwire";
	digitalWrite(BURNWIRE, 1);
	Timer tmr;
	Log("INFO") << "Burn wire triggered" << std::endl;
	while (tmr.elapsed() < 10000) {
		// Get ImP data
		int n = ImP_stream.binread(&p, sizeof (p));
		if (n > 0) {
			Log("DATA (ImP)") << p;
			raspi2.sendPacket(p);
		}

		n = raspi2.recvPacket(p);
		if (n > 0)
			Log("DATA (PI1)") << p;
		Timer::sleep_ms(10);
	}
	digitalWrite(BURNWIRE, 0);
	Log("INFO") << "Burn wire off after " << tmr.elapsed() << " ms";
	Log("INFO") << "Waiting for SODS";
	// Wait for the next signal to continue the program
	bool signal_received = false;
	while (!signal_received) {
		signal_received = poll_input(SODS);
		// Read data from IMU_data_stream and echo it to Ethernet
		int n = ImP_stream.binread(&p, sizeof (p));
		if (n > 0) {
			Log("DATA (ImP)") << p;
			raspi2.sendPacket(p);
		}

		n = raspi2.recvPacket(p);
		if (n > 0)
			Log("DATA (PI1)") << p;
		Timer::sleep_ms(10);
	}
	return SODS_SIGNAL();
}

int LO_SIGNAL() {
	/*
	 * When the 'Lift Off' signal is received from the REXUS Module the cameras
	 * are set to start recording video and we then wait to receive the 'Start
	 * of Experiment' signal (when the nose-cone is ejected)
	 */
	Log("INFO") << "LO signal received";
	Cam.startVideo("Docs/Video/rexus_video");
	Log("INFO") << "Camera started recording video";
	// Poll the SOE pin until signal is received
	Log("INFO") << "Waiting for SOE";
	bool signal_received = false;
	while (!signal_received) {
		Timer::sleep_ms(10);
		signal_received = poll_input(SOE);
		// TODO Implement communications with RXSM
	}
	return SOE_SIGNAL();
}

int main() {
	/*
	 * This part of the program is run before the Lift-Off. In effect it
	 * continually listens for commands from the ground station and runs any
	 * required tests, regularly reporting status until the LO Signal is
	 * received.
	 */
	signal(SIGINT, signal_handler);
	// Create necessary directories for saving files
	system("mkdir -p Docs/Data/Pi1 Docs/Data/Pi2 Docs/Data/test Docs/Video Docs/Logs");
	Log.start_log();
	Log("INFO") << "Pi2 is alive";
	wiringPiSetup();
	// Setup main signal pins
	pinMode(LO, INPUT);
	pullUpDnControl(LO, PUD_UP);
	pinMode(SOE, INPUT);
	pullUpDnControl(SOE, PUD_UP);
	pinMode(SODS, INPUT);
	pullUpDnControl(SODS, PUD_UP);
	pinMode(ALIVE, OUTPUT);
	Log("INFO") << "Main signal pins setup";
	// Setup pins and check whether we are in flight mode
	pinMode(LAUNCH_MODE, INPUT);
	pullUpDnControl(LAUNCH_MODE, PUD_UP);
	flight_mode = digitalRead(LAUNCH_MODE);
	Log("INFO") << (flight_mode ? "flight mode enabled" : "test mode enabled");

	// Setup Burn Wire
	pinMode(BURNWIRE, OUTPUT);

	// Setup server and wait for client
	digitalWrite(ALIVE, 1);
	Log("INFO") << "Waiting for connection from client on port " << port_no;
	try {
		raspi2.run("Docs/Data/Pi1/backup.txt");
	} catch (EthernetException e) {
		Log("FATAL") << "Unable to connect to pi 1";
		signal_handler(-5);
	}
	Log("INFO") << "Connection to Pi1 successfil";
	Log("INFO") << "Waiting for LO signal";
	// Check for LO signal.
	std::string msg;
	bool signal_received = false;
	comms::Packet p;
	comms::byte1_t id;
	comms::byte2_t index;
	char data[16];
	while (!signal_received) {
		Timer::sleep_ms(10);
		signal_received = poll_input(LO);
		// TODO Implement communications with Pi 1
		int n = raspi2.recvPacket(p);
		if (n > 0) {
			Log("PI1") << p;
			comms::Protocol::unpack(p, id, index, data);
			if (id == 0b11000000) {
				Log("RXSM") << "Received Command: " << data[0];
				switch (data[0]) {
					case 1: // restart
						Log("INFO") << "Rebooting...";
						system("sudo reboot now");
						exit(0);
					case 2: // shutdown
						Log("INFO") << "Shutting down...";
						system("sudo shutdown now");
						exit(0);
					case 3: // Toggle flight mode
						Log("INFO") << "Toggling flight mode";
						flight_mode = (flight_mode) ? false : true;
						Log("INFO") << (flight_mode ? "flight mode enabled" : "test mode enabled");
						break;
					case 4: // Run all tests
						tests::all_tests();
				}
			}
			Log("DATA (PI1)") << "Unpacked\n\t\"" << std::string(data) << "\"";
			//TODO handle incoming commands!
		}
	}
	LO_SIGNAL();
	system("sudo reboot");
	return 0;
}
<|endoftext|>


<commit_before>/***************************************
** Tsunagari Tile Engine              **
** reader.cpp                         **
** Copyright 2011-2013 PariahSoft LLC **
***************************************/

// **********
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// **********

#include <errno.h>
#include <stdlib.h>

#include <Gosu/Bitmap.hpp>
#include <Gosu/Image.hpp>
#include <Gosu/IO.hpp>
#include <map>
#include <physfs.h>

#include "cache-template.cpp"
#include "client-conf.h"
#include "formatter.h"
#include "log.h"
#include "python.h"
#include "python-bindings-template.cpp"
#include "reader.h"
#include "script.h"
#include "window.h"
#include "xml.h"

#define ASSERT(x)  if (!(x)) { return false; }

typedef std::shared_ptr<xmlDtd> DTDRef;
typedef std::shared_ptr<std::string> StringRef;

// Caches that store processed, game-ready objects. Garbage collected.
Cache<ImageRef> images;
Cache<TiledImageRef> tiles;
Cache<SampleRef> sounds;
Cache<XMLRef> xmls;
Cache<StringRef> texts;

// DTDs don't expire. No garbage collection.
typedef std::map<std::string, DTDRef> DTDMap;
DTDMap dtds;

static std::string path(const std::string& entryName)
{
	// XXX: archive might not be world
	return conf.worldFilename + "/" + entryName;
}

template <class T>
static bool readFromDisk(const std::string& name, T& buf)
{
	PHYSFS_sint64 size;
	PHYSFS_File* zf;

	if (!PHYSFS_exists(name.c_str())) {
		Log::err("Reader", Formatter("%: file missing")
				% path(name));
		return false;
	}

	zf = PHYSFS_openRead(name.c_str());
	if (!zf) {
		Log::err("Reader", Formatter("%: error opening file: %")
				% path(name) % PHYSFS_getLastError());
		return false;
	}

	size = PHYSFS_fileLength(zf);
	if (size == -1) {
		Log::err("Reader", Formatter("%: could not determine file size: %")
				% path(name) % PHYSFS_getLastError());
		PHYSFS_close(zf);
		return false;
	}
	if (size > std::numeric_limits<uint32_t>::max()) {
		// FIXME: Technically, we just need to issue multiple calls to
		// PHYSFS_read. Fix when needed.
		Log::err("Reader", Formatter("%: file too long (>4GB)")
				% path(name));
		PHYSFS_close(zf);
		return false;
	}

	buf.resize(size);
	if (size == 0) {
		PHYSFS_close(zf);
		return true;
	}

	if (PHYSFS_read(zf, (char*)(buf.data()),
			(PHYSFS_uint32)size, 1) != 1) {
		Log::err("Reader", Formatter("%: error reading file: %")
				% path(name) % PHYSFS_getLastError());
		PHYSFS_close(zf);
		return false;
	}

	PHYSFS_close(zf);
	return true;
}

// FIXME: Should be moved to xml.cpp!!!!!!
static DTDRef parseDTD(const std::string& path)
{
	xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;

	std::string bytes = Reader::readString(path);
	if (bytes.empty())
		return DTDRef();

	xmlParserInputBuffer* input = xmlParserInputBufferCreateMem(
			bytes.c_str(), (int)bytes.size(), enc);
	if (!input)
		return DTDRef();

	xmlDtd* dtd = xmlIOParseDTD(NULL, input, enc);
	if (!dtd)
		return DTDRef();

	return DTDRef(dtd, xmlFreeDtd);
}

// FIXME: Should be moved to xml.cpp!!!!!!
static bool preloadDTDs()
{
	ASSERT(dtds["dtd/area.dtd"]   = parseDTD("dtd/area.dtd"));
	ASSERT(dtds["dtd/entity.dtd"] = parseDTD("dtd/entity.dtd"));
	ASSERT(dtds["dtd/tsx.dtd"] = parseDTD("dtd/tsx.dtd"));
	ASSERT(dtds["dtd/world.dtd"]  = parseDTD("dtd/world.dtd"));
	return true;
}

static xmlDtd* getDTD(const std::string& name)
{
	DTDMap::iterator it = dtds.find(name);
	return it == dtds.end() ? NULL : it->second.get();
}

static XMLDoc* readXMLDoc(const std::string& name,
                          const std::string& dtdPath)
{
	std::string p = path(name);
	std::string data = Reader::readString(name);
	xmlDtd* dtd = getDTD(dtdPath);

	if (!dtd || data.empty())
		return NULL;
	XMLDoc* doc = new XMLDoc;
	if (!doc->init(p, data, dtd)) {
		delete doc;
		return NULL;
	}
	return doc;
}

static bool callInitpy(const std::string& archivePath)
{
	ASSERT(Reader::prependPath(archivePath));
	bool exists = Reader::resourceExists("__init__.py");
	Log::info(archivePath,
		std::string("__init__.py: ") +
		(exists ? "found" : "not found"));
	if (exists)
		// FIXME: Python will cache the __init__ module with no path prefix
		ASSERT(Script::create("__init__"));
	ASSERT(Reader::rmPath(archivePath));
	return true;
}

bool Reader::init(char* argv0)
{
	ASSERT(PHYSFS_init(argv0) != 0);

	// If any of our archives contain a file called "__init__.py", call it.
	for (Conf::StringVector::const_iterator it = conf.dataPath.begin(); it != conf.dataPath.end(); it++) {
		const std::string archive = *it;
		ASSERT(callInitpy(archive));
	}
	ASSERT(callInitpy(BASE_ZIP_PATH));

	ASSERT(prependPath(BASE_ZIP_PATH));

	// DTDs must be loaded from BASE_ZIP. They cannot be allowed to be
	// loaded from the world.
	ASSERT(preloadDTDs());

	ASSERT(prependPath(conf.worldFilename));
	for (Conf::StringVector::const_iterator it = conf.dataPath.begin(); it != conf.dataPath.end(); it++) {
		const std::string archive = *it;
		ASSERT(prependPath(archive));
	}

	return true;
}

void Reader::deinit()
{
	PHYSFS_deinit();
}

bool Reader::prependPath(const std::string& path)
{
	int err = PHYSFS_mount(path.c_str(), NULL, 0);
	if (err == 0) {
		Log::fatal("Reader", Formatter("%: could not open archive: %")
				% path % PHYSFS_getLastError());
		return false;
	}

	pythonPrependPath(path);

	return true;
}

bool Reader::appendPath(const std::string& path)
{
	int err = PHYSFS_mount(path.c_str(), NULL, 1);
	if (err == 0) {
		Log::fatal("Reader", Formatter("%: could not open archive: %")
				% path % PHYSFS_getLastError());
		return false;
	}

	pythonRmPath(path);

	return true;
}

bool Reader::rmPath(const std::string& path)
{
	int err = PHYSFS_removeFromSearchPath(path.c_str());
	if (err == 0) {
		Log::err("Reader", Formatter("libphysfs: %: %")
				% path % PHYSFS_getLastError());
		return false;
	}

	return true;
}

bool Reader::resourceExists(const std::string& name)
{
	return PHYSFS_exists(name.c_str());
}

bool Reader::directoryExists(const std::string& name)
{
	return resourceExists(name) && PHYSFS_isDirectory(name.c_str());
}

bool Reader::fileExists(const std::string& name)
{
	return resourceExists(name) && !PHYSFS_isDirectory(name.c_str());
}

Gosu::Buffer* Reader::readBuffer(const std::string& name)
{
	Gosu::Buffer* buf = new Gosu::Buffer();

	if (readFromDisk(name, *buf)) {
		return buf;
	}
	else {
		delete buf;
		return NULL;
	}
}

std::string Reader::readString(const std::string& name)
{
	std::string str;
	return readFromDisk(name, str) ? str : "";
}

ImageRef Reader::getImage(const std::string& name)
{
	ImageRef existing = images.lifetimeRequest(name);
	if (existing)
		return existing;

	std::unique_ptr<Gosu::Buffer> buffer(readBuffer(name));
	if (!buffer)
		return ImageRef();

	ImageRef result(Image::create(buffer->data(), buffer->size()));
	if (!result)
		return ImageRef();

	images.lifetimePut(name, result);
	return result;
}

TiledImageRef Reader::getTiledImage(const std::string& name,
		int w, int h)
{
	TiledImageRef existing = tiles.momentaryRequest(name);
	if (existing)
		return existing;

	std::unique_ptr<Gosu::Buffer> buffer(readBuffer(name));
	if (!buffer)
		return TiledImageRef();

	TiledImageRef result(
		TiledImage::create(buffer->data(), buffer->size(), w, h)
	);
	if (!result)
		return TiledImageRef();

	tiles.momentaryPut(name, result);
	return result;
}

SampleRef Reader::getSample(const std::string& name)
{
	if (!conf.audioEnabled)
		return SampleRef();

	SampleRef existing = sounds.lifetimeRequest(name);
	if (existing)
		return existing;

	std::unique_ptr<Gosu::Buffer> buffer(readBuffer(name));
	if (!buffer)
		return SampleRef();
	SampleRef result(new Sound(new Gosu::Sample(buffer->frontReader())));

	sounds.lifetimePut(name, result);
	return result;
}

XMLRef Reader::getXMLDoc(const std::string& name,
                            const std::string& dtdFile)
{
	XMLRef existing = xmls.momentaryRequest(name);
	if (existing)
		return existing;

	XMLRef result(readXMLDoc(name, dtdFile));

	xmls.momentaryPut(name, result);
	return result;
}

std::string Reader::getText(const std::string& name)
{
	StringRef existing = texts.momentaryRequest(name);
	if (existing)
		return *existing.get();

	StringRef result(new std::string(readString(name)));

	texts.momentaryPut(name, result);
	return *result.get();
}

void Reader::garbageCollect()
{
	images.garbageCollect();
	tiles.garbageCollect();
	sounds.garbageCollect();
	// songs.garbageCollect();
	xmls.garbageCollect();
	texts.garbageCollect();
}

void exportReader()
{
	// FIXME: Broken with shift to singleton. No instantiated object to bind.
	// Fix will require a stub object.

#if 0
	using namespace boost::python;

	class_<Reader>("Reader", no_init)
		.def("resource_exists", &Reader::resourceExists)
		.def("run_python_script", &Reader::runPythonScript)
		.def("get_text", &Reader::getText);
	pythonSetGlobal("Reader");
#endif
}

<commit_msg>reader.cpp: label global vars with 'static'<commit_after>/***************************************
** Tsunagari Tile Engine              **
** reader.cpp                         **
** Copyright 2011-2013 PariahSoft LLC **
***************************************/

// **********
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
// **********

#include <errno.h>
#include <stdlib.h>

#include <Gosu/Bitmap.hpp>
#include <Gosu/Image.hpp>
#include <Gosu/IO.hpp>
#include <map>
#include <physfs.h>

#include "cache-template.cpp"
#include "client-conf.h"
#include "formatter.h"
#include "log.h"
#include "python.h"
#include "python-bindings-template.cpp"
#include "reader.h"
#include "script.h"
#include "window.h"
#include "xml.h"

#define ASSERT(x)  if (!(x)) { return false; }

typedef std::shared_ptr<xmlDtd> DTDRef;
typedef std::shared_ptr<std::string> StringRef;

// Caches that store processed, game-ready objects. Garbage collected.
static Cache<ImageRef> images;
static Cache<TiledImageRef> tiles;
static Cache<SampleRef> sounds;
static Cache<XMLRef> xmls;
static Cache<StringRef> texts;

// DTDs don't expire. No garbage collection.
typedef std::map<std::string, DTDRef> DTDMap;
static DTDMap dtds;

static std::string path(const std::string& entryName)
{
	// XXX: archive might not be world
	return conf.worldFilename + "/" + entryName;
}

template <class T>
static bool readFromDisk(const std::string& name, T& buf)
{
	PHYSFS_sint64 size;
	PHYSFS_File* zf;

	if (!PHYSFS_exists(name.c_str())) {
		Log::err("Reader", Formatter("%: file missing")
				% path(name));
		return false;
	}

	zf = PHYSFS_openRead(name.c_str());
	if (!zf) {
		Log::err("Reader", Formatter("%: error opening file: %")
				% path(name) % PHYSFS_getLastError());
		return false;
	}

	size = PHYSFS_fileLength(zf);
	if (size == -1) {
		Log::err("Reader", Formatter("%: could not determine file size: %")
				% path(name) % PHYSFS_getLastError());
		PHYSFS_close(zf);
		return false;
	}
	if (size > std::numeric_limits<uint32_t>::max()) {
		// FIXME: Technically, we just need to issue multiple calls to
		// PHYSFS_read. Fix when needed.
		Log::err("Reader", Formatter("%: file too long (>4GB)")
				% path(name));
		PHYSFS_close(zf);
		return false;
	}

	buf.resize(size);
	if (size == 0) {
		PHYSFS_close(zf);
		return true;
	}

	if (PHYSFS_read(zf, (char*)(buf.data()),
			(PHYSFS_uint32)size, 1) != 1) {
		Log::err("Reader", Formatter("%: error reading file: %")
				% path(name) % PHYSFS_getLastError());
		PHYSFS_close(zf);
		return false;
	}

	PHYSFS_close(zf);
	return true;
}

// FIXME: Should be moved to xml.cpp!!!!!!
static DTDRef parseDTD(const std::string& path)
{
	xmlCharEncoding enc = XML_CHAR_ENCODING_NONE;

	std::string bytes = Reader::readString(path);
	if (bytes.empty())
		return DTDRef();

	xmlParserInputBuffer* input = xmlParserInputBufferCreateMem(
			bytes.c_str(), (int)bytes.size(), enc);
	if (!input)
		return DTDRef();

	xmlDtd* dtd = xmlIOParseDTD(NULL, input, enc);
	if (!dtd)
		return DTDRef();

	return DTDRef(dtd, xmlFreeDtd);
}

// FIXME: Should be moved to xml.cpp!!!!!!
static bool preloadDTDs()
{
	ASSERT(dtds["dtd/area.dtd"]   = parseDTD("dtd/area.dtd"));
	ASSERT(dtds["dtd/entity.dtd"] = parseDTD("dtd/entity.dtd"));
	ASSERT(dtds["dtd/tsx.dtd"] = parseDTD("dtd/tsx.dtd"));
	ASSERT(dtds["dtd/world.dtd"]  = parseDTD("dtd/world.dtd"));
	return true;
}

static xmlDtd* getDTD(const std::string& name)
{
	DTDMap::iterator it = dtds.find(name);
	return it == dtds.end() ? NULL : it->second.get();
}

static XMLDoc* readXMLDoc(const std::string& name,
                          const std::string& dtdPath)
{
	std::string p = path(name);
	std::string data = Reader::readString(name);
	xmlDtd* dtd = getDTD(dtdPath);

	if (!dtd || data.empty())
		return NULL;
	XMLDoc* doc = new XMLDoc;
	if (!doc->init(p, data, dtd)) {
		delete doc;
		return NULL;
	}
	return doc;
}

static bool callInitpy(const std::string& archivePath)
{
	ASSERT(Reader::prependPath(archivePath));
	bool exists = Reader::resourceExists("__init__.py");
	Log::info(archivePath,
		std::string("__init__.py: ") +
		(exists ? "found" : "not found"));
	if (exists)
		// FIXME: Python will cache the __init__ module with no path prefix
		ASSERT(Script::create("__init__"));
	ASSERT(Reader::rmPath(archivePath));
	return true;
}

bool Reader::init(char* argv0)
{
	ASSERT(PHYSFS_init(argv0) != 0);

	// If any of our archives contain a file called "__init__.py", call it.
	for (Conf::StringVector::const_iterator it = conf.dataPath.begin(); it != conf.dataPath.end(); it++) {
		const std::string archive = *it;
		ASSERT(callInitpy(archive));
	}
	ASSERT(callInitpy(BASE_ZIP_PATH));

	ASSERT(prependPath(BASE_ZIP_PATH));

	// DTDs must be loaded from BASE_ZIP. They cannot be allowed to be
	// loaded from the world.
	ASSERT(preloadDTDs());

	ASSERT(prependPath(conf.worldFilename));
	for (Conf::StringVector::const_iterator it = conf.dataPath.begin(); it != conf.dataPath.end(); it++) {
		const std::string archive = *it;
		ASSERT(prependPath(archive));
	}

	return true;
}

void Reader::deinit()
{
	PHYSFS_deinit();
}

bool Reader::prependPath(const std::string& path)
{
	int err = PHYSFS_mount(path.c_str(), NULL, 0);
	if (err == 0) {
		Log::fatal("Reader", Formatter("%: could not open archive: %")
				% path % PHYSFS_getLastError());
		return false;
	}

	pythonPrependPath(path);

	return true;
}

bool Reader::appendPath(const std::string& path)
{
	int err = PHYSFS_mount(path.c_str(), NULL, 1);
	if (err == 0) {
		Log::fatal("Reader", Formatter("%: could not open archive: %")
				% path % PHYSFS_getLastError());
		return false;
	}

	pythonRmPath(path);

	return true;
}

bool Reader::rmPath(const std::string& path)
{
	int err = PHYSFS_removeFromSearchPath(path.c_str());
	if (err == 0) {
		Log::err("Reader", Formatter("libphysfs: %: %")
				% path % PHYSFS_getLastError());
		return false;
	}

	return true;
}

bool Reader::resourceExists(const std::string& name)
{
	return PHYSFS_exists(name.c_str());
}

bool Reader::directoryExists(const std::string& name)
{
	return resourceExists(name) && PHYSFS_isDirectory(name.c_str());
}

bool Reader::fileExists(const std::string& name)
{
	return resourceExists(name) && !PHYSFS_isDirectory(name.c_str());
}

Gosu::Buffer* Reader::readBuffer(const std::string& name)
{
	Gosu::Buffer* buf = new Gosu::Buffer();

	if (readFromDisk(name, *buf)) {
		return buf;
	}
	else {
		delete buf;
		return NULL;
	}
}

std::string Reader::readString(const std::string& name)
{
	std::string str;
	return readFromDisk(name, str) ? str : "";
}

ImageRef Reader::getImage(const std::string& name)
{
	ImageRef existing = images.lifetimeRequest(name);
	if (existing)
		return existing;

	std::unique_ptr<Gosu::Buffer> buffer(readBuffer(name));
	if (!buffer)
		return ImageRef();

	ImageRef result(Image::create(buffer->data(), buffer->size()));
	if (!result)
		return ImageRef();

	images.lifetimePut(name, result);
	return result;
}

TiledImageRef Reader::getTiledImage(const std::string& name,
		int w, int h)
{
	TiledImageRef existing = tiles.momentaryRequest(name);
	if (existing)
		return existing;

	std::unique_ptr<Gosu::Buffer> buffer(readBuffer(name));
	if (!buffer)
		return TiledImageRef();

	TiledImageRef result(
		TiledImage::create(buffer->data(), buffer->size(), w, h)
	);
	if (!result)
		return TiledImageRef();

	tiles.momentaryPut(name, result);
	return result;
}

SampleRef Reader::getSample(const std::string& name)
{
	if (!conf.audioEnabled)
		return SampleRef();

	SampleRef existing = sounds.lifetimeRequest(name);
	if (existing)
		return existing;

	std::unique_ptr<Gosu::Buffer> buffer(readBuffer(name));
	if (!buffer)
		return SampleRef();
	SampleRef result(new Sound(new Gosu::Sample(buffer->frontReader())));

	sounds.lifetimePut(name, result);
	return result;
}

XMLRef Reader::getXMLDoc(const std::string& name,
                            const std::string& dtdFile)
{
	XMLRef existing = xmls.momentaryRequest(name);
	if (existing)
		return existing;

	XMLRef result(readXMLDoc(name, dtdFile));

	xmls.momentaryPut(name, result);
	return result;
}

std::string Reader::getText(const std::string& name)
{
	StringRef existing = texts.momentaryRequest(name);
	if (existing)
		return *existing.get();

	StringRef result(new std::string(readString(name)));

	texts.momentaryPut(name, result);
	return *result.get();
}

void Reader::garbageCollect()
{
	images.garbageCollect();
	tiles.garbageCollect();
	sounds.garbageCollect();
	// songs.garbageCollect();
	xmls.garbageCollect();
	texts.garbageCollect();
}

void exportReader()
{
	// FIXME: Broken with shift to singleton. No instantiated object to bind.
	// Fix will require a stub object.

#if 0
	using namespace boost::python;

	class_<Reader>("Reader", no_init)
		.def("resource_exists", &Reader::resourceExists)
		.def("run_python_script", &Reader::runPythonScript)
		.def("get_text", &Reader::getText);
	pythonSetGlobal("Reader");
#endif
}

<|endoftext|>


<commit_before>#include <queue>
#include <cstring>
#include <iostream>
#include <vector>
#include <string.h>
#include <unistd.h>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;
using namespace boost;

// Cases to test:
// cd Desktop&&ls -a
// cd Desktop&[ENTER] ls -a
// cd Desktop;ls -a
// cd Desktop;ls
// ls;ls;ls;ls
// cd Desktop & ls -a
// cd Desktop;
// ls;
// cd Desktop||ls -a;vim touch.cpp&&[ENTER] ls -a
// cd Desktop
// ; clear
// && ls
// || ls -a
// NOTE: cd will NOT be tested or used!
// Re-read specs if unsure!!

// prints user and machine info
void pcmd_prompt() {
    string login(getlogin());
    struct utsname hostname;
    uname(&hostname);
    string nodeName(hostname.nodename);
    cout << login << "@" << nodeName << "$ ";
}

// parses input into queue for execution
void parse_into_queue(queue<string> &l, const string &s) {
    char_separator<char> delim(" ", ";&|#") ;
    tokenizer<char_separator<char>> mytok(s, delim);

    for (auto i = mytok.begin(); i != mytok.end(); ++i) {
        l.push(*i);
    }
}

// for testing purposes
void print_queue (queue<string> q) {
    if (q.empty()) {
        return;
    }
    while (!q.empty()) {
        cout << "[" << q.front() << "] ";
        q.pop();
    }
    cout << endl;
}

// for testing purposes
void qinfo(queue<string> q) {
    cout << "Size: " << q.size() << endl;
    print_queue(q);
}

// reinitializes v with q until connector
void parse_connectors(vector<string> &v, queue<string> &q) {
    while (!q.empty() && q.front() != "&" && q.front() != "|" && q.front() != ";" && q.front() != "#")
    {
        v.push_back(q.front());
        q.pop();
    }
}

bool cmd_exit(vector<string> &v) {
    if (iequals(v.at(0), "exit")) {
        return true;
    }
    return false;
}

void to_arr_cstring(vector<string> &s, vector<char*> &cs) {
    for (auto i = 0; i != s.size(); ++i) {
        cs[i] = &s[i][0];
    }
}

// exec cmd and if success
bool begin_exec(vector<char*> &cs) {
    int status;
    int pid = fork();
    if (pid < 0) { // error
        perror("FORK");
        _exit(-1);
    }
    else if (pid == 0) { // child
        cout << "child process" << endl;
        if (-1 == execvp(cs[0], cs.data())) {
            perror("EXEC");
            _exit(-1);
        }
        _exit(0);
    }
    else if (pid > 0) { // parent
        if (-1 == wait(&status)) {
            perror("WAIT");
        }
        if (status != 0) {
            return false;
        }
    }
    return true;
}

// checks connector logic
bool connect_success(bool status, queue<string> &q) {
    if (!q.empty() && q.front() == "&") {
        q.pop();
        if (!q.empty() && q.front() == "&") {
            q.pop();
            if (!status) {
                return false;
            }
            else {
                return true;
            }
        }
        else if (q.empty()) {
            cout << "Syntax error: incorrect usage of \"&&\"" << endl;
            return false;
        }
        else {
            cout << "Syntax error: incorrect usage of \"&&\"" << endl;
            return false;
        }
    }
    else if (!q.empty() && q.front() == "|") {
        q.pop();
        if (!q.empty() && q.front() == "|") {
            q.pop();
            if (status) {
                return false;
            }
            else {
                return true;
            }
        }
        else if (q.empty()) {
            cout << "Syntax error: incorrect usage of \"||\"" << endl;
            return false;
        }
        else {
            cout << "Syntax error: incorrect usage of \"||\"" << endl;
            return false;
        }
    }
    else if (!q.empty() && q.front() == ";") {
        q.pop();
        return true;
    }
    else if (!q.empty() && q.front() == "#") {
        return false;
    }
    else if (!q.empty())  {
        cout << "Syntax error: " << q.front() <<  " is not a valid connector!" << endl;
        return false;
    }
    else {
        cout << "done" << endl;
        return false;
    }
}

int main(int argc, char* argv[]) {
    while (1) {
        string cmd_input;
        queue<string> list;
        pcmd_prompt();
        getline(cin, cmd_input);
        parse_into_queue(list, cmd_input);
        vector<string> cmd;
        while (!list.empty()) {
            qinfo(list); // debugger
            bool exec_success = false;
            if (list.front() == "#") {
                break;
            }
            parse_connectors(cmd, list);
            if (cmd.size() == 0) {
                cout << "Syntax error: formatting issue in command" << endl;
                break;
            }
            else if (cmd_exit(cmd)) {
                cout << "Exiting RShell" << endl;
                return 0;
            }
            else {
                vector<char*> cmd_cstring(cmd.size() + 1);
                to_arr_cstring(cmd, cmd_cstring);
                exec_success = begin_exec(cmd_cstring);
            }
            if (!connect_success(exec_success, list)) {
                break;
            }
            cmd.clear();
        }
    }
    return 0;
}
<commit_msg>segfault at pcmd_prompt when using script cmd<commit_after>#include <queue>
#include <cstring>
#include <iostream>
#include <vector>
#include <unistd.h>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;
using namespace boost;

// Cases to test:
// cd Desktop&&ls -a
// cd Desktop&[ENTER] ls -a
// cd Desktop;ls -a
// cd Desktop;ls
// ls;ls;ls;ls
// cd Desktop & ls -a
// cd Desktop;
// ls;
// cd Desktop||ls -a;vim touch.cpp&&[ENTER] ls -a
// cd Desktop
// ; clear
// && ls
// || ls -a
// NOTE: cd will NOT be tested or used!
// Re-read specs if unsure!!

// prints user and machine info
void pcmd_prompt() {
    string login;
    login = getlogin();
    if (login.empty()) {
        perror("GETLOGIN");
    }
    char host_arr[1024];
    host_arr[1023] = '\0';
    if (gethostname(host_arr, 1023) == -1) {
        perror("GETHOSTNAME");
    }
    cout << login << "@" << host_arr << "$ ";
}

// parses input into queue for execution
void parse_into_queue(queue<string> &l, const string &s) {
    char_separator<char> delim(" ", ";&|#") ;
    tokenizer<char_separator<char>> mytok(s, delim);

    for (auto i = mytok.begin(); i != mytok.end(); ++i) {
        l.push(*i);
    }
}

// for testing purposes
void print_queue (queue<string> q) {
    if (q.empty()) {
        return;
    }
    while (!q.empty()) {
        cout << "[" << q.front() << "] ";
        q.pop();
    }
    cout << endl;
}

// for testing purposes
void qinfo(queue<string> &q) {
    cout << "Size: " << q.size() << endl;
    print_queue(q);
}

// reinitializes v with q until connector
void parse_connectors(vector<string> &v, queue<string> &q) {
    while (!q.empty() && q.front() != "&" && q.front() != "|" && q.front() != ";" && q.front() != "#")
    {
        v.push_back(q.front());
        q.pop();
    }
}

bool cmd_exit(vector<string> &v) {
    if (iequals(v.at(0), "exit")) {
        return true;
    }
    return false;
}

void to_arr_cstring(vector<string> &s, vector<char*> &cs) {
    for (auto i = 0; i != s.size(); ++i) {
        cs[i] = &s[i][0];
    }
}

// exec cmd and if success
bool begin_exec(vector<char*> &cs) {
    int status;
    int pid = fork();
    if (pid < 0) { // error
        perror("FORK");
        _exit(-1);
    }
    else if (pid == 0) { // child
        cout << "child process" << endl;
        if (-1 == execvp(cs[0], cs.data())) {
            perror("EXEC");
            _exit(-1);
        }
        _exit(0);
    }
    else if (pid > 0) { // parent
        if (-1 == wait(&status)) {
            perror("WAIT");
        }
        if (status != 0) {
            return false;
        }
    }
    return true;
}

// checks connector logic
bool connect_success(bool status, queue<string> &q) {
    if (!q.empty() && q.front() == "&") {
        q.pop();
        if (!q.empty() && q.front() == "&") {
            q.pop();
            if (!status) {
                return false;
            }
            else {
                return true;
            }
        }
        else if (q.empty()) {
            cout << "Syntax error: incorrect usage of \"&&\"" << endl;
            return false;
        }
        else {
            cout << "Syntax error: incorrect usage of \"&&\"" << endl;
            return false;
        }
    }
    else if (!q.empty() && q.front() == "|") {
        q.pop();
        if (!q.empty() && q.front() == "|") {
            q.pop();
            if (status) {
                return false;
            }
            else {
                return true;
            }
        }
        else if (q.empty()) {
            cout << "Syntax error: incorrect usage of \"||\"" << endl;
            return false;
        }
        else {
            cout << "Syntax error: incorrect usage of \"||\"" << endl;
            return false;
        }
    }
    else if (!q.empty() && q.front() == ";") {
        q.pop();
        return true;
    }
    else if (!q.empty() && q.front() == "#") {
        return false;
    }
    else if (!q.empty())  {
        cout << "Syntax error: " << q.front() <<  " is not a valid connector!" << endl;
        return false;
    }
    else {
        // cout << "done" << endl;
        return false;
    }
}

int main(){
    while (1) {
        string cmd_input;
        queue<string> list;
        pcmd_prompt();
        getline(cin, cmd_input);
        parse_into_queue(list, cmd_input);
        vector<string> cmd;
        while (!list.empty()) {
            // qinfo(list); // debugger
            bool exec_success = false;
            if (list.front() == "#") {
                break;
            }
            parse_connectors(cmd, list);
            if (cmd.size() == 0) {
                cout << "Syntax error: formatting issue in command" << endl;
                break;
            }
            else if (cmd_exit(cmd)) {
                cout << "Exiting RShell" << endl;
                return 0;
            }
            else {
                vector<char*> cmd_cstring(cmd.size() + 1);
                to_arr_cstring(cmd, cmd_cstring);
                exec_success = begin_exec(cmd_cstring);
            }
            if (!connect_success(exec_success, list)) {
                break;
            }
            cmd.clear();
        }
    }
    return 0;
}
<|endoftext|>