main.cpp
#include <iostream>
#include "JShell.h"
int main() {
std::cout << "Hello, JShell!" << std::endl;
JShell jShell;
jShell.execute();
return 0;
}
JShell.h
//
// Created by JieWei on 8/22/16.
//
#ifndef UNIXAPI_JSHELL_H
#define UNIXAPI_JSHELL_H
#include <string>
#include <map>
#include <vector>
#include "cmds.h"
using namespace std;
class JShell {
const char* _promot = "JShell > ";
int handleCommands(string line);
int handleCommand(string command);
int handleCommand(vector<string> segs, string input, string& output1, string& output2);
int single_exec(vector<string> segs, string input, string& output1, string& output2);
std::map<string, cmd_type> buildin_cmds;
void log(string str);
public:
JShell();
void execute();
};
#endif //UNIXAPI_JSHELL_H
JShell.cpp
//
// Created by JieWei on 8/22/16.
//
#include <iostream>
#include <vector>
#include <sstream>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdio>
#include <algorithm>
#include <memory>
#include <fstream>
#include <sstream>
#include "JShell.h"
#include "cmds.h"
using namespace std;
string& ltrim(string& s){
auto p = s.find_first_not_of(" ");
if (p > 0){
s.erase(0, p);
}
return s;
}
vector<string> split(string& str){
vector<string> ret;
stringstream ss(str);
string buf;
while(ss >> buf){
ret.push_back(buf);
}
return ret;
}
vector<string> split(const string &s, char delim) {
vector<string> ret;
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
ret.push_back(item);
}
return ret;
}
string join(vector<string>& segs, string glue = " "){
stringstream ss;
for(auto i = 0; i < segs.size(); ++i){
if(i != 0)
ss << glue;
ss << segs[i];
}
return ss.str();
}
vector<string> split_cmd(string& str){
vector<string> ret;
auto s1 = ltrim(str);
if(s1.length() == 0){
return ret;
}
auto sp = s1.find_first_of(' ');
if (sp == string::npos){
ret.push_back(str);
ret.push_back("");
}else{
ret.push_back(str.substr(0, sp));
ret.push_back(str.substr(sp+1));
}
return ret;
}
void write_file(string file, string content){
ofstream myfile;
myfile.open (file);
myfile << content;
myfile.close();
}
string read_file(string file){
std::ifstream t(file);
std::string str((std::istreambuf_iterator<char>(t)),
std::istreambuf_iterator<char>());
return str;
}
void JShell::execute() {
int st = 0;
while(st != 100) {
cout << _promot;
string line;
getline(cin, line);
st = handleCommands(line);
}
}
int JShell::handleCommand(string command) {
// handle command with redirect 2
auto segs = split(command);
string input_file, output1_file, output2_file;
auto input_iter = find(segs.begin(), segs.end(), "<");
if (input_iter != segs.end()){
input_file = *(input_iter+1);
segs.erase(input_iter, input_iter+2);
log( "redirect input from " + input_file );
}
auto output2_iter = find(segs.begin(), segs.end(), "2>");
if (output2_iter != segs.end()){
output2_file = *(output2_iter+1);
segs.erase(output2_iter, output2_iter+2);
log( "redirect output2 to " + output2_file );
}
auto output1_iter = find(segs.begin(), segs.end(), ">");
if (output1_iter != segs.end()) {
output1_file = *(output1_iter + 1);
segs.erase(output1_iter, output1_iter + 2);
log( "redirect output1 to " + output1_file );
}
string input = "";
string output1 = "";
string output2 = "";
if (!input_file.empty()){
input = read_file(input_file);
}
auto cmd_ret = handleCommand(segs, input, output1, output2);
if (!output1_file.empty()){
write_file(output1_file, output1);
}else{
if (!output1.empty())
cout << output1 << endl;
}
if (!output2_file.empty()){
write_file(output2_file, output2);
}else{
if (!output2.empty())
cout << output2 << endl;
}
return cmd_ret;
}
JShell::JShell() {
// initialize buildin commands
buildin_cmds["cd"] = &cd;
buildin_cmds["echo"] = &echo;
buildin_cmds["cmd1"] = &cmd1;
}
int JShell::handleCommand(vector<string> segs, string input, string& output1, string& output2) {
// handle commmand with build in cmds
string command = join(segs);
auto argv_segs = split_cmd(command);
int process_ret = 0;
if(segs.size() == 0){
log("Empty command");
return 0;
}else{
string cmd = segs[0];
if (cmd == "exit") {
return 100;
}
if (buildin_cmds.find(cmd) != buildin_cmds.end()){
log(cmd + " is a built in command!");
process_ret = buildin_cmds[cmd](argv_segs[1].c_str(), output1);
}else{
process_ret = single_exec(segs, input, output1, output2);
}
if (process_ret != 0) {
stringstream ss;
ss << process_ret;
log(cmd + " return non-zero: " + ss.str());
}
}
return 0;
}
int JShell::single_exec(vector<string> segs, string input, string& output1, string& output2) {
int pipe_in[2];
int pipe_out1[2];
int pipe_out2[2];
pipe(pipe_in);
pipe(pipe_out1);
pipe(pipe_out2);
auto pid = fork();
if (pid == 0){
// child
char** argv = new char*[segs.size()+1];
for(int i = 0; i < segs.size(); ++i){
argv[i] = const_cast<char*>(segs[i].c_str());
}
argv[segs.size()] = NULL;
if(!input.empty()){
dup2(pipe_in[0], STDIN_FILENO);
close(pipe_in[1]);
}
close(pipe_out1[0]);
dup2(pipe_out1[1], STDOUT_FILENO);
close(pipe_out2[0]);
dup2(pipe_out2[1], STDERR_FILENO);
int ret = execvp(segs[0].c_str(), argv);
delete [] argv;
exit(ret);
}else{ // parent
char buf[10240];
// input
if(!input.empty()){
close(pipe_in[0]);
write(pipe_in[1], input.c_str(), input.length());
close(pipe_in[1]);
}
close(pipe_out1[1]);
auto len = read(pipe_out1[0], buf, sizeof(buf));
buf[len] = 0; output1 = buf;
close(pipe_out2[1]);
len = read(pipe_out2[0], buf, sizeof(buf));
buf[len] = 0; output2 = buf;
int child_ret;
wait(&child_ret);
return child_ret;
}
}
void JShell::log(string str) {
// JShell's output format
cout << "*[" << str << "]*" << endl;
}
int JShell::handleCommands(string line) {
// handles command with pipe
int ret = 0;
auto cmds = split(line, '|');
switch(cmds.size()){
case 1: {
ret = handleCommand(line);
break;
}
case 2: {
log("pipe command");
string inp, output1, output2;
auto cmd1 = split(cmds[0]);
handleCommand(cmd1, inp, output1, output2);
string cmd2_output1, cmd2_output2;
auto cmd2 = split(cmds[1]);
ret = handleCommand(cmd2, output1 + output2, cmd2_output1, cmd2_output2);
if (!cmd2_output1.empty())
cout << cmd2_output1 << endl;
if (!cmd2_output2.empty())
cout << cmd2_output2 << endl;
break;
}
default: {
log("Unsupport multi pipe!");
}
}
return ret;
}
cmds.h
//
// Created by JieWei on 8/22/16.
//
#ifndef UNIXAPI_CMDS_H
#define UNIXAPI_CMDS_H
#include <iostream>
using namespace std;
typedef int (*cmd_type)(const char*, string&);
int echo(const char* argv, string& output);
int cd(const char *argv, string& output);
int cmd1(const char *argv, string& output);
#endif //UNIXAPI_CMDS_H
cmds.cpp
//
// Created by JieWei on 8/22/16.
//
// cmds.cpp contains commmands like binary in /usr/bin
#include <iostream>
#include <unistd.h>
#include "cmds.h"
int cd(const char *argv, string& output) {
int ret = chdir(argv);
output = string("I'm cd, i'm going to ") + argv;
return ret;
}
int echo(const char* argv, string& output) {
output = string(argv) + "\n";
return 0;
}
int cmd1(const char* argv, string& output) {
output = "cmd1... do nothing.\n";
return 0;
}
// add more built in commands here...