<commit_before>#include <fcntl.h>
#include <algorithm>
#include <boost/tokenizer.hpp>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <pwd.h>

const std::multimap<std::string, int> DEFINED_OPS = { std::make_pair("&", 2),
                                                      std::make_pair("|", 2),
                                                      std::make_pair(";", 1),
                                                      std::make_pair("<", 1),
                                                      std::make_pair(">", 2) };

const std::vector<std::string> IMPLEMENTED_OPS{ "&&", "||", ";", "|", ">>", ">", "<" };

void PeekForRedirection(std::list<std::string> &input);
// handles what to do with finalized input state
void Execute(std::list<std::string> &input);

// assumes an operator in the first position of the input list
// extracts and pops it for determination of shell command flow
bool UseOperator(std::list<std::string> &input, bool prevcommandstate);

// used when an UseOperator returns false ie asdf && ls
// the first command is false and && must force a properly inputted second
// command
// to not execute. the command is extracted from the input list and nothing
// is done with it.
void DumpCommand(std::list<std::string> &input);

// assumes non operator in first position of the list
// tokens are extracted from the list until an operator or the end of
// the list is reached. the command is forked and execvp'd
bool UseCommand(std::list<std::string> &input);

// helper function that searched through global const IMPLEMENTED_OPS
// checks inputted string to see if it matches any within the vector
bool ContainsImplementedOp(std::string token);

// Returns true on >1 operators occuring back to back
// e.g. "&&&&&", ";;", etc.
bool FoundRepeat(const std::list<std::string> &input);

// assumes all like-operators have been merged together
// this finds 'strings' of operators that are too long
// rebuilds a std pair from the global const map DEFINED_OPS
// uses it as param to string::find on each element of the input list
// if its found that means it CONTAINS the operator. checking if it
// doesn't equal at this point means it's using operator symbols of an
// incorrect type.
bool InvalidRepeatOp(const std::list<std::string> &input,
                     std::pair<std::string, int> op);

// helper function that calls  with each element of global const
// DEFINED_OPS as 2nd parameter (the single instances of & | ;)
void CombineDefinedOps(std::list<std::string> &input);

// unused check for unimplemented operators
bool UnimplementedOp(const std::list<std::string> &input, std::string op);

// uses unix calls to get username and hostname
// returns 'shellish' formatted str ie usrname@host:~$
std::string UserHostInfo();

// output username@hostname$ and wait for command input
std::string Prompt();

// separates string by delimeters into vector elements
// if # is found it excludes it and anything beyond in the string
std::list<std::string> Split(const std::string &input);

// debugging inputlist outputter
void Output(std::list<std::string> &input);

// takes the input list and an operator character and merges all repeating
// instances of that character within the list
// operators in shell can use the same symbol a different amount of times to
// represent different things
// ie bitwise & vs and &&
// the delimiter method I used to separate the arguments in the first place only
// had single character delimiting available
// since the function is general it will:
// avoid having to create new handcrafted parses when more features have to be
// added
// make bug checking general and simple (is & implemented? is &&& implemented?
// if not theyre bugs)
void RebuildOps(std::list<std::string> &input, std::string op);

// does a search in input list for a known operator and checks if the next
// element is also
// a known operator.
bool FoundAdjOp(std::list<std::string> &input);

