This commit is contained in:
Ben Harris 2016-11-14 16:59:30 -05:00
parent d628315a5f
commit 3e6baa5f7e
5 changed files with 311 additions and 141 deletions

281
bish.cc
View File

@ -21,168 +21,175 @@ using namespace std;
int main(int argc, char **argv){
signal(SIGINT, ctrlCHandler);
signal(SIGINT, ctrlCHandler);
stringstream prompt;
static char* line = (char*)NULL;
stringstream prompt;
static char* line = (char*)NULL;
const char *homedir;
if ((homedir = getenv("HOME")) == NULL) {
homedir = getpwuid(getuid())->pw_dir;
}
const char *homedir;
if ((homedir = getenv("HOME")) == NULL) {
homedir = getpwuid(getuid())->pw_dir;
}
// set up history
using_history();
string histpath = string(homedir) + string("/.bish_history");
if (read_history(histpath.c_str())) {
cout << "history file not found. creating `~/.bish_history`." << endl;
int hist = open(histpath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
close(hist);
}
// set up history
using_history();
string histpath = string(homedir) + string("/.bish_history");
if (read_history(histpath.c_str())) {
cout << "history file not found. creating `~/.bish_history`." << endl;
int hist = open(histpath.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IRGRP | S_IROTH);
close(hist);
}
int done = 0;
while (!done) {
int done = 0;
while (!done) {
prompt.str("");
prompt << "\e[34mbish:\e[92m" << get_current_dir_name() << "\e[34m:$\e[0m ";
prompt.str("");
prompt << "\e[34mbish:\e[92m" << get_current_dir_name() << "\e[34m:$\e[0m ";
line = readline(prompt.str().c_str());
if (line == NULL) break;
if (strcmp(line, "") == 0) continue;
if (line && *line) add_history (line);
line = readline(prompt.str().c_str());
if (line == NULL) break;
if (strcmp(line, "") == 0) continue;
if (line && *line) add_history (line);
// handle multiple commands w/ semicolon
vector<string> wfwe = split(line, ';');
for (auto it: wfwe) {
// handle multiple commands w/ semicolon
vector<string> wfwe = split(line, ';');
for (auto it: wfwe) {
command *cmd = parse(split(it.c_str()));
// debug info
print_cmd(cmd);
cout << "-----------------------------" << endl;
command *cmd = parse(split(it.c_str()));
int num_cmds = cmd->cmds.size();
// debug info
print_cmd(cmd);
// clear line var
free(line);
line = (char*)NULL;
// clear line var
free(line);
line = (char*)NULL;
if (cmd->cmds[0].vargs[0] == "exit") {
done = 1;
break;
}
if (cmd->cmds[0].vargs[0] == "cd") {
if (cmd->cmds[0].vargs[1] == "") {
if (chdir(homedir) < 0) perror("chdir");
} else {
if (chdir(cmd->cmds[0].vargs[1].c_str()) < 0) perror("chdir");
}
}
for (auto curr: cmd->cmds) {
// else { // do fork/exec
pid_t kidpid = fork();
// fork error
if (kidpid < 0) {
perror("fork");
return -1;
}
// run it
// also check the path for things
else if (kidpid == 0){
int infd = 0, outfd = 1;
// io redirection
if (curr.outfile != "") {
outfd = open(curr.outfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
if (outfd < 0) {
perror("outfile");
exit(0);
}
if (cmd->cmds[0].vargs[0] == "exit") {
done = 1;
break;
}
if (curr.infile != "") {
infd = open(curr.infile.c_str(), O_RDONLY);
if (infd < 0) {
perror("infile");
exit(0);
}
}
bishexec(&curr, infd, outfd);
exit(1);
} // end child
// parent waits for kid to die
else {
int status;
if (!cmd->background){
do {
if (waitpid(kidpid, &status, WUNTRACED | WCONTINUED) == -1) {
perror("waitpid");
exit(1);
if (cmd->cmds[0].vargs[0] == "cd") {
if (cmd->cmds[0].vargs[1] == "") {
if (chdir(homedir) < 0) perror("chdir");
} else {
if (chdir(cmd->cmds[0].vargs[1].c_str()) < 0) perror("chdir");
}
if (WIFEXITED(status)) {
cout << "(" << WEXITSTATUS(status) << "):";
} else if (WIFSIGNALED(status)) {
cout << endl << "killed by signal " << WTERMSIG(status) << endl;
} else if (WIFSTOPPED(status)) {
cout << endl << "stopped by signal " << WSTOPSIG(status) << endl;
} else if (WIFCONTINUED(status)) {
cout << endl << "continued" << endl;
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
}
} // end parent
// int i;
int in = 0, fd[2];
for (int i = 0; i < num_cmds-1; i++) {
pipe(fd);
dup_io(in, fd[1], true);
bish_expandexec(&cmd->cmds[i]);
close(fd[1]);
in = fd[0];
}
if (in != 0) {
dup_io(in, 1);
bish_expandexec(&cmd->cmds[num_cmds]);
}
// } // end fork/exec stuff
}
// // else { // do fork/exec
// pid_t kidpid = fork();
// // fork error
// if (kidpid < 0) {
// perror("fork");
// return -1;
// }
// COMMANDS that do something with the line before fork/exec
// if (strcmp(cmd->cmds[0]->args[0], "!") == 0) {
// line = history_get(where_history())->line;
// cout << line << endl;
// cmd = parse(split(line));
// }
// // run it
// // also check the path for things
// else if (kidpid == 0){
// int infd = 0, outfd = 1;
// else if (strcmp(cmd->cmds[0]->args[0], ".") == 0 || strcmp(cmd->cmds[0]->args[0], "source") == 0) {
// int dotsrcfile = open(cmd->cmds[0]->args[1], O_RDONLY);
// if (dotsrcfile < 0) {
// perror("dotsrcfile");
// continue;
// }
// // do the thing to read the files
// }
// if (num_cmds > 1) {
// int pipefd[2];
// pipe(pipefd);
// pid_t nextkidpid = fork();
// if (nextkidpid < 0) {
// perror("fork");
// return -1;
// }
// else if (nextkidpid == 0) {
// close(pipefd[0]);
// outfd = pipefd[1];
// bishexec(&curr, infd, outfd);
// }
// }
// COMMANDS that skip fork/exec
// http://www.linuxquestions.org/questions/programming-9/making-a-c-shell-775690/
// if (strcmp(cmd->cmds[0]->args[0], "exit") == 0) break;
// // io redirection
// if (curr.outfile != "") {
// outfd = open(curr.outfile.c_str(), O_RDWR | O_CREAT | O_TRUNC, 0644);
// if (outfd < 0) {
// perror("outfile");
// exit(0);
// }
// }
// if (curr.infile != "") {
// infd = open(curr.infile.c_str(), O_RDONLY);
// if (infd < 0) {
// perror("infile");
// exit(0);
// }
// }
// handle chdir
// else if (strcmp(cmd->cmds[0]->args[0], "cd") == 0) {
// if (cmd->cmds[0]->args[1] == NULL) {
// if (chdir(homedir) < 0) perror("chdir");
// }
// else {
// if (chdir(cmd->cmds[0]->args[1]) < 0) perror("chdir");
// }
// }
// bishexec(&curr, infd, outfd);
// exit(1);
// process the line.
// else {
// } // end child
// // parent waits for kid to die
// else {
// int status;
// if (!cmd->background){
// do {
// if (waitpid(kidpid, &status, WUNTRACED | WCONTINUED) == -1) {
// perror("waitpid");
// exit(1);
// }
// if (WIFEXITED(status)) {
// cout << "(" << WEXITSTATUS(status) << "):";
// } else if (WIFSIGNALED(status)) {
// cout << endl << "killed by signal " << WTERMSIG(status) << endl;
// } else if (WIFSTOPPED(status)) {
// cout << endl << "stopped by signal " << WSTOPSIG(status) << endl;
// } else if (WIFCONTINUED(status)) {
// cout << endl << "continued" << endl;
// }
// } while (!WIFEXITED(status) && !WIFSIGNALED(status));
// }
// } // end parent
// } // end execute area
// COMMANDS that do something with the line before fork/exec
// if (strcmp(cmd->cmds[0]->args[0], "!") == 0) {
// line = history_get(where_history())->line;
// cout << line << endl;
// cmd = parse(split(line));
// }
// else if (strcmp(cmd->cmds[0]->args[0], ".") == 0 || strcmp(cmd->cmds[0]->args[0], "source") == 0) {
// int dotsrcfile = open(cmd->cmds[0]->args[1], O_RDONLY);
// if (dotsrcfile < 0) {
// perror("dotsrcfile");
// continue;
// }
// // do the thing to read the files
// }
// reset args array for the next prompt
delete cmd;
} // end ';' split
} // end main while loop
cout << endl;
if (write_history(histpath.c_str())) perror("write_history");
// reset args array for the next prompt
delete cmd;
return 0;
} // end ';' split
} // end main while loop
cout << endl;
if (write_history(histpath.c_str())) perror("write_history");
return 0;
}

View File

@ -11,16 +11,20 @@ using namespace std;
void print_cmd(command *cmd) {
int i = 0;
cout << "-----------------------------" << endl;
for (auto cmd_iter: cmd->cmds) {
if (cmd->background) cout << "backgroud: true" << endl;
cout << "simple_command: " << i++ << endl;
cout << "command " << i++ << endl;
cout << "infile: " << cmd_iter.infile << endl;
cout << "outfile: " << cmd_iter.outfile << endl << endl;
cout << ">>\t";
for (auto scmd_iter: cmd_iter.vargs) {
cout << scmd_iter << " ";
cout << "\"" << scmd_iter << "\" ";
}
cout << endl;
cout << endl << endl;
}
cout << "-----------------------------" << endl;
}

View File

@ -10,7 +10,6 @@ using namespace std;
// simple_command struct
struct simple_command {
vector<string> vargs;
// char** args;
string infile;
string outfile;
};

View File

@ -9,6 +9,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <wordexp.h>
#include <sys/wait.h>
#include "parse.h"
#include "util_fns.h"
using namespace std;
@ -44,6 +46,10 @@ char** v_to_cpp(vector<string> vargs) {
void bishexec(simple_command* cmd, int infd, int outfd) {
// if (!remap_pipe_stdin_stdout(infd, outfd)) {
// perror("dup2");
// // return;
// }
if (outfd != 1) {
if (dup2(outfd, 1) == -1) {
perror("dup2 outfile");
@ -72,3 +78,153 @@ void bishexec(simple_command* cmd, int infd, int outfd) {
// nothing found here...
cout << "that's not a command, bish" << endl;
}
void dup_io(int infd, int outfd, bool ispipe) {
cout << "in: " << infd << "out: " << outfd << endl;
if (ispipe) {
if (!remap_pipe_stdin_stdout(infd, outfd)) {
perror("dup2");
return;
}
}
else {
if (outfd != 1) {
if (dup2(outfd, 1) == -1) {
perror("dup2 outfile");
exit(0);
}
}
if (infd != 0) {
if (dup2(infd, 0) == -1) {
perror("dup2 infile");
exit(0);
}
}
}
}
int bish_expandexec(simple_command* cmd) {
return expand_and_execute(cmd->vargs[0].c_str(), v_to_cpp(cmd->vargs));
}
// http://www.gnu.org/software/libc/manual/html_node/Wordexp-Example.html
int expand_and_execute (const char *program, char **options) {
wordexp_t result;
pid_t pid;
int status, i;
/* Expand the string for the program to run. */
switch (wordexp (program, &result, 0)) {
case 0: /* Successful. */
break;
case WRDE_NOSPACE:
/* If the error was WRDE_NOSPACE,
then perhaps part of the result was allocated. */
wordfree (&result);
default: /* Some other error. */
return -1;
}
/* Expand the strings specified for the arguments. */
for (i = 0; options[i] != NULL; i++) {
if (wordexp (options[i], &result, WRDE_APPEND)) {
wordfree (&result);
return -1;
}
}
pid = fork ();
if (pid == 0) {
/* This is the child process. Execute the command. */
execvp (result.we_wordv[0], result.we_wordv);
exit (EXIT_FAILURE);
}
else if (pid < 0)
/* The fork failed. Report failure. */
status = -1;
else
/* This is the parent process. Wait for the child to complete. */
if (waitpid (pid, &status, 0) != pid)
status = -1;
wordfree (&result);
return status;
}
#define DUP2CLOSE(oldfd, newfd) (dup2(oldfd, newfd) == 0 && close(oldfd) == 0)
// http://unixwiz.net/techtips/remap-pipe-fds.c.txt
// !!!!!!!
bool remap_pipe_stdin_stdout(int rpipe, int wpipe) {
/*------------------------------------------------------------------
* CASE [A]
*
* This is the trivial case that probably never happens: the two FDs
* are already in the right place and we have nothing to do. Though
* this probably doesn't happen much, it's guaranteed that *doing*
* any shufflingn would close descriptors that shouldn't have been.
*/
if ( rpipe == 0 && wpipe == 1 ) return true;
/*----------------------------------------------------------------
* CASE [B] and [C]
*
* These two have the same handling but not the same rules. In case
* [C] where both FDs are "out of the way", it doesn't matter which
* of the FDs is closed first, but in case [B] it MUST be done in
* this order.
*/
if ( rpipe >= 1 && wpipe > 1 ) {
return DUP2CLOSE(rpipe, 0)
&& DUP2CLOSE(wpipe, 1);
}
/*----------------------------------------------------------------
* CASE [D]
* CASE [E]
*
* In these cases, *one* of the FDs is already correct and the other
* one can just be dup'd to the right place:
*/
if ( rpipe == 0 && wpipe >= 1 )
return DUP2CLOSE(wpipe, 1);
if ( rpipe >= 1 && wpipe == 1 )
return DUP2CLOSE(rpipe, 0);
/*----------------------------------------------------------------
* CASE [F]
*
* Here we have the write pipe in the read slot, but the read FD
* is out of the way: this means we can do this in just two steps
* but they MUST be in this order.
*/
if ( rpipe >= 1 && wpipe == 0 ) {
return DUP2CLOSE(wpipe, 1)
&& DUP2CLOSE(rpipe, 0);
}
/*----------------------------------------------------------------
* CASE [G]
*
* This is the trickiest case because the two file descriptors are
* *backwards*, and the only way to make it right is to make a
* third temporary FD during the swap.
*/
if ( rpipe == 1 && wpipe == 0 ) {
const int tmp = dup(wpipe); /* NOTE! this is not dup2() ! */
return tmp > 1
&& close(wpipe) == 0
&& DUP2CLOSE(rpipe, 0)
&& DUP2CLOSE(tmp, 1);
}
/* SHOULD NEVER GET HERE */
return false;
}

View File

@ -13,5 +13,9 @@ void ctrlCHandler(int sig);
vector<string> split(const char *str, char c = ' ');
char** v_to_cpp(vector<string> vargs);
void bishexec(simple_command* cmd, int infd, int outfd);
bool remap_pipe_stdin_stdout(int rpipe, int wpipe);
int expand_and_execute (const char *program, char **options);
int bish_expandexec(simple_command* cmd);
void dup_io(int infd, int outfd, bool ispipe = false);
#endif