mirror of
https://github.com/SmallPond/MIT6.828_OS.git
synced 2026-04-30 05:41:08 +08:00
my solution to lab5
This commit is contained in:
330
lab/user/sh.c
Normal file
330
lab/user/sh.c
Normal file
@@ -0,0 +1,330 @@
|
||||
#include <inc/lib.h>
|
||||
|
||||
#define BUFSIZ 1024 /* Find the buffer overrun bug! */
|
||||
int debug = 0;
|
||||
|
||||
|
||||
// gettoken(s, 0) prepares gettoken for subsequent calls and returns 0.
|
||||
// gettoken(0, token) parses a shell token from the previously set string,
|
||||
// null-terminates that token, stores the token pointer in '*token',
|
||||
// and returns a token ID (0, '<', '>', '|', or 'w').
|
||||
// Subsequent calls to 'gettoken(0, token)' will return subsequent
|
||||
// tokens from the string.
|
||||
int gettoken(char *s, char **token);
|
||||
|
||||
|
||||
// Parse a shell command from string 's' and execute it.
|
||||
// Do not return until the shell command is finished.
|
||||
// runcmd() is called in a forked child,
|
||||
// so it's OK to manipulate file descriptor state.
|
||||
#define MAXARGS 16
|
||||
void
|
||||
runcmd(char* s)
|
||||
{
|
||||
char *argv[MAXARGS], *t, argv0buf[BUFSIZ];
|
||||
int argc, c, i, r, p[2], fd, pipe_child;
|
||||
|
||||
pipe_child = 0;
|
||||
gettoken(s, 0);
|
||||
|
||||
again:
|
||||
argc = 0;
|
||||
while (1) {
|
||||
switch ((c = gettoken(0, &t))) {
|
||||
|
||||
case 'w': // Add an argument
|
||||
if (argc == MAXARGS) {
|
||||
cprintf("too many arguments\n");
|
||||
exit();
|
||||
}
|
||||
argv[argc++] = t;
|
||||
break;
|
||||
|
||||
case '<': // Input redirection
|
||||
// Grab the filename from the argument list
|
||||
if (gettoken(0, &t) != 'w') {
|
||||
cprintf("syntax error: < not followed by word\n");
|
||||
exit();
|
||||
}
|
||||
// Open 't' for reading as file descriptor 0
|
||||
// (which environments use as standard input).
|
||||
// We can't open a file onto a particular descriptor,
|
||||
// so open the file as 'fd',
|
||||
// then check whether 'fd' is 0.
|
||||
// If not, dup 'fd' onto file descriptor 0,
|
||||
// then close the original 'fd'.
|
||||
if ( (fd = open(t, O_RDONLY) )< 0 ) {
|
||||
fprintf(2,"file %s is no exist\n", t);
|
||||
exit();
|
||||
}
|
||||
if (fd != 0) {
|
||||
dup(fd, 0);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// LAB 5: Your code here.
|
||||
// panic("< redirection not implemented");
|
||||
break;
|
||||
|
||||
case '>': // Output redirection
|
||||
// Grab the filename from the argument list
|
||||
if (gettoken(0, &t) != 'w') {
|
||||
cprintf("syntax error: > not followed by word\n");
|
||||
exit();
|
||||
}
|
||||
if ((fd = open(t, O_WRONLY|O_CREAT|O_TRUNC)) < 0) {
|
||||
cprintf("open %s for write: %e", t, fd);
|
||||
exit();
|
||||
}
|
||||
if (fd != 1) {
|
||||
dup(fd, 1);
|
||||
close(fd);
|
||||
}
|
||||
break;
|
||||
|
||||
case '|': // Pipe
|
||||
if ((r = pipe(p)) < 0) {
|
||||
cprintf("pipe: %e", r);
|
||||
exit();
|
||||
}
|
||||
if (debug)
|
||||
cprintf("PIPE: %d %d\n", p[0], p[1]);
|
||||
if ((r = fork()) < 0) {
|
||||
cprintf("fork: %e", r);
|
||||
exit();
|
||||
}
|
||||
if (r == 0) {
|
||||
if (p[0] != 0) {
|
||||
dup(p[0], 0);
|
||||
close(p[0]);
|
||||
}
|
||||
close(p[1]);
|
||||
goto again;
|
||||
} else {
|
||||
pipe_child = r;
|
||||
if (p[1] != 1) {
|
||||
dup(p[1], 1);
|
||||
close(p[1]);
|
||||
}
|
||||
close(p[0]);
|
||||
goto runit;
|
||||
}
|
||||
panic("| not implemented");
|
||||
break;
|
||||
|
||||
case 0: // String is complete
|
||||
// Run the current command!
|
||||
goto runit;
|
||||
|
||||
default:
|
||||
panic("bad return %d from gettoken", c);
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
runit:
|
||||
// Return immediately if command line was empty.
|
||||
if(argc == 0) {
|
||||
if (debug)
|
||||
cprintf("EMPTY COMMAND\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Clean up command line.
|
||||
// Read all commands from the filesystem: add an initial '/' to
|
||||
// the command name.
|
||||
// This essentially acts like 'PATH=/'.
|
||||
if (argv[0][0] != '/') {
|
||||
argv0buf[0] = '/';
|
||||
strcpy(argv0buf + 1, argv[0]);
|
||||
argv[0] = argv0buf;
|
||||
}
|
||||
argv[argc] = 0;
|
||||
|
||||
// Print the command.
|
||||
if (debug) {
|
||||
cprintf("[%08x] SPAWN:", thisenv->env_id);
|
||||
for (i = 0; argv[i]; i++)
|
||||
cprintf(" %s", argv[i]);
|
||||
cprintf("\n");
|
||||
}
|
||||
|
||||
// Spawn the command!
|
||||
if ((r = spawn(argv[0], (const char**) argv)) < 0)
|
||||
cprintf("spawn %s: %e\n", argv[0], r);
|
||||
|
||||
// In the parent, close all file descriptors and wait for the
|
||||
// spawned command to exit.
|
||||
close_all();
|
||||
if (r >= 0) {
|
||||
if (debug)
|
||||
cprintf("[%08x] WAIT %s %08x\n", thisenv->env_id, argv[0], r);
|
||||
wait(r);
|
||||
if (debug)
|
||||
cprintf("[%08x] wait finished\n", thisenv->env_id);
|
||||
}
|
||||
|
||||
// If we were the left-hand part of a pipe,
|
||||
// wait for the right-hand part to finish.
|
||||
if (pipe_child) {
|
||||
if (debug)
|
||||
cprintf("[%08x] WAIT pipe_child %08x\n", thisenv->env_id, pipe_child);
|
||||
wait(pipe_child);
|
||||
if (debug)
|
||||
cprintf("[%08x] wait finished\n", thisenv->env_id);
|
||||
}
|
||||
|
||||
// Done!
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
// Get the next token from string s.
|
||||
// Set *p1 to the beginning of the token and *p2 just past the token.
|
||||
// Returns
|
||||
// 0 for end-of-string;
|
||||
// < for <;
|
||||
// > for >;
|
||||
// | for |;
|
||||
// w for a word.
|
||||
//
|
||||
// Eventually (once we parse the space where the \0 will go),
|
||||
// words get nul-terminated.
|
||||
#define WHITESPACE " \t\r\n"
|
||||
#define SYMBOLS "<|>&;()"
|
||||
|
||||
int
|
||||
_gettoken(char *s, char **p1, char **p2)
|
||||
{
|
||||
int t;
|
||||
|
||||
if (s == 0) {
|
||||
if (debug > 1)
|
||||
cprintf("GETTOKEN NULL\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (debug > 1)
|
||||
cprintf("GETTOKEN: %s\n", s);
|
||||
|
||||
*p1 = 0;
|
||||
*p2 = 0;
|
||||
|
||||
while (strchr(WHITESPACE, *s))
|
||||
*s++ = 0;
|
||||
if (*s == 0) {
|
||||
if (debug > 1)
|
||||
cprintf("EOL\n");
|
||||
return 0;
|
||||
}
|
||||
if (strchr(SYMBOLS, *s)) {
|
||||
t = *s;
|
||||
*p1 = s;
|
||||
*s++ = 0;
|
||||
*p2 = s;
|
||||
if (debug > 1)
|
||||
cprintf("TOK %c\n", t);
|
||||
return t;
|
||||
}
|
||||
*p1 = s;
|
||||
while (*s && !strchr(WHITESPACE SYMBOLS, *s))
|
||||
s++;
|
||||
*p2 = s;
|
||||
if (debug > 1) {
|
||||
t = **p2;
|
||||
**p2 = 0;
|
||||
cprintf("WORD: %s\n", *p1);
|
||||
**p2 = t;
|
||||
}
|
||||
return 'w';
|
||||
}
|
||||
|
||||
int
|
||||
gettoken(char *s, char **p1)
|
||||
{
|
||||
static int c, nc;
|
||||
static char* np1, *np2;
|
||||
|
||||
if (s) {
|
||||
nc = _gettoken(s, &np1, &np2);
|
||||
return 0;
|
||||
}
|
||||
c = nc;
|
||||
*p1 = np1;
|
||||
nc = _gettoken(np2, &np1, &np2);
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
usage(void)
|
||||
{
|
||||
cprintf("usage: sh [-dix] [command-file]\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
void
|
||||
umain(int argc, char **argv)
|
||||
{
|
||||
int r, interactive, echocmds;
|
||||
struct Argstate args;
|
||||
|
||||
interactive = '?';
|
||||
echocmds = 0;
|
||||
argstart(&argc, argv, &args);
|
||||
while ((r = argnext(&args)) >= 0)
|
||||
switch (r) {
|
||||
case 'd':
|
||||
debug++;
|
||||
break;
|
||||
case 'i':
|
||||
interactive = 1;
|
||||
break;
|
||||
case 'x':
|
||||
echocmds = 1;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
|
||||
if (argc > 2)
|
||||
usage();
|
||||
if (argc == 2) {
|
||||
close(0);
|
||||
if ((r = open(argv[1], O_RDONLY)) < 0)
|
||||
panic("open %s: %e", argv[1], r);
|
||||
assert(r == 0);
|
||||
}
|
||||
if (interactive == '?')
|
||||
interactive = iscons(0);
|
||||
|
||||
while (1) {
|
||||
char *buf;
|
||||
|
||||
buf = readline(interactive ? "$ " : NULL);
|
||||
if (buf == NULL) {
|
||||
if (debug)
|
||||
cprintf("EXITING\n");
|
||||
exit(); // end of file
|
||||
}
|
||||
if (debug)
|
||||
cprintf("LINE: %s\n", buf);
|
||||
if (buf[0] == '#')
|
||||
continue;
|
||||
if (echocmds)
|
||||
printf("# %s\n", buf);
|
||||
if (debug)
|
||||
cprintf("BEFORE FORK\n");
|
||||
if ((r = fork()) < 0)
|
||||
panic("fork: %e", r);
|
||||
if (debug)
|
||||
cprintf("FORK: %d\n", r);
|
||||
if (r == 0) {
|
||||
runcmd(buf);
|
||||
exit();
|
||||
} else
|
||||
wait(r);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user