int main() {
  while (true) {
    auto cmd = Prompt();
    auto input = Split(cmd);
    CombineDefinedOps(input);
    Execute(input);
  }
}
std::string UserHostInfo() {
  auto userinfo = getpwuid(getuid());
  if (errno != 0) {
    perror("error in getpwuid");
    exit(1);
  }
  std::string login(userinfo->pw_name);

  char *rawhost = new char[100];
  auto status = gethostname(rawhost, 100);
  if (status == -1) {
    perror("gethostname failed");
  }

  std::string hostname(rawhost);
  delete[] rawhost;

  char *rawpwd = get_current_dir_name();
  if (rawpwd == NULL) {
    perror("get_current_dir_name returned NULL");
    exit(1);
  }
  std::string pwd(rawpwd);
  delete rawpwd;

  // handles /home/username -> ~/ shortcut
  std::string target = userinfo->pw_dir;
  if (pwd.find(target) == 0) {
    pwd.erase(0, target.size());
    pwd = "~" + pwd;
  }
  return login + "@" + hostname + ":" + pwd + "$ ";
}
std::string Prompt() {
  std::cout << "->" << UserHostInfo();
  std::string input;
  std::getline(std::cin, input);
  return input;
}
std::list<std::string> Split(const std::string &input) {
  using namespace boost;
  using namespace std;
  list<string> input_split;
  char_separator<char> sep(" ", "#&|;><");
  typedef tokenizer<char_separator<char> > tokener;

  tokener tokens(input, sep);

  // complexity increases if I handle # as bash does.
  for (const auto &t : tokens) {
    if (t == "#")
      break;
    input_split.push_back(t);
  }
  return input_split;
}
void Output(std::list<std::string> &input) {
  using namespace std;
  for (const auto &e : input)
    cout << e << endl;
}
void RebuildOps(std::list<std::string> &input, std::string op) {
  using namespace std;
  auto front = input.begin();
  auto back = input.end();
  while (front != back) {
    auto element = find(front, back, op);
    int count = 0;
    while (element != back) {
      if (*element == op) {
        element = input.erase(element);
        count++;
      } else {
        break;
      }
    }
    std::string tempstr = "";
    while (count--) {
      tempstr += op;
    }
    if (!tempstr.empty())
      front = input.insert(element, tempstr);
    front++;
  }
}
void CombineDefinedOps(std::list<std::string> &input) {
  for (const auto &op : DEFINED_OPS) {
    RebuildOps(input, op.first);
  }
}
bool FoundRepeat(const std::list<std::string> &input) {
  using namespace std;
  for (const auto &op : DEFINED_OPS) {
    if (InvalidRepeatOp(input, op)) {
      cout << "Invalid '" << op.first << "' usage found" << endl
           << "known operator used an invalid amount of consecutive" << endl
           << "times: e.g. '&&&' -> '&&' ?" << endl;
      return true;
    }
  }
  return false;
}
bool InvalidRepeatOp(const std::list<std::string> &input,
                     std::pair<std::string, int> pair) {
  std::string rebuilt_op = "";
  auto op_size = pair.second;
  while (op_size--) {
    rebuilt_op += pair.first;
  }
  auto front = input.begin();
  auto back = input.end();
  while (front != back) {
    auto itr = std::find_if(front, back, [&](std::string elem) {
      if (elem.find(rebuilt_op) == std::string::npos)
        return false;
      else
        return true;
    });
    if (itr == back)
      return false;

    else if (*itr != rebuilt_op)
      break;
    else {
      front = itr;
      front++;
    }
  }
  return true;
}
bool UnimplementedOp(const std::list<std::string> &input, std::string op) {
  using namespace std;
  auto itr = find(input.begin(), input.end(), op);
  if (itr == input.end()) {
    cout << "operator '" << op << "' is unimplemented" << endl;
    return false;
  }
  return true;
}
bool UseCommand(std::list<std::string> &input) {
  using namespace std;

  // Take list of strings, make copies of their c_strs, and put into vector
  // a vector of char* can be used as char** if used as such
  vector<char *> vectorcommand;
  while (!input.empty() && !ContainsImplementedOp(input.front())) {
    string transferstr = input.front();
    input.pop_front();
    char *cstrcopy = new char[transferstr.size() + 1];
    memcpy(cstrcopy, transferstr.c_str(), transferstr.size() + 1);
    cstrcopy[transferstr.size()] = 0;
    vectorcommand.push_back(cstrcopy);
  }
  vectorcommand.push_back(NULL);

  char **rawcommand = &vectorcommand[0];
  int exitvalue = 0;
  auto pid = fork();
  if (pid == -1) {
    perror("Error on fork");
    exit(1);
  }
  // child state
  else if (pid == 0) {
    //TODO search operators inplace of input vector?
    if (!input.empty()) {
      PeekForRedirection(input);
    }

    execvp(rawcommand[0], rawcommand);
    if (errno != 0) {
      perror("Error in execvp. Likely a nonexisting command?");
      exit(1);
    }
    for (size_t i = 0; i < vectorcommand.size(); i++)
      delete[] rawcommand[i];
  }
  // parent
  else {
    int status;
    auto wait_val = wait(&status);
    if (wait_val == -1) {
      perror("Error on waiting for child process to finish");
      exit(1);
    }
    exitvalue = WEXITSTATUS(status);
    for (size_t i = 0; i < vectorcommand.size(); i++)
      delete[] rawcommand[i];
  }
  if (exitvalue == 0)
    return true;
  else
    return false;
}
bool ContainsImplementedOp(std::string token) {
  auto match = find(IMPLEMENTED_OPS.begin(), IMPLEMENTED_OPS.end(), token);
  if (match != IMPLEMENTED_OPS.end())
    return true;
  return false;
}
bool UseOperator(std::list<std::string> &input, bool prevcommandstate) {
  using namespace std;
  if (input.empty())
    return false;
  string op = input.front();
  input.pop_front();
  if (prevcommandstate == true) {
    if (op == ";")
      return true;
    else if (op == "&&")
      return true;
    else if (op == "||")
      return false;
  } else {
    if (op == ";")
      return true;
    else if (op == "&&")
      return false;
    else if (op == "||")
      return true;
  }
  // proper input ensures we never get down here, so im killing warning message
  // fixing this 'properly' would make it annoying to add more operators later
  //TODO make that not wrong^
  return true;
}
void Execute(std::list<std::string> &input) {
  if (input.empty() || FoundRepeat(input) || FoundAdjOp(input))
    return;
  bool cmdstate = true;
  while (!input.empty()) {
    if (input.front() == "exit")
      exit(0);
    if (cmdstate) {
      cmdstate = UseCommand(input);
      }
    else
      DumpCommand(input);
    cmdstate = UseOperator(input, cmdstate);
  }
}
void DumpCommand(std::list<std::string> &input) {
  while (!input.empty() && !ContainsImplementedOp(input.front())) {
    input.pop_front();
  }
}
bool FoundAdjOp(std::list<std::string> &input) {
  using namespace std;
  for (const auto &op : IMPLEMENTED_OPS) {
    auto element = input.begin();
    auto front = input.begin();
    auto next = input.begin();

    auto back = input.end();
    while (element != input.end()) {
      element = find(front, back, op);
      if (element == input.end())
        continue;
      next = element;
      next++;
      if (next == input.end())
        continue;
      else if (find(IMPLEMENTED_OPS.begin(), IMPLEMENTED_OPS.end(), *next) !=
               IMPLEMENTED_OPS.end()) {
        cout << "Two operators are adjacent to each other e.g. '&&;' ?" << endl;
        return true;
      }
      front = next;
      front++;
    }
  }
  return false;
}
void PeekForRedirection(std::list<std::string> &input) {
  std::string op = input.front();
  input.pop_front();
  std::string target = input.front();
  input.pop_front();
  int fd = -1;
  if (op == ">") {
    fd = open(target.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
    close(1);
    dup2(fd, 1);
  }
  else if (op == ">>") {
    fd = open(target.c_str(), O_RDWR | O_CREAT | O_APPEND, 0644);
    close(1);
    dup2(fd, 1);
  }
  else if (op == "<") {
    fd = open(target.c_str(), O_RDONLY, 0644);
    close(0);
    dup2(fd, 0);
  }
}
<commit_msg>reconstructed PeakForRedirect to fix a bug involving editing &input inside a fork<commit_after>#include <fcntl.h>
#include <algorithm>
#include <boost/tokenizer.hpp>
#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <sstream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <vector>
#include <pwd.h>

enum class RedirectionTypes {
  OUT_TRUNC,
  OUT_APPEND,
  IN,
  PIPE,
  NONE
};

void Redirect(RedirectionTypes r, std::string target);
const std::multimap<std::string, int> DEFINED_OPS = { std::make_pair("&", 2),
                                                      std::make_pair("|", 2),
                                                      std::make_pair(";", 1),
                                                      std::make_pair("<", 1),
                                                      std::make_pair(">", 2) };

const std::vector<std::string> IMPLEMENTED_OPS{ "&&", "||", ";", "|", ">>", ">", "<" };

RedirectionTypes PeekForRedirection(std::list<std::string> input);
// handles what to do with finalized input state
void Execute(std::list<std::string> &input);

// assumes an operator in the first position of the input list
// extracts and pops it for determination of shell command flow
bool UseOperator(std::list<std::string> &input, bool prevcommandstate);

// used when an UseOperator returns false ie asdf && ls
// the first command is false and && must force a properly inputted second
// command
// to not execute. the command is extracted from the input list and nothing
// is done with it.
void DumpCommand(std::list<std::string> &input);

// assumes non operator in first position of the list
// tokens are extracted from the list until an operator or the end of
// the list is reached. the command is forked and execvp'd
bool UseCommand(std::list<std::string> &input);

// helper function that searched through global const IMPLEMENTED_OPS
// checks inputted string to see if it matches any within the vector
bool ContainsImplementedOp(std::string token);

// Returns true on >1 operators occuring back to back
// e.g. "&&&&&", ";;", etc.
bool FoundRepeat(const std::list<std::string> &input);

// assumes all like-operators have been merged together
// this finds 'strings' of operators that are too long
// rebuilds a std pair from the global const map DEFINED_OPS
// uses it as param to string::find on each element of the input list
// if its found that means it CONTAINS the operator. checking if it
// doesn't equal at this point means it's using operator symbols of an
// incorrect type.
bool InvalidRepeatOp(const std::list<std::string> &input,
                     std::pair<std::string, int> op);

// helper function that calls  with each element of global const
// DEFINED_OPS as 2nd parameter (the single instances of & | ;)
void CombineDefinedOps(std::list<std::string> &input);

// unused check for unimplemented operators
bool UnimplementedOp(const std::list<std::string> &input, std::string op);

// uses unix calls to get username and hostname
// returns 'shellish' formatted str ie usrname@host:~$
std::string UserHostInfo();

// output username@hostname$ and wait for command input
std::string Prompt();

// separates string by delimeters into vector elements
// if # is found it excludes it and anything beyond in the string
std::list<std::string> Split(const std::string &input);

// debugging inputlist outputter
void Output(std::list<std::string> &input);

// takes the input list and an operator character and merges all repeating
// instances of that character within the list
// operators in shell can use the same symbol a different amount of times to
// represent different things
// ie bitwise & vs and &&
// the delimiter method I used to separate the arguments in the first place only
// had single character delimiting available
// since the function is general it will:
// avoid having to create new handcrafted parses when more features have to be
// added
// make bug checking general and simple (is & implemented? is &&& implemented?
// if not theyre bugs)
void RebuildOps(std::list<std::string> &input, std::string op);

// does a search in input list for a known operator and checks if the next
// element is also
// a known operator.
bool FoundAdjOp(std::list<std::string> &input);

int main() {
  while (true) {
    auto cmd = Prompt();
    auto input = Split(cmd);
    CombineDefinedOps(input);
    Execute(input);
  }
}
std::string UserHostInfo() {
  auto userinfo = getpwuid(getuid());
  if (errno != 0) {
    perror("error in getpwuid");
    exit(1);
  }
  std::string login(userinfo->pw_name);

  char *rawhost = new char[100];
  auto status = gethostname(rawhost, 100);
  if (status == -1) {
    perror("gethostname failed");
  }

  std::string hostname(rawhost);
  delete[] rawhost;

  char *rawpwd = get_current_dir_name();
  if (rawpwd == NULL) {
    perror("get_current_dir_name returned NULL");
    exit(1);
  }
  std::string pwd(rawpwd);
  delete rawpwd;

  // handles /home/username -> ~/ shortcut
  std::string target = userinfo->pw_dir;
  if (pwd.find(target) == 0) {
    pwd.erase(0, target.size());
    pwd = "~" + pwd;
  }
  return login + "@" + hostname + ":" + pwd + "$ ";
}
std::string Prompt() {
  std::cout << "->" << UserHostInfo();
  std::string input;
  std::getline(std::cin, input);
  return input;
}
std::list<std::string> Split(const std::string &input) {
  using namespace boost;
  using namespace std;
  list<string> input_split;
  char_separator<char> sep(" ", "#&|;><");
  typedef tokenizer<char_separator<char> > tokener;

  tokener tokens(input, sep);

  // complexity increases if I handle # as bash does.
  for (const auto &t : tokens) {
    if (t == "#")
      break;
    input_split.push_back(t);
  }
  return input_split;
}
void Output(std::list<std::string> &input) {
  using namespace std;
  for (const auto &e : input)
    cout << e << endl;
}
void RebuildOps(std::list<std::string> &input, std::string op) {
  using namespace std;
  auto front = input.begin();
  auto back = input.end();
  while (front != back) {
    auto element = find(front, back, op);
    int count = 0;
    while (element != back) {
      if (*element == op) {
        element = input.erase(element);
        count++;
      } else {
        break;
      }
    }
    std::string tempstr = "";
    while (count--) {
      tempstr += op;
    }
    if (!tempstr.empty())
      front = input.insert(element, tempstr);
    front++;
  }
}
void CombineDefinedOps(std::list<std::string> &input) {
  for (const auto &op : DEFINED_OPS) {
    RebuildOps(input, op.first);
  }
}
bool FoundRepeat(const std::list<std::string> &input) {
  using namespace std;
  for (const auto &op : DEFINED_OPS) {
    if (InvalidRepeatOp(input, op)) {
      cout << "Invalid '" << op.first << "' usage found" << endl
           << "known operator used an invalid amount of consecutive" << endl
           << "times: e.g. '&&&' -> '&&' ?" << endl;
      return true;
    }
  }
  return false;
}
bool InvalidRepeatOp(const std::list<std::string> &input,
                     std::pair<std::string, int> pair) {
  std::string rebuilt_op = "";
  auto op_size = pair.second;
  while (op_size--) {
    rebuilt_op += pair.first;
  }
  auto front = input.begin();
  auto back = input.end();
  while (front != back) {
    auto itr = std::find_if(front, back, [&](std::string elem) {
      if (elem.find(rebuilt_op) == std::string::npos)
        return false;
      else
        return true;
    });
    if (itr == back)
      return false;

    else if (*itr != rebuilt_op)
      break;
    else {
      front = itr;
      front++;
    }
  }
  return true;
}
bool UnimplementedOp(const std::list<std::string> &input, std::string op) {
  using namespace std;
  auto itr = find(input.begin(), input.end(), op);
  if (itr == input.end()) {
    cout << "operator '" << op << "' is unimplemented" << endl;
    return false;
  }
  return true;
}
bool UseCommand(std::list<std::string> &input) {
  using namespace std;

  // Take list of strings, make copies of their c_strs, and put into vector
  // a vector of char* can be used as char** if used as such
  vector<char *> vectorcommand;
  while (!input.empty() && !ContainsImplementedOp(input.front())) {
    string transferstr = input.front();
    input.pop_front();
    char *cstrcopy = new char[transferstr.size() + 1];
    memcpy(cstrcopy, transferstr.c_str(), transferstr.size() + 1);
    cstrcopy[transferstr.size()] = 0;
    vectorcommand.push_back(cstrcopy);
  }
  vectorcommand.push_back(NULL);

  char **rawcommand = &vectorcommand[0];
  int exitvalue = 0;

  //look ahead for redirection operators
  RedirectionTypes r = RedirectionTypes::NONE;
  if (!input.empty()) {
    r = PeekForRedirection(input);
  }

  //drop the operator + RHS of the redirection from input list
  //if a redir op is found. has to be done from outside the fork
  //or state isn't preserved in parent process
  std::string target;
  if (r != RedirectionTypes::NONE) {
    input.pop_front();
    target = input.front();
    input.pop_front();
  }
  auto pid = fork();
  if (pid == -1) {
    perror("Error on fork");
    exit(1);
  }
  // child state
  else if (pid == 0) {
    Redirect(r, target);
    execvp(rawcommand[0], rawcommand);
    if (errno != 0) {
      perror("Error in execvp. Likely a nonexisting command?");
      exit(1);
    }
    for (size_t i = 0; i < vectorcommand.size(); i++)
      delete[] rawcommand[i];
  }
  // parent
  else {
    int status;
    auto wait_val = wait(&status);
    if (wait_val == -1) {
      perror("Error on waiting for child process to finish");
      exit(1);
    }
    exitvalue = WEXITSTATUS(status);
    for (size_t i = 0; i < vectorcommand.size(); i++)
      delete[] rawcommand[i];
  }
  if (exitvalue == 0)
    return true;
  else
    return false;
}
bool ContainsImplementedOp(std::string token) {
  auto match = find(IMPLEMENTED_OPS.begin(), IMPLEMENTED_OPS.end(), token);
  if (match != IMPLEMENTED_OPS.end())
    return true;
  return false;
}
bool UseOperator(std::list<std::string> &input, bool prevcommandstate) {
  using namespace std;
  if (input.empty())
    return false;
  string op = input.front();
  input.pop_front();
  if (prevcommandstate == true) {
    if (op == ";")
      return true;
    else if (op == "&&")
      return true;
    else if (op == "||")
      return false;
  } else {
    if (op == ";")
      return true;
    else if (op == "&&")
      return false;
    else if (op == "||")
      return true;
  }
  // proper input ensures we never get down here, so im killing warning message
  // fixing this 'properly' would make it annoying to add more operators later
  //TODO make that not wrong^
  return true;
}
void Execute(std::list<std::string> &input) {
  if (input.empty() || FoundRepeat(input) || FoundAdjOp(input))
    return;
  bool cmdstate = true;
  while (!input.empty()) {
    if (input.front() == "exit")
      exit(0);
    if (cmdstate) {
      cmdstate = UseCommand(input);
      }
    else
      DumpCommand(input);
    cmdstate = UseOperator(input, cmdstate);
  }
}
void DumpCommand(std::list<std::string> &input) {
  while (!input.empty() && !ContainsImplementedOp(input.front())) {
    input.pop_front();
  }
}
bool FoundAdjOp(std::list<std::string> &input) {
  using namespace std;
  for (const auto &op : IMPLEMENTED_OPS) {
    auto element = input.begin();
    auto front = input.begin();
    auto next = input.begin();

    auto back = input.end();
    while (element != input.end()) {
      element = find(front, back, op);
      if (element == input.end())
        continue;
      next = element;
      next++;
      if (next == input.end())
        continue;
      else if (find(IMPLEMENTED_OPS.begin(), IMPLEMENTED_OPS.end(), *next) !=
               IMPLEMENTED_OPS.end()) {
        cout << "Two operators are adjacent to each other e.g. '&&;' ?" << endl;
        return true;
      }
      front = next;
      front++;
    }
  }
  return false;
}
RedirectionTypes PeekForRedirection(std::list<std::string> input) {
  std::string op = input.front();
  input.pop_front();
  std::string target = input.front();
  input.pop_front();
  if (op == ">") {
    return RedirectionTypes::OUT_TRUNC;
  }
  else if (op == ">>") {
    return RedirectionTypes::OUT_APPEND;
  }
  else if (op == "<") {
    return RedirectionTypes::IN;
  }

  return RedirectionTypes::NONE;
}
void Redirect(RedirectionTypes r, std::string target) {
  int fd = -1;
  if (r == RedirectionTypes::OUT_TRUNC) {
    fd = open(target.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
    close(1);
    dup2(fd, 1);
  }
  else if (r == RedirectionTypes::OUT_APPEND) {
    fd = open(target.c_str(), O_RDWR | O_CREAT | O_APPEND, 0644);
    close(1);
    dup2(fd, 1);
  }
 else if (r == RedirectionTypes::IN) {
    fd = open(target.c_str(), O_RDONLY, 0644);
    close(0);
    dup2(fd, 0);
  }
}
<|endoftext|>


<commit_before>/*
 * Alexander Puh
 * I shan't cheat.
 * 10/27/2015
 * CS100 - Rshell - rshell.cpp
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <cstring>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>

using namespace std;
using namespace boost;

char SPACE = ' ';
string input;
char ** cmds;

void print_vec(vector<string>& v){
    for(unsigned i = 0; i < v.size(); i++)
	cout << v[i] << endl;
}

//Tokenizes a string into passed in vector
void mystrtok(string& input, vector<string>&all){
    //cout << "I'm currently toking..." << endl;
    //CODE TO LOOK FOR CONNECTORS like ; || && and also comments like #
    //FIRST REMOVE ALL COMMENTS BUT CHECK TO SEE THAT ONE MAY POTENTIALLY EXIST FIRST
    if(input.find('#') != string::npos){
        int sharp = input.find('#');
	    //cuts off the part with sharp to the end;
	    input = input.substr(0, sharp-1);
    }
    //cout << "Dont with comments." << endl;
    //CHECKS FOR CONNECTORS AND ADD SPACES IF NECESSARY
    //lko for last known occurence
    unsigned lko = 0;
    while(input.find("||", lko)!= string::npos){
	lko = input.find("||", lko);
        if((lko != 0) && (input.at(lko-1) != SPACE)){
	    input = input.substr(0, lko) + SPACE + input.substr(lko);
	    lko+=3;
	    if(lko < input.size() && input.at(lko) != SPACE){
	    	input = input.substr(0, lko) + SPACE + input.substr(lko);
	    	lko++;
		}
	}
	else if(lko < input.size() && input.at(lko+2) != SPACE){
	    input = input.substr(0, lko+2) + SPACE + input.substr(lko+2);
	    lko+=3;
	}
	else
	    lko+=2;
    //cout << input << endl;
    //string yourmother;
    //cin >> yourmother;
    }
    lko = 0;
    while(input.find("&&", lko)!= string::npos){
	lko = input.find("&&", lko);
        if((lko != 0) && (input.at(lko-1) != SPACE)){
	    input = input.substr(0, lko) + SPACE + input.substr(lko);
	    lko+=3;
	    if(lko < input.size() && input.at(lko) != SPACE){
	    	input = input.substr(0, lko) + SPACE + input.substr(lko);
	    	lko++;
		}
	}
	else if(lko < input.size() && input.at(lko+2) != SPACE){
	    input = input.substr(0, lko+2) + SPACE + input.substr(lko+2);
	    lko+=3;
	}
	else
	    lko+=2;
    }

    //cout << "Done with ||" << endl;

    lko = 0;
    while( input.find(";", lko)!= string::npos){
    	lko = input.find(";", lko);
	//Add space if theres not already a space before
        if((lko != 0) && (input.at(lko-1) != SPACE)){
	    input = input.substr(0, lko) + SPACE + input.substr(lko);
	    lko+=2;
	    if(lko < input.size() && lko + 1 < input.size() && input.at(lko) != SPACE){
	    	input = input.substr(0, lko) + SPACE + input.substr(lko);
	    	lko++;
		}
	}
	else if(lko < input.size() && lko+1 < input.size() && input.at(lko+1) != SPACE){
	    input = input.substr(0, lko+1) + SPACE + input.substr(lko+1);
	    lko+=2;
	}
	else
	    lko+=1;
    }

    //Now we can tokenize properly
    split(all, input, is_any_of(" "), token_compress_on);
}

bool promptUser(){

    //cout << "prompted user" << endl;
    cout << "$ ";
    getline(cin, input);

    //cout << "Done getting input" << endl;
    while(input != "quit"){
	//Holds all commands;
	vector<string>allCmds;
	if(input.find_first_not_of(' ')!=string::npos && input!="quit" && input.at(0)!='#'){
	    mystrtok(input, allCmds);
	//////////////TEST
	//		print_vec(allCmds);
	/////////////

	//cout << "Done toking" << endl;

	    //PHASE 2: TRACK WHICH ARE EXECUTED
	    //AVAILABLE: begin, ;, &&, || or unknown
	    //c_c for current connector.
	    string c_c = "begin";
            //Beginning of next command sequence.
	    //unsigned begin_next = 0;

	    //Assume success unless failed by Execvp
	    bool succeeded = 1;
	    bool init = 1;
	    //Checking to see if this is the first cmd or if this is a cmd following &&
	    //and a previously successful cmd OR if cmd followed by || after a failed cmd
	    //previously OR if cmd is followed by ; which allows immediate execution.

	    //c_p for connector place, which runs until a connector is hit
	    unsigned c_p = 0, c_s = 0;
	    //c_s for command start, where the current  command seq starts

	    unsigned sz;

	    while(c_p < allCmds.size()){
	    	//Calc the index of the next connector first to get end of command seq
	    	for(; c_p < allCmds.size()
	            && (allCmds[c_p] != ";" && allCmds[c_p] != "&&" && allCmds[c_p] != "||"); c_p++);

		//cout << "What is c_p" << c_p << endl;
		//cout << "allCmds" << allCmds.size() << endl;
		//cout << "Whats at c_c" << c_c << endl;

		unsigned next_inx = 0;
	        if(init || c_c == ";" || c_c == "end" || (succeeded && c_c == "&&") || (!succeeded && c_c == "||")){
		    //Set new connector
		    if(c_p < allCmds.size())
		        c_c = allCmds[c_p];
		    else
		        c_c = "end";

		    //connector found
	            init = 0;
		    //if(c_p != allCmds.size()){
		    sz = c_p - c_s;

	   	    cmds = new char * [sz + 1];
		    //From first part of cmd to last part of cmd
		    for(; c_s < c_p; c_s++){
			//Create a new c_string
		        cmds[next_inx] = new char[allCmds[c_s].size() + 1];
		        copy(allCmds.at(c_s).begin(), allCmds.at(c_s).end(), cmds[next_inx]);
		        //strcpy(cmds[c_s], allCmds[c_s].c_str());
			cmds[next_inx][allCmds[c_s].size()] = '\0';
			//strcat(cmds[c_s], "\0");
			next_inx++;
		    }
		    //cout << "Got here1"<< endl;
		    cmds[next_inx] = NULL; //APPEND NULL
		    //New c_c value is the c_p we computed earlier in the 2nd for loop above.
		    if(c_p < allCmds.size()){
		        //set c_p = sz if it is not after end (sz is one after the connector)
		        //set new c_s as sz, again one after connector
		        c_p++;
		        c_s = c_p;
		    }
		    //cout << "Got here" << endl;

	            //PHASE 3: EXECUTION AND TRACK WHICH SUCCEEDED

		    ////////////////TEST
		    //CODE TO "CONVERT" VECTOR TO POINTER TO A CSTRING ARR
/*				cmds = new char * [allCmds.size() + 1];

				for(unsigned i = 0; i < allCmds.size(); i++){
					cmds[i] = new char[(allCmds.at(i)).size() + 1];
			        	copy(allCmds.at(i).begin(), allCmds.at(i).end(), cmds[i]);
					cmds[i][allCmds.at(i).size()] = '\0';
				}
		        	//REMEMBER TO APPEND NULL AT END OF VECTOR
		        	cmds[allCmds.size()] = NULL;
		    //////////////////////////////////////
*/
		    //EXECUTE
		    pid_t c_pid;
	   	    int status = 0;
		    c_pid = fork();

	            if( c_pid < 0 ){
		        perror("fork failed");
		        exit(1);
		    }
		    else if (c_pid == 0){
		        int exec = execvp(cmds[0], cmds);
		        if(exec < 0){
			    perror("Execvp failed");
			    exit(1);
			    succeeded = 0;
		        }
			else
		            succeeded = 1;
	            }
		    else if (c_pid > 0){
		    //cout << "Stuck in limbo" << endl;
		        waitpid(-1, &status, 0);
		    }

		    //AFTER EXECUTION DELETE EVERYTHING
		    for(unsigned i = 0; i < sz; i++){
		        cmds[i] = NULL;
		    }
		    //delete[] cmds;
	    	}
		else{
		    c_p++;
		    c_s = c_p;
		}

        }

	}
	fflush(stdout);
	//RESET ALL VALUES
	input="quit";
	cout << "$ ";
	cin.clear();
	getline(cin, input);
    }
    return false;
}

