From 98d1ccfa7ddeea5e0f7a760b9ced69d6f0ac5826 Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Fri, 2 Mar 2012 18:02:31 +0100 Subject: [PATCH] Improved the shell with pipes, background processes and semicolons. It's really hacky, but it's an improvement. --- utils/mxsh.cpp | 195 ++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 151 insertions(+), 44 deletions(-) diff --git a/utils/mxsh.cpp b/utils/mxsh.cpp index 5d0abc74..daa54207 100644 --- a/utils/mxsh.cpp +++ b/utils/mxsh.cpp @@ -10,6 +10,155 @@ int status = 0; +int runcommandline(const char** tokens) +{ + int result = 127; + size_t cmdnext = 0; + size_t cmdstart; + size_t cmdend; + bool lastcmd = false; + int pipein = 0; + int pipeout = 1; + int pipeinnext = 0; + char* const* argv; + size_t cmdlen; + const char* execmode; + const char* outputfile; + pid_t childpid; + bool internal; + int internalresult; +readcmd: + cmdstart = cmdnext; + for ( cmdend = cmdstart; true; cmdend++ ) + { + const char* token = tokens[cmdend]; + if ( !token || + strcmp(token, ";") == 0 || + strcmp(token, "&") == 0 || + strcmp(token, "|") == 0 || + strcmp(token, ">") == 0 || + false ) + { + break; + } + } + + cmdlen = cmdend - cmdstart; + if ( !cmdlen ) { fprintf(stderr, "expected command\n"); goto out; } + execmode = tokens[cmdend]; + if ( !execmode ) { lastcmd = true; execmode = ";"; } + tokens[cmdend] = NULL; + + if ( strcmp(execmode, "|") == 0 ) + { + int pipes[2]; + if ( pipe(pipes) ) { perror("pipe"); goto out; } + if ( pipeout != 1 ) { close(pipeout); } pipeout = pipes[1]; + if ( pipeinnext != 0 ) { close(pipeinnext); } pipeinnext = pipes[0]; + } + + outputfile = NULL; + if ( strcmp(execmode, ">") == 0 ) + { + outputfile = tokens[cmdend+1]; + if ( !outputfile ) { fprintf(stderr, "expected filename\n"); goto out; } + const char* nexttok = tokens[cmdend+2]; + if ( nexttok ) { fprintf(stderr, "too many filenames\n"); goto out; } + } + + cmdnext = cmdend + 1; + argv = (char* const*) (tokens + cmdstart); + + internal = false; + internalresult = 0; + if ( strcmp(argv[0], "cd") == 0 ) + { + internal = true; + const char* newdir = "/"; + if ( argv[1] ) { newdir = argv[1]; } + if ( chdir(newdir) ) + { + error(0, errno, "cd: %s", newdir); + internalresult = 1; + } + } + if ( strcmp(argv[0], "exit") == 0 ) + { + int exitcode = argv[1] ? atoi(argv[1]) : 0; + exit(exitcode); + } + + childpid = internal ? getpid() : fork(); + if ( childpid < 0 ) { perror("fork"); goto out; } + if ( childpid ) + { + if ( pipein != 0 ) { close(pipein); pipein = 0; } + if ( pipeout != 1 ) { close(pipeout); pipeout = 1; } + if ( pipeinnext != 0 ) { pipein = pipeinnext; pipeinnext = 0; } + + if ( strcmp(execmode, "&") == 0 && !tokens[cmdnext] ) + { + result = 0; goto out; + } + + if ( strcmp(execmode, "&") == 0 || strcmp(execmode, "|") == 0 ) + { + goto readcmd; + } + + int status = internalresult; + if ( !internal && waitpid(childpid, &status, 0) < 0 ) + { + perror("waitpid"); + return 127; + } + + if ( strcmp(execmode, ";") == 0 && tokens[cmdnext] && !lastcmd ) + { + goto readcmd; + } + + result = status; + goto out; + } + + if ( pipeinnext != 0 ) { close(pipeinnext); } + + if ( pipein != 0 ) + { + close(0); + dup(pipein); + close(pipein); + } + + if ( pipeout != 1 ) + { + close(1); + dup(pipeout); + close(pipeout); + } + + if ( outputfile ) + { + close(1); + if ( open(outputfile, O_CREAT | O_WRONLY | O_TRUNC | O_APPEND) < 0 ) + { + error(127, errno, "%s", outputfile); + } + } + + + execv(argv[0], argv); + error(127, errno, "%s", argv[0]); + return 127; + +out: + if ( pipein != 0 ) { close(pipein); } + if ( pipeout != 1 ) { close(pipeout); } + if ( pipeinnext != 0 ) { close(pipeout); } + return result; +} + void command() { unsigned termmode = TERMMODE_UNICODE @@ -75,51 +224,9 @@ void command() if ( !argv[0] ) { return; } - if ( strcmp(argv[0], "exit") == 0 ) - { - const char* status = "1"; - if ( 1 < argc ) { status = argv[1]; } - exit(atoi(status)); - } - - if ( strcmp(argv[0], "cd") == 0 ) - { - status = 0; - const char* newdir = "/"; - if ( 1 < argc ) { newdir = argv[1]; } - if ( chdir(newdir) ) - { - error(0, errno, "cd: %s", newdir); - status = 1; - } - return; - } - - pid_t child = fork(); - if ( child < 0 ) { perror("fork"); status = 1; return; } - if ( child != 0 ) - { - pid_t childpid = wait(&status); - return; - } - - if ( 3 <= argc ) - { - if ( strcmp(argv[argc-2], ">") == 0 ) - { - const char* file = argv[argc-1]; - int outfd = open(file, O_CREAT | O_WRONLY | O_TRUNC | O_APPEND); - if ( outfd < 0 ) { error(127, errno, "%s", file); exit(127); } - close(1); - dup(outfd); - close(outfd); - argc -= 2; - } - } - argv[argc] = NULL; - execv(argv[0], (char* const*) argv); - error(127, errno, "%s", argv[0]); + status = runcommandline(argv); + return; } int main(int argc, char* argv[])