/*
 * hpsh: Hal Perkins' SHell
 * Just a simple shell. Dedicated to Hal Perkins.
 * Make sure to compile with C++11 support!
 *
 * Note: valgrind will complain about memory leaks. They
 * are all fine. Don't worry about it.
 */

#include <iostream>
#include <sstream>
#include <string>
#include <vector>

#include <string.h>
#include <unistd.h>

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

using std::cerr;
using std::cin;
using std::cout;
using std::endl;
using std::getline;
using std::string;
using std::stringstream;
using std::vector;

static vector<string> tokenize(const string& line);
static int parse_line(const string& prompt);

int main() {
  int ret_value;
  while (1) {
    string prompt;
    cout << "hpsh% ";
    if (!getline(cin, prompt).good()) {
      cout << "exit" << endl;
      prompt = "exit";
    }
    ret_value = parse_line(prompt);
  }
  return ret_value;
}

vector<string> tokenize(const string& line) {
  vector<string> tokens;
  size_t cur = 0;
  bool curspace = true;
  for (size_t i = 0; i < line.size(); i++) {
    bool onspace = isspace(line[i]);
    if (onspace && !curspace) {
      tokens.emplace_back(line, cur, i - cur);
      curspace = true;
    } else if (!onspace && curspace) {
      cur = i;
      curspace = false;
    }
  }
  if (!curspace) {
    tokens.emplace_back(line, cur);
  }
  return tokens;
}

int parse_line(const string& prompt) {
  static int ret_value = EXIT_SUCCESS;
  vector<string> tokens = tokenize(prompt);

  if (tokens.size() == 0)
    return ret_value;

  if (tokens[0] == "exit") {
    if (tokens.size() > 1) {
      stringstream ss (tokens[1]);
      ss >> ret_value;
    }
    exit(ret_value);
  } else if (tokens[0] == ".") {
    // MISSING:
    // open the file specified by tokens[1],
    // and execute each line as if the user had typed it at the prompt.
    // Left as an exercise to the reader :)
  } else if (tokens[0] == "cd") {
    string dir;
    if (tokens.size() > 1) {
      dir = tokens[1];
    } else {
      dir = getenv("HOME");
    }
    if (chdir(dir.c_str()) == -1) {
      cerr << "cd: " << dir << ": No such file or directory" << endl;
    }
  } else {
    int pid = fork();
    if (pid < 0) {
      cerr << "Could not fork" << endl;
      return errno;
    } else if (pid == 0) {
      char **argv = new char*[tokens.size() + 1];
      for (size_t i = 0; i < tokens.size(); i++) {
        argv[i] = strdup(tokens[i].c_str());
      }
      argv[tokens.size()] = NULL;

      if (execvp(tokens[0].c_str(), argv) == -1) {
        cerr << tokens[0] << ": No such file or directory" << endl;
        exit(EXIT_FAILURE);
      }
    }

    int statloc = 0;
    waitpid(pid, &statloc, 0);
    ret_value = WEXITSTATUS(statloc);
  }

  return ret_value;
}