int main(int argc, char * argv[]){
	while(promptUser());
	return 0;
}

<commit_msg>Does not work well<commit_after>/*
 * Alexander Puh
 * I shan't cheat.
 * 10/27/2015
 * CS100 - Rshell - rshell.cpp
 */

#include <sys/ioctl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/wait.h>
#include <iostream>
#include <string>
#include <cstring>
#include <boost/tokenizer.hpp>
#include <boost/algorithm/string.hpp>

#include <dirent.h>
#include <errno.h>
#include <algorithm>
#include <stdio.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>

using namespace std;
using namespace boost;

//Tracks if user uses test
bool test_mode = 0;
string test_flag = "";

char SPACE = ' ';
string input;
char ** cmds;

void print_vec(vector<string>& v){
    cout << "Printing.... " << endl;
    for(unsigned i = 0; i < v.size(); i++)
	cout << v[i] << " ";
    cout << endl;
}

//Use to check if there is an even (modable by 2)
//number of quotes, parens, braces, brackets
bool all_occur_closed(string in, string s){
    int occurence = 0;
    string clos = "";

    if(s == "\""){
	int cnt = 0;
	while(in.find(s, occurence) != string::npos){
	    cnt++;
	    occurence = in.find(s, occurence) + 1;
	}
	if(cnt%2==0)
	    return true;
	return false;
    }
    else if(s == "["){
	clos = "]";
    }
    else if(s == "{"){
 	clos = "}";
    }
    else if(s == "("){
	clos = ")";
    }

    int cnt_op = 0, cnt_ed = 0;
    while(in.find(s, occurence) != string::npos){
        cnt_op++;
	occurence = in.find(s, occurence) + 1;
    }
    occurence = 0;
    while(in.find(clos, occurence) != string::npos){
	cnt_ed++;
	occurence = in.find(clos, occurence) + 1;
    }
    if(cnt_op == cnt_ed)
	return 1;
    return 0;
}

//Tokenizes a string into passed in vector
void mystrtok(string& input, vector<string>&all){
    //Check for occurences of {, [, (, " that all are closed
    if(!all_occur_closed(input, "{") || !all_occur_closed(input, "\"") ||
       !all_occur_closed(input, "[") || !all_occur_closed(input, "(")){
	cout << "Error: Quotes/Parens/Bracket/Braces not closed." << endl;
	input = "";
	return;
    }

    //cout << "I'm currently toking..." << endl;
    //CODE TO LOOK FOR CONNECTORS like ; || && and also comments like #
    //FIRST REMOVE ALL COMMENTS BUT CHECK TO SEE THAT ONE MAY POTENTIALLY EXIST FIRST
    if(input.find('#') != string::npos){
        int sharp = input.find('#');
	    //cuts off the part with sharp to the end;
	    input = input.substr(0, sharp-1);
    }
    //cout << "Dont with comments." << endl;
    //CHECKS FOR CONNECTORS AND ADD SPACES IF NECESSARY
    //lko for last known occurence
    unsigned lko = 0;
    while(input.find("||", lko)!= string::npos){
	lko = input.find("||", lko);
        if((lko != 0) && (input.at(lko-1) != SPACE)){
	    input = input.substr(0, lko) + SPACE + input.substr(lko);
	    lko+=3;
	    if(lko < input.size() && input.at(lko) != SPACE){
	    	input = input.substr(0, lko) + SPACE + input.substr(lko);
	    	lko++;
		}
	}
	else if(lko < input.size() && input.at(lko+2) != SPACE){
	    input = input.substr(0, lko+2) + SPACE + input.substr(lko+2);
	    lko+=3;
	}
	else
	    lko+=2;
    //cout << input << endl;
    //string yourmother;
    //cin >> yourmother;
    }
    lko = 0;
    while(input.find("&&", lko)!= string::npos){
	lko = input.find("&&", lko);
        if((lko != 0) && (input.at(lko-1) != SPACE)){
	    input = input.substr(0, lko) + SPACE + input.substr(lko);
	    lko+=3;
	    if(lko < input.size() && input.at(lko) != SPACE){
	    	input = input.substr(0, lko) + SPACE + input.substr(lko);
	    	lko++;
		}
	}
	else if(lko < input.size() && input.at(lko+2) != SPACE){
	    input = input.substr(0, lko+2) + SPACE + input.substr(lko+2);
	    lko+=3;
	}
	else
	    lko+=2;
    }

    //cout << "Done with ||" << endl;

    lko = 0;
    while( input.find(";", lko)!= string::npos){
    	lko = input.find(";", lko);
	//Add space if theres not already a space before
        if((lko != 0) && (input.at(lko-1) != SPACE)){
	    input = input.substr(0, lko) + SPACE + input.substr(lko);
	    lko+=2;
	    if(lko < input.size() && lko + 1 < input.size() && input.at(lko) != SPACE){
	    	input = input.substr(0, lko) + SPACE + input.substr(lko);
	    	lko++;
		}
	}
	else if(lko < input.size() && lko+1 < input.size() && input.at(lko+1) != SPACE){
	    input = input.substr(0, lko+1) + SPACE + input.substr(lko+1);
	    lko+=2;
	}
	else
	    lko+=1;
    }

    //CHECK FOR [ ] AND CONVERT TO TEST
    //CHECK FOR " ". IF EVEN NUMBER OF " WE GO ON, IF ODD RETURN ERROR

    //Now we can tokenize properly
    split(all, input, is_any_of(" "), token_compress_on);
}

