Improved the shell with pipes, background processes and semicolons.
It's really hacky, but it's an improvement.
This commit is contained in:
parent
0ed0082070
commit
98d1ccfa7d
195
utils/mxsh.cpp
195
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[])
|
||||
|
|
Loading…
Reference in New Issue