bool promptUser(){

    //cout << "prompted user" << endl;
    cout << "$ ";
    getline(cin, input);

    //cout << "Done getting input" << endl;
    while(input != "quit"){
	//Holds all commands;
	vector<string>allCmds;
	if(input.find_first_not_of(' ')!=string::npos && input!="quit" && input.at(0)!='#'){
	    mystrtok(input, allCmds);
	//////////////TEST
		print_vec(allCmds);
	/////////////

	//cout << "Done toking" << endl;

	    //PHASE 2: TRACK WHICH ARE EXECUTED
	    //AVAILABLE: begin, ;, &&, || or unknown
	    //c_c for current connector.
	    string c_c = "begin";
            //Beginning of next command sequence.
	    //unsigned begin_next = 0;

	    //Assume success unless failed by Execvp
	    bool succeeded = 1;
	    bool init = 1;
	    //Checking to see if this is the first cmd or if this is a cmd following &&
	    //and a previously successful cmd OR if cmd followed by || after a failed cmd
	    //previously OR if cmd is followed by ; which allows immediate execution.

	    //c_p for connector place, which runs until a connector is hit
	    unsigned c_p = 0, c_s = 0;
	    //c_s for command start, where the current  command seq starts

	    unsigned sz;

	    while(c_p < allCmds.size()){
	    	//Calc the index of the next connector first to get end of command seq
	    	for(; c_p < allCmds.size()
	            && (allCmds[c_p] != ";" && allCmds[c_p] != "&&" && allCmds[c_p] != "||"); c_p++);

		//cout << "What is c_p" << c_p << endl;
		//cout << "allCmds" << allCmds.size() << endl;
		//cout << "Whats at c_c" << c_c << endl;
cout << "    DEBUG The first cmd is: " << allCmds[c_s] << endl;
cout << "    DEBUG What is success: " << succeeded << endl;
cout << "    DEBUG What is next conn: " << c_c << endl;
cout << "    DEBUG What is init " << init << endl;
cout << "    DEBUG " << (!succeeded && c_c == "||") << endl;
cout << "    DEBUG What is success and c_c &&:" << (succeeded && c_c == "&&") << endl;
cout << "    DEBUG if statement evaluates: " << ((init || c_c == ";" || c_c == "end" || (succeeded && c_c == "&&") ||
		   (!succeeded && c_c == "||")) && allCmds[c_s] != "test") << endl;

		unsigned next_inx = 0;
	        if((init || c_c == ";" || c_c == "end" || (succeeded && c_c == "&&") ||
		   (!succeeded && c_c == "||")) && allCmds[c_s] != "test"){
cout << "    DEBUG WHY ARE WE IN HERE? " << endl;
		    //Set new connector
		    if(c_p < allCmds.size())
		        c_c = allCmds[c_p];
		    else
		        c_c = "end";

		    //connector found
	            init = 0;
		    //if(c_p != allCmds.size()){
		    sz = c_p - c_s;

	   	    cmds = new char * [sz + 1];
		    //From first part of cmd to last part of cmd
		    for(; c_s < c_p; c_s++){
			//Create a new c_string
		        cmds[next_inx] = new char[allCmds[c_s].size() + 1];
		        copy(allCmds.at(c_s).begin(), allCmds.at(c_s).end(), cmds[next_inx]);
		        //strcpy(cmds[c_s], allCmds[c_s].c_str());
			cmds[next_inx][allCmds[c_s].size()] = '\0';
			//strcat(cmds[c_s], "\0");
			next_inx++;
		    }
		    //cout << "Got here1"<< endl;
		    cmds[next_inx] = NULL; //APPEND NULL
		    //New c_c value is the c_p we computed earlier in the 2nd for loop above.
		    if(c_p < allCmds.size()){
		        //set c_p = sz if it is not after end (sz is one after the connector)
		        //set new c_s as sz, again one after connector
		        c_p++;
		        c_s = c_p;
		    }
		    //cout << "Got here" << endl;

	            //PHASE 3: EXECUTION AND TRACK WHICH SUCCEEDED

		    ////////////////TEST
		    //CODE TO "CONVERT" VECTOR TO POINTER TO A CSTRING ARR
/*				cmds = new char * [allCmds.size() + 1];

				for(unsigned i = 0; i < allCmds.size(); i++){
					cmds[i] = new char[(allCmds.at(i)).size() + 1];
			        	copy(allCmds.at(i).begin(), allCmds.at(i).end(), cmds[i]);
					cmds[i][allCmds.at(i).size()] = '\0';
				}
		        	//REMEMBER TO APPEND NULL AT END OF VECTOR
		        	cmds[allCmds.size()] = NULL;
		    //////////////////////////////////////
*/
		    //EXECUTE
		    pid_t c_pid;
	   	    int status = 0;
		    c_pid = fork();

	            if( c_pid < 0 ){
		        perror("fork failed");
		        exit(1);
		    }
		    else if (c_pid == 0){
		        int exec = execvp(cmds[0], cmds);
		        if(exec < 0){
			    perror("Execvp failed");
			    exit(1);
			    succeeded = 0;
		        }
			else
		            succeeded = 1;
	            }
		    else if (c_pid > 0){
		    //cout << "Stuck in limbo" << endl;
		        waitpid(-1, &status, 0);
		    }

		    //AFTER EXECUTION DELETE EVERYTHING
		    for(unsigned i = 0; i < sz; i++){
		        cmds[i] = NULL;
		    }
		    //delete[] cmds;
	    	}
		//Implies test was used
		else if(allCmds[c_s] == "test" ){
cout << "test detected//////////////////////////////////////////" << endl;
		    init = 0;
		    //Set new connector
		    if(c_p < allCmds.size())
		        c_c = allCmds[c_p];
		    else
		        c_c = "end";

		    //Check for flags. Careful about out of index!
		    //Flags are -e, -f, -d or no flag which evals to -e
		    //Args can or may not exist. Args can be one string only. Multiple
		    //Args will output an error and be counted as a failed execution

		    //c_p is the connector and c_s is the start.
		    //c_s can at most be one before the next connector c_s < c_p
		    //and c_s + n where n is the size-1 of this cmd sequence
		    //must be < size of all Cmd sequences.
		    if(c_s + 1 < c_p && c_p - c_s <= 3){
			struct stat sb;

			if(c_s + 2 < c_p){
			    if(allCmds[c_s + 1] == "-e"){
cout << "    DEBUG entered e" << endl;
				if(stat(allCmds[c_s + 2].c_str(), &sb)==-1){
				    perror("Stat error");
				    succeeded = 0;
				}
				else if((sb.st_mode & S_IFREG) || (sb.st_mode & S_IFDIR))
				    succeeded = 1;
			    }
			    else if(allCmds[c_s + 1] == "-f"){
cout << "    DEBUG entered f////////////////////////////////////////////" << endl;
				if(stat(allCmds[c_s + 2].c_str(), &sb)==-1){
				    perror("Stat error");
				    succeeded = 0;
				}
				else if((sb.st_mode & S_IFREG))
				    succeeded = 1;
			    }
			    else if(allCmds[c_s + 1] == "-d"){
cout << "    DEBUG entered d" << endl;
				if(stat(allCmds[c_s + 2].c_str(), &sb)==-1){
				    perror("Stat error");
				    succeeded = 0;
				}
				else if((sb.st_mode & S_IFDIR))
				    succeeded = 1;
			    }
			}
			//Deafaults to -e
			else{
cout << "    DEBUG default e" << endl;
			    if(stat(allCmds[c_s + 1].c_str(), &sb)==-1){
				perror("Stat error");
			 	succeeded = 0;
			    }
			    else if((sb.st_mode & S_IFREG) || (sb.st_mode & S_IFDIR)){
				    succeeded = 1;
cout << "    DEBUG default e success" << endl;

			    }
			}
		    }
		    else{
			cout << "Bash: test error." << endl;
		        succeeded = 0;
		    }

		    c_p++;
		    c_s = c_p;
		}
		//Implies failed cmd combo(eg. SUCCESS || CMD, CMD fails)
		else{
		    cout << "Welcome to limbo" << endl << endl;
		    c_p++;
		    c_s = c_p;
		    succeeded = 0;
		}
            }
	}
	fflush(stdout);
	//RESET ALL VALUES
	input="quit";
	cout << "$ ";
	cin.clear();
	getline(cin, input);
    }
    return false;
}

int main(int argc, char * argv[]){
	while(promptUser());
	return 0;
}

<|endoftext|>


<commit_before>#include<iostream>
#include<fcntl.h>
#include<pwd.h>
#include<sstream>
#include<stdio.h>
#include<boost/tokenizer.hpp>
#include<errno.h>
#include<string>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/unistd.h>

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<char> > Tok;

void readCommands(string str);
int conjunct(int n, stringstream& ss, const Tok::iterator &it, bool didExtra = false);
void skipCommand(Tok::iterator &it, Tok &tokens);
void splitString(char** args, stringstream& ss, int n);
bool ioRedir(const Tok::iterator &it, bool inRedir, bool append);
void resetIO(int in0, int out1);
void pipeRedir(int fd[], int fdLoop[], bool pipeOut, bool isFirst, bool isLast, int n, stringstream& ss, const Tok::iterator &it, bool didRedir);

int main(){

    cout << endl;
    string str = "";
    struct passwd *pass;

    pass = getpwuid(getuid());
    if(pass == NULL){
        perror("getpwuid()");
    }

    char* usrname = pass->pw_name;
    char hostname[128];

    int success = gethostname(hostname, sizeof hostname);
    if(success == -1){
        perror("gethostname()");
    }

    string userinfo = "";
    string username = "";

    if(usrname != NULL && success == 0){
        username = usrname;
        userinfo = username + "@" + hostname;
    }

    bool notExited = true;

    while(notExited){
        cout << userinfo << "$ ";
        getline(cin, str);
        readCommands(str);
    }

    return 0;
}

void readCommands(string str){

    char_separator<char> sep("\" ", ";#|&<>");

    Tok tokens(str, sep);
    stringstream ss;
    int in0;
    int out1;
    int fd[2];
    int fdLoop[2];
    bool pipeOut = false; bool isFirst = true;
    bool didRedir = false;
    bool didPipe = false; bool didExtra = false;
    int n = 0;

    if((in0 = dup(0)) == -1){
        perror("input-0");
        return;
    }

    if((out1 = dup(1)) == -1){
        perror("output-1");
        return;
    }

    if((pipe(fd)) == -1){
        perror("pipe");
        exit(-1);
    }

    for(Tok::iterator it = tokens.begin(); it != tokens.end(); it++){

        begin:

        if(*it == ";"){
            conjunct(n, ss, it);
            n = 0;
            it++;
            if(it == tokens.end()) break;
        }
        else if(*it ==  "&"){
            if(conjunct(n, ss, it) == -1){
                skipCommand(it, tokens);
            }
            n = 0;
            if(it == tokens.end()) break;
            it++;
            if(it == tokens.end()) break;
        }
        else if(*it == "|"){
            Tok::iterator copy = it;
            copy++;

            if(copy == tokens.end() || *copy == ";" || *copy == "&"){
                conjunct(0, ss, it);
                skipCommand(it, tokens);
                return;
            }

            if(*copy == "|"){
                copy++;
                if(copy == tokens.end() || *copy == ";" || *copy == "&"){
                    conjunct(0, ss, it);
                    skipCommand(it, tokens);
                    return;
                }

                if(conjunct(n, ss, it) != -1){
                    skipCommand(it, tokens);
                }
            }
            else{
//TODO piping
                didPipe = true;

                (!pipeOut) ? pipeOut = true : pipeOut = false;

                pipeRedir(fd, fdLoop, pipeOut, isFirst, false, n, ss, it, didRedir);
                isFirst = false;

                n = 0;
                continue;
            }

            n = 0;
            if(it == tokens.end()) break;
            it++;
        }
        else if(*it == "<" || *it == ">"){
            bool append = false;

another:
            Tok::iterator check = it;
            check++;
            bool inRedir = false;
            bool stringRedir = false;

            if(check != tokens.end() && *check == ">"){
                append = true;
                it++;
                goto here;
            }

            if(*it == "<"){
                inRedir = true;
                Tok::iterator copy = it;
                int i;
                for(i = 0; i < 2; i++){
                    copy++;
                    if(copy == tokens.end() || *copy != "<") break;
                }

                if(i == 2 && copy != tokens.end() && *copy != ";" && *copy != "|" && *copy != "&"){
                    stringRedir = true;
                }
            }

            if(stringRedir){
                didExtra = true;
                it++; it++; it++;
                string redirString = "";

                while(it != tokens.end() && *it != ";" && *it != "|" && *it != "&"){
                    redirString += (*it + " ");
                    it++;
                }

                if(write(0, (char*) redirString.c_str(), redirString.size() + 1) == -1){
                    perror("write to extra");
                    return;
                }

                if(it == tokens.end()){
                    break;
                }
                else{
                    goto begin;
                }

            }
here:
            it++;

            if(it == tokens.end()){
                cerr << "Bash: syntax error near unexpected token \'newline\'" << endl;
                exit(-1);
            }

            if(!ioRedir(it, inRedir, append)) return;

            didRedir = true;

            Tok::iterator copy = it;
            copy++;

            if(copy == tokens.end()) break;

            if(*copy == ">" || *copy == "<"){
                it++;
                goto another;
            }

            continue;
        }

        if(*it == "#"){
            if(didPipe){
                (!pipeOut) ? pipeOut = true : pipeOut = false;
                pipeRedir(fd, fdLoop, pipeOut, isFirst, true, n, ss, it, didRedir);
                return;
            }

            conjunct(n, ss, it);

            if(didRedir){
                resetIO(in0, out1);
            }
            return;
        }

        if(*it != "&" && *it != "|"){
            ss << *it;
            ss << " ";
            n++;
        }
    }
    //TODO piping
    if(n > 0){
        Tok::iterator it = tokens.begin();
        if(didPipe){
            (!pipeOut) ? pipeOut = true : pipeOut = false;
            pipeRedir(fd, fdLoop,  pipeOut, isFirst, true, n, ss, it, didRedir);
        }
        else{
            conjunct(n, ss, it, didExtra);
        }
    }

    if(didRedir){
        resetIO(in0, out1);
    }
}

int conjunct(int n, stringstream& ss, const Tok::iterator &it, bool didExtra){

    int status;

    if(n == 0 && *it != "#"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return -1;
    }
    else if(n == 0 && *it == "#") return 0;
    char** args = new char*[n + 1];

    splitString(args, ss, n);

    int pid = fork();

    if(pid == -1){
        perror("There was an error with the fork().");
        exit(1);
    }
    else if(pid == 0){
        if(-1 == execvp((const char*) args[0], (char* const*) args)){
            perror(args[0]);
            exit(3);
        }
    }
    else if(pid > 0){

        if(-1 == wait(&status)){
            perror("There was an error with wait().");
            return -1;
        }

        if(WIFEXITED(status)){
            if(WEXITSTATUS(status) == 3){
                delete[] args;
                return -1;
            }
        }
    }

    delete[] args;

    return 0;
}

bool ioRedir(const Tok::iterator &it, bool inRedir, bool append){

    if(*it == "&" || *it == "|" || *it == ";"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return false;
    }

    if(inRedir){
        string input = *it;

        int fdi;

        if((fdi = open((char*) input.c_str(), O_RDONLY)) == -1){
            perror("open input");
            return false;
        }

        if(close(0) == -1){
            perror("close input");
            exit(-1);
        }

        if(dup(fdi) == -1){
            perror("dup input");
            exit(-1);
        }
        return true;
    }
    else{
        string output = *it;

        int fdo;
        mode_t readWrite = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

        if(append){
            if((fdo = open((char*) output.c_str(), O_RDWR | O_CREAT | O_APPEND,  readWrite)) == -1){
                perror("open output");
                exit(-1);
            }
        }
        else{
            if((fdo = open((char*) output.c_str(), O_RDWR | O_CREAT | O_TRUNC,  readWrite)) == -1){
                perror("open output append");
                exit(-1);
            }
        }

        if(close(1) == -1){
            perror("close output");
            exit(-1);
        }

        if(dup(fdo) == -1){
            perror("dup output");
            exit(-1);
        }
    }

    return true;
}

void resetIO(int in0, int out1){

    if(dup2(in0, 0) == -1){
        perror("dup input - 0");
        exit(-1);
    }

    if(dup2(out1, 1) == -1){
        perror("dup output - 1");
        exit(-1);
    }
}

//TODO piping
void pipeRedir(int fd[], int fdLoop[], bool pipeOut, bool isFirst, bool isLast, int n, stringstream& ss, const Tok::iterator& it, bool didRedir){

    int status;

    if(n == 0 && *it != "#"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return;
    }
    else if(n == 0 && *it == "#") return;
    char** args = new char*[n + 1];

    splitString(args, ss, n);

    if(pipeOut){
        int pid;

        if(!isFirst){
            if(pipe(fd) == -1){
                perror("repipe fd in input");
                exit(-1);
            }
        }

        if((pid = fork()) == -1){
            perror("fork in pipe");
            exit(-1);
        }

        if(pid == 0){
            if(isFirst){
                if(close(fd[0]) == -1){
                    perror("close in pipe");
                    exit(-1);
                }
            }

            if(!isFirst){
                if(dup2(fdLoop[0], 0) == -1){
                    perror("dup2 fdLoop in input");
                    exit(-1);
                }
            }

            if(!isLast){
                if(dup2(fd[1], 1) == -1){
                    perror("dup2 in pipe");
                    exit(-1);
                }
            }
            //TODO execute command here

            if(-1 == execvp((const char*) args[0], (char* const*) args)){
                perror("execvp in pipe");
                exit(-1);
            }
        }
        else{
            if(wait(&status) == -1){
                perror("wait in pipe");
                exit(-1);
            }

            if(close(fd[1]) == -1){
                perror("close in pipe parent");
                exit(-1);
            }

            if(!isFirst){
                if(close(fdLoop[0]) == -1){
                    perror("close fdLoop in input");
                    exit(-1);
                }
            }

            if(WIFEXITED(status)){
                if(WEXITSTATUS(status) == 3){
                    exit(-1);
                }
            }

        }
    }
    else{
        int pid;
        if(!isLast){
            if(pipe(fdLoop) == -1){
                perror("fdLoop in output");
                exit(-1);
            }
        }

        if((pid = fork()) == -1){
            perror("fork#2 in pipe");
            exit(-1);
        }

        if(pid == 0){

            if(dup2(fd[0], 0) == -1){
                perror("dup2 #2 in pipe");
                exit(-1);
            }

            //TODO execute command here

            if(!isLast){
                if(dup2(fdLoop[1], 1) == -1){
                    perror("dup2 loop in output");
                    exit(-1);
                }
            }

            if(-1 == execvp((const char*) args[0], (char* const*) args)){
                perror("execvp in pipe");
                exit(-1);
            }

        }
        else{

            if(wait(&status) == -1){
                perror("wait #2 in pipe");
                exit(-1);
            }

            if(close(fd[0]) == -1){
                perror("close #2 in pipe parent");
                exit(-1);
            }

            if(!isLast){
                if(close(fdLoop[1]) == -1){
                    perror("close fdLoop in output");
                    exit(-1);
                }
            }
            if(WIFEXITED(status)){
                if(WEXITSTATUS(status) == 3){
                    exit(-1);
                }
            }
        }
    }
}

void skipCommand(Tok::iterator &it, Tok &tokens){
    while(it != tokens.end() && *it != ";"){
        it++;
    }
}

void splitString(char** args, stringstream& ss, int n){

    string *str = new string[n];

    for(int i = 0; i < n; i++){

        ss >> str[i];

        args[i] = (char*) str[i].c_str();
    }

    args[n] = NULL;

    char exitC[] = "exit";

    if(strcmp(args[0], exitC) == 0){
        cout << endl;
        exit(0);
    }

    delete[] str;
}
<commit_msg>added cd and PATH aspects of rshell<commit_after>#include<iostream>
#include<sys/stat.h>
#include<fcntl.h>
#include<pwd.h>
#include<sstream>
#include<stdio.h>
#include<boost/tokenizer.hpp>
#include<errno.h>
#include<string>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/unistd.h>

using namespace std;
using namespace boost;

typedef tokenizer<char_separator<char> > Tok;

char directory[1024];
string cwd = "";

void readCommands(string str);
int conjunct(int n, stringstream& ss, const Tok::iterator &it, bool didExtra = false);
void skipCommand(Tok::iterator &it, Tok &tokens);
void splitString(char** args, stringstream& ss, int n);
bool ioRedir(const Tok::iterator &it, bool inRedir, bool append);
void resetIO(int in0, int out1);
void pipeRedir(int fd[], int fdLoop[], bool pipeOut, bool isFirst, bool isLast, int n, stringstream& ss, const Tok::iterator &it, bool didRedir);
string findPath(string file);

int main(){
    cout << endl;
    string str = "";
    struct passwd *pass;

    bool hasDirectory = true;

    if((getcwd(directory, 1024)) == NULL){
        perror("getcwd");
        hasDirectory = false;
    }

    pass = getpwuid(getuid());
    if(pass == NULL){
        perror("getpwuid()");
    }

    char* usrname = pass->pw_name;
    char hostname[128];

    int success = gethostname(hostname, sizeof hostname);
    if(success == -1){
        perror("gethostname()");
    }

    string userinfo = "";
    string username = "";

    if(usrname != NULL && success == 0){
        username = usrname;
        userinfo = username + "@" + hostname;
    }

    if(hasDirectory){
        cwd = directory;
        cwd = ":" + cwd;
    }

    bool notExited = true;

    while(notExited){
        cout << userinfo << cwd << "$ ";
        getline(cin, str);
        readCommands(str);
    }

    return 0;
}

void readCommands(string str){

    char_separator<char> sep("\" ", ";#|&<>");

    Tok tokens(str, sep);
    stringstream ss;
    int in0;
    int out1;
    int fd[2];
    int fdLoop[2];
    bool pipeOut = false; bool isFirst = true;
    bool didRedir = false;
    bool didPipe = false; bool didExtra = false;
    int n = 0;

    if((in0 = dup(0)) == -1){
        perror("input-0");
        return;
    }

    if((out1 = dup(1)) == -1){
        perror("output-1");
        return;
    }

    if((pipe(fd)) == -1){
        perror("pipe");
        exit(-1);
    }

    for(Tok::iterator it = tokens.begin(); it != tokens.end(); it++){

        begin:

        if(*it == ";"){
            conjunct(n, ss, it);
            n = 0;
            it++;
            if(it == tokens.end()) break;
        }
        else if(*it ==  "&"){
            if(conjunct(n, ss, it) == -1){
                skipCommand(it, tokens);
            }
            n = 0;
            if(it == tokens.end()) break;
            it++;
            if(it == tokens.end()) break;
        }
        else if(*it == "|"){
            Tok::iterator copy = it;
            copy++;

            if(copy == tokens.end() || *copy == ";" || *copy == "&"){
                conjunct(0, ss, it);
                skipCommand(it, tokens);
                return;
            }

            if(*copy == "|"){
                copy++;
                if(copy == tokens.end() || *copy == ";" || *copy == "&"){
                    conjunct(0, ss, it);
                    skipCommand(it, tokens);
                    return;
                }

                if(conjunct(n, ss, it) != -1){
                    skipCommand(it, tokens);
                }
            }
            else{
//TODO piping
                didPipe = true;

                (!pipeOut) ? pipeOut = true : pipeOut = false;

                pipeRedir(fd, fdLoop, pipeOut, isFirst, false, n, ss, it, didRedir);
                isFirst = false;

                n = 0;
                continue;
            }

            n = 0;
            if(it == tokens.end()) break;
            it++;
        }
        else if(*it == "<" || *it == ">"){
            bool append = false;

another:
            Tok::iterator check = it;
            check++;
            bool inRedir = false;
            bool stringRedir = false;

            if(check != tokens.end() && *check == ">"){
                append = true;
                it++;
                goto here;
            }

            if(*it == "<"){
                inRedir = true;
                Tok::iterator copy = it;
                int i;
                for(i = 0; i < 2; i++){
                    copy++;
                    if(copy == tokens.end() || *copy != "<") break;
                }

                if(i == 2 && copy != tokens.end() && *copy != ";" && *copy != "|" && *copy != "&"){
                    stringRedir = true;
                }
            }

            if(stringRedir){
                didExtra = true;
                it++; it++; it++;
                string redirString = "";

                while(it != tokens.end() && *it != ";" && *it != "|" && *it != "&"){
                    redirString += (*it + " ");
                    it++;
                }

                if(write(0, (char*) redirString.c_str(), redirString.size() + 1) == -1){
                    perror("write to extra");
                    return;
                }

                if(it == tokens.end()){
                    break;
                }
                else{
                    goto begin;
                }

            }
here:
            it++;

            if(it == tokens.end()){
                cerr << "Bash: syntax error near unexpected token \'newline\'" << endl;
                exit(-1);
            }

            if(!ioRedir(it, inRedir, append)) return;

            didRedir = true;

            Tok::iterator copy = it;
            copy++;

            if(copy == tokens.end()) break;

            if(*copy == ">" || *copy == "<"){
                it++;
                goto another;
            }

            continue;
        }

        if(*it == "#"){
            if(didPipe){
                (!pipeOut) ? pipeOut = true : pipeOut = false;
                pipeRedir(fd, fdLoop, pipeOut, isFirst, true, n, ss, it, didRedir);
                return;
            }

            conjunct(n, ss, it);

            if(didRedir){
                resetIO(in0, out1);
            }
            return;
        }

        if(*it != "&" && *it != "|"){
            ss << *it;
            ss << " ";
            n++;
        }
    }
    //TODO piping
    if(n > 0){
        Tok::iterator it = tokens.begin();
        if(didPipe){
            (!pipeOut) ? pipeOut = true : pipeOut = false;
            pipeRedir(fd, fdLoop,  pipeOut, isFirst, true, n, ss, it, didRedir);
        }
        else{
            conjunct(n, ss, it, didExtra);
        }
    }

    if(didRedir){
        resetIO(in0, out1);
    }
}

int conjunct(int n, stringstream& ss, const Tok::iterator &it, bool didExtra){

    int status;

    if(n == 0 && *it != "#"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return -1;
    }
    else if(n == 0 && *it == "#") return 0;
    char** args = new char*[n + 1];

    splitString(args, ss, n);

    if(strcmp(args[0], "cd") == 0){
        if(n <= 1){

            string home;

            if((home = getenv("HOME")) == ""){
                perror("getenv");
            }

            if(chdir((char*) home.c_str()) == -1){
                perror("chdir");
                return -1;
            }

            if((getcwd(directory, 1024)) == NULL){
                perror("getcwd");
            }
            else{
                cwd = directory;
                cwd = ":" + cwd;
            }
        }
        else{
            if(chdir(args[1]) == -1){
                perror("chdir");
                return -1;
            }

            if((getcwd(directory, 1024)) == NULL){
                perror("getcwd");
            }
            else{
                cwd = directory;
                cwd = ":" + cwd;
            }
        }
    }
    else{

        int pid = fork();

        if(pid == -1){
            perror("There was an error with the fork().");
            exit(1);
        }
        else if(pid == 0){
            if(-1 == execv((const char*) args[0], (char* const*) args)){
                perror(args[0]);
                exit(3);
            }
        }
        else if(pid > 0){

            if(-1 == wait(&status)){
                perror("There was an error with wait().");
                return -1;
            }

            if(WIFEXITED(status)){
                if(WEXITSTATUS(status) == 3){
                    delete[] args;
                    return -1;
                }
            }
        }
    }

    delete[] args;

    return 0;
}

bool ioRedir(const Tok::iterator &it, bool inRedir, bool append){

    if(*it == "&" || *it == "|" || *it == ";"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return false;
    }

    if(inRedir){
        string input = *it;

        int fdi;

        if((fdi = open((char*) input.c_str(), O_RDONLY)) == -1){
            perror("open input");
            return false;
        }

        if(close(0) == -1){
            perror("close input");
            exit(-1);
        }

        if(dup(fdi) == -1){
            perror("dup input");
            exit(-1);
        }
        return true;
    }
    else{
        string output = *it;

        int fdo;
        mode_t readWrite = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;

        if(append){
            if((fdo = open((char*) output.c_str(), O_RDWR | O_CREAT | O_APPEND,  readWrite)) == -1){
                perror("open output");
                exit(-1);
            }
        }
        else{
            if((fdo = open((char*) output.c_str(), O_RDWR | O_CREAT | O_TRUNC,  readWrite)) == -1){
                perror("open output append");
                exit(-1);
            }
        }

        if(close(1) == -1){
            perror("close output");
            exit(-1);
        }

        if(dup(fdo) == -1){
            perror("dup output");
            exit(-1);
        }
    }

    return true;
}

void resetIO(int in0, int out1){

    if(dup2(in0, 0) == -1){
        perror("dup input - 0");
        exit(-1);
    }

    if(dup2(out1, 1) == -1){
        perror("dup output - 1");
        exit(-1);
    }
}

//TODO piping
void pipeRedir(int fd[], int fdLoop[], bool pipeOut, bool isFirst, bool isLast, int n, stringstream& ss, const Tok::iterator& it, bool didRedir){

    int status;

    if(n == 0 && *it != "#"){
        cerr << "Bash: syntax error near unexpected token \'" << *it << "\'" << endl;
        return;
    }
    else if(n == 0 && *it == "#") return;
    char** args = new char*[n + 1];

    splitString(args, ss, n);

    if(pipeOut){
        int pid;

        if(!isFirst){
            if(pipe(fd) == -1){
                perror("repipe fd in input");
                exit(-1);
            }
        }

        if((pid = fork()) == -1){
            perror("fork in pipe");
            exit(-1);
        }

        if(pid == 0){
            if(isFirst){
                if(close(fd[0]) == -1){
                    perror("close in pipe");
                    exit(-1);
                }
            }

            if(!isFirst){
                if(dup2(fdLoop[0], 0) == -1){
                    perror("dup2 fdLoop in input");
                    exit(-1);
                }
            }

            if(!isLast){
                if(dup2(fd[1], 1) == -1){
                    perror("dup2 in pipe");
                    exit(-1);
                }
            }
            //TODO execute command here

            if(-1 == execv((const char*) args[0], (char* const*) args)){
                perror("execv in pipe");
                exit(-1);
            }
        }
        else{
            if(wait(&status) == -1){
                perror("wait in pipe");
                exit(-1);
            }

            if(close(fd[1]) == -1){
                perror("close in pipe parent");
                exit(-1);
            }

            if(!isFirst){
                if(close(fdLoop[0]) == -1){
                    perror("close fdLoop in input");
                    exit(-1);
                }
            }

            if(WIFEXITED(status)){
                if(WEXITSTATUS(status) == 3){
                    exit(-1);
                }
            }

        }
    }
    else{
        int pid;
        if(!isLast){
            if(pipe(fdLoop) == -1){
                perror("fdLoop in output");
                exit(-1);
            }
        }

        if((pid = fork()) == -1){
            perror("fork#2 in pipe");
            exit(-1);
        }

        if(pid == 0){

            if(dup2(fd[0], 0) == -1){
                perror("dup2 #2 in pipe");
                exit(-1);
            }

            //TODO execute command here

            if(!isLast){
                if(dup2(fdLoop[1], 1) == -1){
                    perror("dup2 loop in output");
                    exit(-1);
                }
            }

            if(-1 == execv((const char*) args[0], (char* const*) args)){
                perror("execv in pipe");
                exit(-1);
            }

        }
        else{

            if(wait(&status) == -1){
                perror("wait #2 in pipe");
                exit(-1);
            }

            if(close(fd[0]) == -1){
                perror("close #2 in pipe parent");
                exit(-1);
            }

            if(!isLast){
                if(close(fdLoop[1]) == -1){
                    perror("close fdLoop in output");
                    exit(-1);
                }
            }
            if(WIFEXITED(status)){
                if(WEXITSTATUS(status) == 3){
                    exit(-1);
                }
            }
        }
    }
}

void skipCommand(Tok::iterator &it, Tok &tokens){
    while(it != tokens.end() && *it != ";"){
        it++;
    }
}

void splitString(char** args, stringstream& ss, int n){

    string *str = new string[n];

    for(int i = 0; i < n; i++){

        ss >> str[i];

        if(str[i] != "cd" && i == 0){
           str[i] = findPath(str[i]);
        }

        args[i] = (char*) str[i].c_str();
    }

    args[n] = NULL;

    char exitC[] = "exit";

    if(strcmp(args[0], exitC) == 0){
        cout << endl;
        exit(0);
    }

    delete[] str;
}

string findPath(string file){
    string env = "";

    if((env = getenv("PATH")) == ""){
        perror("getenv findPath");
        exit(-1);
    }

    string dir = "";
    string path = "";
    struct stat buf;

    int i;

    while((i = env.find(":")) != -1){
        dir = env.substr(0, i);

        env = env.substr(i + 1, env.size());

        if(stat((dir + "/" + file).c_str(), &buf) != -1){
            path = dir + "/" + file;
        }
    }

    return path;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <stdlib.h> //NULL, exit
#include <string.h> //strcmp
#include <sys/wait.h> //waitpid
#include <unistd.h> //fork, execvp
using namespace std;

#define MAX_LENGTH 256 //for login and host name
#define CONNECTOR_ERROR_MESSAGE \
	"syntax error: connector must be preceded by a command"

//separate special characters with whitespace for tokenization
//WARNING: this function calls new
string preprocess(const string& line) {
	string processed = "";
	for (int i = 0; i < line.size(); ++i) {
		if (line[i] == ';')
			processed += " ; ";
		else if (line[i] == '&' && i+1 < line.size() && line[i+1] == '&') {
			processed += " && ";
			++i;
		}
		else if (line[i] == '|' && i+1 < line.size() && line[i+1] == '|') {
			processed += " || ";
			++i;
		}
		else
			processed += line[i];
	}
	return processed;
}

//raw input is parsed into an array of commands
int parse(string& line, vector< vector<string> >& cmd) {
	line = preprocess(line);
	cmd.push_back(vector<string>());
	stringstream ss(line);
	string token;
	int i = 0;
	while (ss >> token) {
		if (token == "#")
			break;
		if (token == ";" || token == "||" || token == "&&") {
			if (cmd[i].empty())
				return 1;
			cmd.push_back(vector<string>());
			++i;
		}
		cmd[i].push_back(token);
	}
	return 0;
}

//converts vector of vector of strings to vector of array of cstrings
//WARNING: calls new
vector<char**> c_compatible(const vector< vector<string> >& cmd) {
	vector<char**> c_cmd;
	for (int i = 0; i < cmd.size(); ++i) {
		char **command = new char*[cmd[i].size()+1];
		int j;
		for (j = 0; j < cmd[i].size(); ++j) {
			command[j] = new char[cmd[i][j].size()];
			memcpy(command[j], cmd[i][j].c_str(), cmd[i][j].size()+1);
		}
		command[j] = NULL;
		c_cmd.push_back(command);
	}
	return c_cmd;
}

//deletes c compatible array of cstrings
void c_delete(const vector< vector<string> >& cmd, vector<char**>& c_cmd) {
	for (int i = 0; i < cmd.size(); ++i) {
		for (int j = 0; j <= cmd[i].size(); ++j) {
			delete[] c_cmd[i][j];
		}
		delete c_cmd[i];
	}
}

//execute commands using fork, execvp, and waitpid
void execute(const vector< vector<string> >& cmd) {
	vector<char**> c_cmd = c_compatible(cmd);
	int status;
	for (int i = 0; i < c_cmd.size(); ++i) {
		int pid = fork();
		if (pid < 0) {
			perror("fork");
			exit(1);
		}
		else if (pid == 0) {
			if (i == 0) {
				execvp(c_cmd[i][0], c_cmd[i]);
				perror("execvp");
				exit(1);
			}
			else {
				if (strcmp(c_cmd[i][0], "||") == 0 && status == 0)
					exit(0);
				if (strcmp(c_cmd[i][0], "&&") == 0 && status != 0)
					exit(0);
				execvp(c_cmd[i][1], c_cmd[i]+1);
				perror("execvp");
				exit(1);
			}
		}
		else {
			if (waitpid(pid, &status, 0) < 0)
				perror("waitpid");
		}
	}
	c_delete(cmd, c_cmd);
}

void exit_check(const string& line) {
	if (line.size() >= 4 && line[0] == 'e' && line[1] == 'x'
						 && line[2] == 'i' && line[3] == 't')
		exit(0);
}

//prompt for input, get input, parse, execute, repeat
int main() {
	while (1) {
		char *login = getlogin();
		char hostname[MAX_LENGTH];
		gethostname(hostname, MAX_LENGTH);
		cout << login << "@" << hostname << "$ ";
		string line;
		getline(cin, line);
		exit_check(line);
		vector< vector<string> > cmd;
		if (parse(line, cmd)) {
			cout << CONNECTOR_ERROR_MESSAGE <<  endl;
			continue;
		}
		execute(cmd);
	}
	return 0;
}
<commit_msg>fixed comments<commit_after>#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <stdlib.h> //NULL, exit
#include <string.h> //strcmp
#include <sys/wait.h> //waitpid
#include <unistd.h> //fork, execvp
using namespace std;

#define MAX_LENGTH 256 //for login and host name
#define CONNECTOR_ERROR_MESSAGE \
	"syntax error: connector must be preceded by a command"

//separate special characters with whitespace for tokenization
string preprocess(const string& line) {
	string processed = "";
	for (int i = 0; i < line.size(); ++i) {
		if (line[i] == ';')
			processed += " ; ";
		else if (line[i] == '&' && i+1 < line.size() && line[i+1] == '&') {
			processed += " && ";
			++i;
		}
		else if (line[i] == '|' && i+1 < line.size() && line[i+1] == '|') {
			processed += " || ";
			++i;
		}
		else
			processed += line[i];
	}
	return processed;
}

//raw input is parsed into an array of commands
int parse(string& line, vector< vector<string> >& cmd) {
	line = preprocess(line);
	cmd.push_back(vector<string>());
	stringstream ss(line);
	string token;
	int i = 0;
	while (ss >> token) {
		if (token == "#")
			break;
		if (token == ";" || token == "||" || token == "&&") {
			if (cmd[i].empty())
				return 1;
			cmd.push_back(vector<string>());
			++i;
		}
		cmd[i].push_back(token);
	}
	return 0;
}

//converts vector of vector of strings to vector of array of cstrings
vector<char**> c_compatible(const vector< vector<string> >& cmd) {
	vector<char**> c_cmd;
	for (int i = 0; i < cmd.size(); ++i) {
		char **command = new char*[cmd[i].size()+1];
		int j;
		for (j = 0; j < cmd[i].size(); ++j) {
			command[j] = new char[cmd[i][j].size()];
			memcpy(command[j], cmd[i][j].c_str(), cmd[i][j].size()+1);
		}
		command[j] = NULL;
		c_cmd.push_back(command);
	}
	return c_cmd;
}

//deletes c compatible array of cstrings
void c_delete(const vector< vector<string> >& cmd, vector<char**>& c_cmd) {
	for (int i = 0; i < cmd.size(); ++i) {
		for (int j = 0; j <= cmd[i].size(); ++j) {
			delete[] c_cmd[i][j];
		}
		delete c_cmd[i];
	}
}

//execute commands using fork, execvp, and waitpid
void execute(const vector< vector<string> >& cmd) {
	vector<char**> c_cmd = c_compatible(cmd);
	int status;
	for (int i = 0; i < c_cmd.size(); ++i) {
		int pid = fork();
		if (pid < 0) {
			perror("fork");
			exit(1);
		}
		else if (pid == 0) {
			if (i == 0) {
				execvp(c_cmd[i][0], c_cmd[i]);
				perror("execvp");
				exit(1);
			}
			else {
				if (strcmp(c_cmd[i][0], "||") == 0 && status == 0)
					exit(0);
				if (strcmp(c_cmd[i][0], "&&") == 0 && status != 0)
					exit(0);
				execvp(c_cmd[i][1], c_cmd[i]+1);
				perror("execvp");
				exit(1);
			}
		}
		else {
			if (waitpid(pid, &status, 0) < 0)
				perror("waitpid");
		}
	}
	c_delete(cmd, c_cmd);
}

void exit_check(const string& line) {
	if (line.size() >= 4 && line[0] == 'e' && line[1] == 'x'
						 && line[2] == 'i' && line[3] == 't')
		exit(0);
}

//prompt for input, get input, parse, execute, repeat
int main() {
	while (1) {
		char *login = getlogin();
		char hostname[MAX_LENGTH];
		gethostname(hostname, MAX_LENGTH);
		cout << login << "@" << hostname << "$ ";
		string line;
		getline(cin, line);
		exit_check(line);
		vector< vector<string> > cmd;
		if (parse(line, cmd)) {
			cout << CONNECTOR_ERROR_MESSAGE <<  endl;
			continue;
		}
		execute(cmd);
	}
	return 0;
}
<|endoftext|>


<commit_before>#include "postgres_backend.hpp"
#include <boost/filesystem.hpp>
#include <fstream>
#include <iostream>
#include <limits.h>
#include <unistd.h>

namespace eosio {
namespace blockvault {

postgres_backend::postgres_backend(const std::string& options)
    : conn(options) {
   pqxx::work w(conn);
   w.exec("CREATE TABLE IF NOT EXISTS BlockData (watermark_bn bigint, watermark_ts bigint, lib bigint, "
          "block_id bytea, previous_block_id bytea, block oid, block_size bigint);"
          "CREATE TABLE IF NOT EXISTS SnapshotData (watermark_bn bigint, watermark_ts bigint, snapshot oid);");
   w.commit();

   conn.prepare(
       "insert_constructed_block",
       "INSERT INTO BlockData (watermark_bn, watermark_ts, lib, block_id, previous_block_id, block, block_size) "
       "SELECT $1, $2, $3, $4, $5, $6, $7  WHERE NOT "
       "EXISTS (SELECT * FROM BlockData WHERE (watermark_bn >= $1) OR (watermark_ts > $2) OR (lib >= $3) OR (block_id "
       "= $4))");

   conn.prepare(
       "insert_external_block",
       "INSERT INTO BlockData (watermark_bn, watermark_ts, lib, block_id, previous_block_id, block, block_size) SELECT "
       "COALESCE((SELECT MAX(watermark_bn) FROM BlockData),0), COALESCE((SELECT MAX(watermark_ts) FROM "
       "BlockData),0), $2, $3, $4, $5, $6 WHERE NOT "
       "EXISTS (SELECT * FROM BlockData WHERE lib >= $1 OR block_id = $3)");

   conn.prepare("get_block_insertion_result", "SELECT block from BlockData WHERE block=$1");

   conn.prepare("insert_snapshot",
                "INSERT INTO SnapshotData (watermark_bn, watermark_ts, snapshot) SELECT $1, $2, $3 WHERE NOT EXISTS "
                "(SELECT * FROM SnapshotData WHERE watermark_bn >= $1 OR watermark_ts >= $2)");

   conn.prepare("get_snapshot_insertion_result", "SELECT snapshot from SnapshotData WHERE snapshot=$1");

   conn.prepare("get_sync_watermark", "SELECT watermark_bn, watermark_ts FROM BlockData WHERE "
                                      "previous_block_id = $1 ORDER BY watermark_bn, watermark_ts LIMIT 1");

   conn.prepare("get_latest_snapshot", "SELECT snapshot FROM SnapshotData "
                                       "ORDER BY watermark_bn DESC, watermark_ts DESC LIMIT 1");

   conn.prepare("get_blocks_since_watermark", "SELECT block, block_size FROM BlockData WHERE "
                                              "watermark_bn >= $1 AND watermark_ts >= $2"
                                              "ORDER BY watermark_ts, watermark_bn");

   conn.prepare("get_all_blocks", "SELECT block, block_size FROM BlockData");

   conn.prepare("delete_outdated_block_lo",
                "SELECT lo_unlink(r.block) FROM BlockData r WHERE watermark_bn < $1 OR watermark_ts < $2;");
   conn.prepare("delete_outdated_block_data", "DELETE FROM BlockData WHERE watermark_bn < $1 OR watermark_ts < $2;");
   conn.prepare("delete_outdated_snapshot_lo",
                "SELECT lo_unlink(r.snapshot) FROM SnapshotData r WHERE watermark_bn < $1 OR watermark_ts < $2;");
   conn.prepare("delete_outdated_snapshot_data",
                "DELETE FROM SnapshotData WHERE watermark_bn < $1 OR watermark_ts < $2;");
} // namespace blockvault

bool postgres_backend::propose_constructed_block(std::pair<uint32_t, uint32_t> watermark, uint32_t lib,
                                                 const std::vector<char>& block_content, std::string_view block_id,
                                                 std::string_view previous_block_id) {
   try {
      pqxx::work w(conn);

      pqxx::largeobjectaccess obj(w);
      pqxx::binarystring      block_id_blob(block_id.data(), block_id.size());
      pqxx::binarystring      previous_block_id_blob(previous_block_id.data(), previous_block_id.size());
      w.exec_prepared0("insert_constructed_block", watermark.first, watermark.second, lib, block_id_blob,
                       previous_block_id_blob, obj.id(), block_content.size());
      auto r = w.exec_prepared1("get_block_insertion_result", obj.id());
      obj.write(block_content.data(), block_content.size());
      w.commit();
      return true;
   } catch (pqxx::unexpected_rows&) {
      return false;
   }
}

bool postgres_backend::append_external_block(uint32_t block_num, uint32_t lib, const std::vector<char>& block_content,
                                             std::string_view block_id, std::string_view previous_block_id) {
   try {
      pqxx::work w(conn);

      pqxx::largeobjectaccess obj(w);
      pqxx::binarystring      block_id_blob(block_id.data(), block_id.size());
      pqxx::binarystring      previous_block_id_blob(previous_block_id.data(), previous_block_id.size());
      w.exec_prepared0("insert_external_block", block_num, lib, block_id_blob, previous_block_id_blob, obj.id(),
                       block_content.size());
      auto r = w.exec_prepared1("get_block_insertion_result", obj.id());
      obj.write(block_content.data(), block_content.size());
      w.commit();
      return true;

   } catch (pqxx::unexpected_rows& ex) {
      return false;
   }
}

bool postgres_backend::propose_snapshot(std::pair<uint32_t, uint32_t> watermark, const char* snapshot_filename) {

   try {
      std::filebuf infile;

      infile.open(snapshot_filename, std::ios::in);

      pqxx::work              w(conn);
      pqxx::largeobjectaccess obj(w);

      w.exec_prepared0("insert_snapshot", watermark.first, watermark.second, obj.id());
      auto r = w.exec_prepared("get_snapshot_insertion_result", obj.id());

      if (!r.empty()) {

         const int chunk_size = 4096;
         char      chunk[chunk_size];

         auto sz = chunk_size;
         while (sz == chunk_size) {
            sz = infile.sgetn(chunk, chunk_size);
            obj.write(chunk, sz);
         };

         w.exec_prepared("delete_outdated_block_lo", watermark.first, watermark.second);
         w.exec_prepared("delete_outdated_block_data", watermark.first, watermark.second);
         w.exec_prepared("delete_outdated_snapshot_lo", watermark.first, watermark.second);
         w.exec_prepared("delete_outdated_snapshot_data", watermark.first, watermark.second);
      }

      w.commit();
      return !r.empty();

   } catch (pqxx::unexpected_rows&) {
      return false;
   }
}

void retrieve_blocks(backend::sync_callback& callback, pqxx::work& trx, pqxx::result r) {
   std::vector<char> block_data;

   for (const auto& x : r) {
      pqxx::oid               block_oid  = x[0].as<pqxx::oid>();
      uint64_t                block_size = x[1].as<uint64_t>();
      pqxx::largeobjectaccess obj(trx, block_oid, std::ios::in | std::ios::binary);
      block_data.resize(block_size);
      obj.read(block_data.data(), block_size);
      callback.on_block(std::string_view{block_data.data(), block_data.size()});
   }

   trx.commit();
}

void postgres_backend::sync(std::string_view previous_block_id, backend::sync_callback& callback) {
   pqxx::work trx(conn);

   if (previous_block_id.size()) {
      pqxx::binarystring blob(previous_block_id.data(), previous_block_id.size());
      auto               r = trx.exec_prepared("get_sync_watermark", blob);

      if (!r.empty()) {
         retrieve_blocks(
             callback, trx,
             trx.exec_prepared("get_blocks_since_watermark", r[0][0].as<uint32_t>(), r[0][1].as<uint32_t>()));
         return;
      }
   }

   auto r = trx.exec_prepared("get_latest_snapshot");

   if (!r.empty()) {
      pqxx::largeobject snapshot_obj(r[0][0].as<pqxx::oid>());

      fc::temp_file temp_file;
      std::string   fname = temp_file.path().string();

      snapshot_obj.to_file(trx, fname.c_str());
      callback.on_snapshot(fname.c_str());
   }

   retrieve_blocks(callback, trx, trx.exec_prepared("get_all_blocks"));
}

} // namespace blockvault
} // namespace eosio<commit_msg>fix postgress bug<commit_after>#include "postgres_backend.hpp"
#include <boost/filesystem.hpp>
#include <fstream>
#include <iostream>
#include <limits.h>
#include <unistd.h>

namespace eosio {
namespace blockvault {

postgres_backend::postgres_backend(const std::string& options)
    : conn(options) {
   pqxx::work w(conn);
   w.exec("CREATE TABLE IF NOT EXISTS BlockData (watermark_bn bigint, watermark_ts bigint, lib bigint, "
          "block_id bytea, previous_block_id bytea, block oid, block_size bigint);"
          "CREATE TABLE IF NOT EXISTS SnapshotData (watermark_bn bigint, watermark_ts bigint, snapshot oid);");
   w.commit();

   conn.prepare(
       "insert_constructed_block",
       "INSERT INTO BlockData (watermark_bn, watermark_ts, lib, block_id, previous_block_id, block, block_size) "
       "SELECT $1, $2, $3, $4, $5, $6, $7  WHERE NOT "
       "EXISTS (SELECT * FROM BlockData WHERE (watermark_bn >= $1) OR (watermark_ts > $2) OR (lib >= $3) OR (block_id "
       "= $4))");

   conn.prepare(
       "insert_external_block",
       "INSERT INTO BlockData (watermark_bn, watermark_ts, lib, block_id, previous_block_id, block, block_size) SELECT "
       "COALESCE((SELECT MAX(watermark_bn) FROM BlockData),0), COALESCE((SELECT MAX(watermark_ts) FROM "
       "BlockData),0), $2, $3, $4, $5, $6 WHERE NOT "
       "EXISTS (SELECT * FROM BlockData WHERE lib >= $1 OR block_id = $3)");

   conn.prepare("get_block_insertion_result", "SELECT block from BlockData WHERE block=$1");

   conn.prepare("insert_snapshot",
                "INSERT INTO SnapshotData (watermark_bn, watermark_ts, snapshot) SELECT $1, $2, $3 WHERE NOT EXISTS "
                "(SELECT * FROM SnapshotData WHERE watermark_bn >= $1 OR watermark_ts >= $2)");

   conn.prepare("get_snapshot_insertion_result", "SELECT snapshot from SnapshotData WHERE snapshot=$1");

   conn.prepare("get_sync_watermark", "SELECT watermark_bn, watermark_ts FROM BlockData WHERE "
                                      "previous_block_id = $1 ORDER BY watermark_bn, watermark_ts LIMIT 1");

   conn.prepare("get_latest_snapshot", "SELECT snapshot FROM SnapshotData "
                                       "ORDER BY watermark_bn DESC, watermark_ts DESC LIMIT 1");

   conn.prepare("get_blocks_since_watermark", "SELECT block, block_size FROM BlockData WHERE "
                                              "watermark_bn >= $1 AND watermark_ts >= $2"
                                              "ORDER BY watermark_ts, watermark_bn");

   conn.prepare("get_all_blocks", "SELECT block, block_size FROM BlockData");

   conn.prepare("delete_outdated_block_lo",
                "SELECT lo_unlink(r.block) FROM BlockData r WHERE watermark_bn < $1 OR watermark_ts < $2;");
   conn.prepare("delete_outdated_block_data", "DELETE FROM BlockData WHERE watermark_bn < $1 OR watermark_ts < $2;");
   conn.prepare("delete_outdated_snapshot_lo",
                "SELECT lo_unlink(r.snapshot) FROM SnapshotData r WHERE watermark_bn < $1 OR watermark_ts < $2;");
   conn.prepare("delete_outdated_snapshot_data",
                "DELETE FROM SnapshotData WHERE watermark_bn < $1 OR watermark_ts < $2;");
} // namespace blockvault

bool postgres_backend::propose_constructed_block(std::pair<uint32_t, uint32_t> watermark, uint32_t lib,
                                                 const std::vector<char>& block_content, std::string_view block_id,
                                                 std::string_view previous_block_id) {
   try {
      pqxx::work w(conn);

      pqxx::largeobjectaccess obj(w);
      obj.write(nullptr, 0);
      pqxx::binarystring      block_id_blob(block_id.data(), block_id.size());
      pqxx::binarystring      previous_block_id_blob(previous_block_id.data(), previous_block_id.size());
      w.exec_prepared0("insert_constructed_block", watermark.first, watermark.second, lib, block_id_blob,
                       previous_block_id_blob, obj.id(), block_content.size());
      auto r = w.exec_prepared1("get_block_insertion_result", obj.id());
      obj.write(block_content.data(), block_content.size());
      w.commit();
      return true;
   } catch (pqxx::unexpected_rows&) {
      return false;
   }
}

bool postgres_backend::append_external_block(uint32_t block_num, uint32_t lib, const std::vector<char>& block_content,
                                             std::string_view block_id, std::string_view previous_block_id) {
   try {
      pqxx::work w(conn);

      pqxx::largeobjectaccess obj(w);
      obj.write(nullptr, 0);
      pqxx::binarystring      block_id_blob(block_id.data(), block_id.size());
      pqxx::binarystring      previous_block_id_blob(previous_block_id.data(), previous_block_id.size());
      w.exec_prepared0("insert_external_block", block_num, lib, block_id_blob, previous_block_id_blob, obj.id(),
                       block_content.size());
      auto r = w.exec_prepared1("get_block_insertion_result", obj.id());
      obj.write(block_content.data(), block_content.size());
      w.commit();
      return true;

   } catch (pqxx::unexpected_rows& ex) {
      return false;
   }
}

bool postgres_backend::propose_snapshot(std::pair<uint32_t, uint32_t> watermark, const char* snapshot_filename) {

   try {
      std::filebuf infile;

      infile.open(snapshot_filename, std::ios::in);

      pqxx::work              w(conn);
      pqxx::largeobjectaccess obj(w);
      obj.write(nullptr, 0);

      w.exec_prepared0("insert_snapshot", watermark.first, watermark.second, obj.id());
      auto r = w.exec_prepared("get_snapshot_insertion_result", obj.id());

      if (!r.empty()) {

         const int chunk_size = 4096;
         char      chunk[chunk_size];

         auto sz = chunk_size;
         while (sz == chunk_size) {
            sz = infile.sgetn(chunk, chunk_size);
            obj.write(chunk, sz);
         };

         w.exec_prepared("delete_outdated_block_lo", watermark.first, watermark.second);
         w.exec_prepared("delete_outdated_block_data", watermark.first, watermark.second);
         w.exec_prepared("delete_outdated_snapshot_lo", watermark.first, watermark.second);
         w.exec_prepared("delete_outdated_snapshot_data", watermark.first, watermark.second);
      }

      w.commit();
      return !r.empty();

   } catch (pqxx::unexpected_rows&) {
      return false;
   }
}

void retrieve_blocks(backend::sync_callback& callback, pqxx::work& trx, pqxx::result r) {
   std::vector<char> block_data;

   for (const auto& x : r) {
      pqxx::oid               block_oid  = x[0].as<pqxx::oid>();
      uint64_t                block_size = x[1].as<uint64_t>();
      pqxx::largeobjectaccess obj(trx, block_oid, std::ios::in | std::ios::binary);
      block_data.resize(block_size);
      obj.read(block_data.data(), block_size);
      callback.on_block(std::string_view{block_data.data(), block_data.size()});
   }

   trx.commit();
}

void postgres_backend::sync(std::string_view previous_block_id, backend::sync_callback& callback) {
   pqxx::work trx(conn);

   if (previous_block_id.size()) {
      pqxx::binarystring blob(previous_block_id.data(), previous_block_id.size());
      auto               r = trx.exec_prepared("get_sync_watermark", blob);

      if (!r.empty()) {
         retrieve_blocks(
             callback, trx,
             trx.exec_prepared("get_blocks_since_watermark", r[0][0].as<uint32_t>(), r[0][1].as<uint32_t>()));
         return;
      }
   }

   auto r = trx.exec_prepared("get_latest_snapshot");

   if (!r.empty()) {
      pqxx::largeobject snapshot_obj(r[0][0].as<pqxx::oid>());

      fc::temp_file temp_file;
      std::string   fname = temp_file.path().string();

      snapshot_obj.to_file(trx, fname.c_str());
      callback.on_snapshot(fname.c_str());
   }

   retrieve_blocks(callback, trx, trx.exec_prepared("get_all_blocks"));
}

} // namespace blockvault
} // namespace eosio<|endoftext|>