Skip to content
Snippets Groups Projects
Commit 1b25f3b0 authored by rsc's avatar rsc
Browse files

New shell.

parent f0d11fea
No related branches found
No related tags found
No related merge requests found
// Shell.
#include "types.h"
#include "stat.h"
#include "user.h"
#include "fcntl.h"
#define BUFSIZ 512
#define MAXARGS 10
#define MAXIO 2
#define MAXCMD 2
// Parsed command representation
#define EXEC 1
#define REDIR 2
#define PIPE 3
#define LIST 4
#define BACK 5
// an embarrassingly naive shell
#define MAXARGS 10
// some day a real parse tree; for now ad-hoc
struct ionode {
int token;
char *s;
struct cmd {
int type;
};
struct cmd {
struct execcmd {
int type;
char *argv[MAXARGS];
char argv0buf[BUFSIZ];
int argc;
int token;
struct ionode iolist[MAXIO];
struct ionode *io;
char *eargv[MAXARGS];
};
struct cmd cmdlist[MAXCMD];
struct cmd *cmd;
char buf[BUFSIZ];
int debug;
struct redircmd {
int type;
struct cmd *cmd;
char *file;
char *efile;
int mode;
int fd;
};
int parse(char *s);
void runcmd(void);
int getcmd(char *buf, int nbuf);
int ioredirection(struct ionode *iolist, int nio);
int gettoken(char *s, char **token);
int _gettoken(char *s, char **p1, char **p2);
struct pipecmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct listcmd {
int type;
struct cmd *left;
struct cmd *right;
};
struct backcmd {
int type;
struct cmd *cmd;
};
struct cmd *parsecmd(char*);
void panic(char*);
int
main(void)
fork1(void)
{
while(getcmd(buf, sizeof(buf)) >= 0) {
if(parse(buf) >= 0)
runcmd();
int pid;
pid = fork();
if(pid == -1)
panic("fork");
return pid;
}
// Execute cmd. Never returns.
void
runcmd(struct cmd *cmd)
{
int p[2];
struct backcmd *bcmd;
struct execcmd *ecmd;
struct listcmd *lcmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if(cmd == 0)
return;
switch(cmd->type){
default:
panic("runcmd");
case EXEC:
ecmd = (struct execcmd*)cmd;
if(ecmd->argv[0] == 0)
exit();
exec(ecmd->argv[0], ecmd->argv);
printf(2, "exec %s failed\n", ecmd->argv[0]);
break;
case REDIR:
rcmd = (struct redircmd*)cmd;
close(rcmd->fd);
if(open(rcmd->file, rcmd->mode) < 0){
printf(2, "open %s failed\n", rcmd->file);
exit();
}
runcmd(rcmd->cmd);
break;
case PIPE:
pcmd = (struct pipecmd*)cmd;
if(pipe(p) < 0)
panic("pipe");
if(fork1() == 0){
close(1);
dup(p[1]);
close(p[0]);
close(p[1]);
runcmd(pcmd->left);
}
if(fork1() == 0){
close(0);
dup(p[0]);
close(p[0]);
close(p[1]);
runcmd(pcmd->right);
}
close(p[0]);
close(p[1]);
wait();
wait();
break;
case LIST:
lcmd = (struct listcmd*)cmd;
if(fork1() == 0)
runcmd(lcmd->left);
wait();
runcmd(lcmd->right);
break;
case BACK:
bcmd = (struct backcmd*)cmd;
if(fork1() == 0)
runcmd(bcmd->cmd);
break;
}
exit();
}
......@@ -59,282 +152,324 @@ getcmd(char *buf, int nbuf)
}
int
parse(char *s)
main(void)
{
char *t;
int c, i;
gettoken(s, 0);
static char buf[100];
cmd = &cmdlist[0];
for(i = 0; i < MAXCMD; i++) {
cmdlist[i].argc = 0;
cmdlist[i].token = 0;
cmdlist[i].io = cmdlist[i].iolist;
while(getcmd(buf, sizeof(buf)) >= 0) {
if(fork1() == 0)
runcmd(parsecmd(buf));
wait();
}
for(;;){
switch((c = gettoken(0, &t))) {
case 'w': // Add an argument
if(cmd->argc >= MAXARGS) {
printf(2, "too many arguments\n");
return -1;
}
cmd->argv[cmd->argc++] = t;
break;
exit();
}
case '>': // Input and output redirection
case '<':
// Grab the filename from the argument list
if(gettoken(0, &t) != 'w') {
printf(2, "syntax error: > not followed by word\n");
return -1;
}
if(cmd->io - cmd->iolist >= MAXIO) {
printf(2, "too many redirections\n");
return -1;
}
cmd->io->token = c;
cmd->io->s = t;
cmd->io++;
break;
void
panic(char *s)
{
printf(2, "%s\n", s);
exit();
}
case ';': // command sequence
case '|': // pipe
if(cmd->io - cmd->iolist >= MAXIO) {
printf(2, "too many redirections\n");
return -1;
}
cmd->token = c;
cmd++;
break;
// Constructors
case 0: // String is complete
return 0;
struct cmd*
execcmd(void)
{
struct execcmd *cmd;
default:
printf(2, "syntax error: bad return %d from gettoken", c);
return -1;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = EXEC;
return (struct cmd*)cmd;
}
}
}
struct cmd*
redircmd(struct cmd *subcmd, char *file, char *efile, int mode, int fd)
{
struct redircmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = REDIR;
cmd->cmd = subcmd;
cmd->file = file;
cmd->efile = efile;
cmd->mode = mode;
cmd->fd = fd;
return (struct cmd*)cmd;
}
void
runcmd(void)
struct cmd*
pipecmd(struct cmd *left, struct cmd *right)
{
int i, r, pid, tfd;
int fdarray[2];
struct cmd *c;
struct ionode *io;
// Return immediately if command line was empty.
if(cmdlist[0].argc == 0) {
if(debug)
printf(2, "EMPTY COMMAND\n");
return;
}
struct pipecmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = PIPE;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}
for(c = &cmdlist[0]; c <= cmd; c++) {
// Clean up command line.
// Read all commands from the filesystem: add an initial '/' to
// the command name.
// This essentially acts like 'PATH=/'.
if(c->argv[0][0] != '/') {
c->argv0buf[0] = '/';
strcpy(c->argv0buf + 1, c->argv[0]);
c->argv[0] = c->argv0buf;
}
c->argv[c->argc] = 0;
// Print the command.
if(debug) {
printf(2, "[%d] SPAWN:", getpid());
for(i = 0; c->argv[i]; i++)
printf(2, " %s", c->argv[i]);
for(io = c->iolist; io <= c->io; io++) {
printf(2, "%c %s", io->token, io->s);
}
printf(2, "\n");
}
struct cmd*
listcmd(struct cmd *left, struct cmd *right)
{
struct listcmd *cmd;
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = LIST;
cmd->left = left;
cmd->right = right;
return (struct cmd*)cmd;
}
if(strcmp(c->argv[0], "/cd") == 0) {
if(debug)
printf (2, "/cd %s is build in\n", c->argv[1]);
chdir(c->argv[1]);
return;
}
struct cmd*
backcmd(struct cmd *subcmd)
{
struct backcmd *cmd;
if(c->token == '|')
if(pipe(fdarray) < 0)
printf(2, "cmd %d pipe failed\n", c);
pid = fork();
if(pid == 0) {
if(c->token == '|') {
if(close(1) < 0)
printf(2, "close 1 failed\n");
if((tfd = dup(fdarray[1])) < 0)
printf(2, "dup failed\n");
if(close(fdarray[0]) < 0)
printf(2, "close fdarray[0] failed\n");
if(close(fdarray[1]) < 0)
printf(2, "close fdarray[1] failed\n");
}
if(c > cmdlist && (c-1)->token == '|') {
if(close(0) < 0)
printf(2, "close 0 failed\n");
if((tfd = dup(fdarray[0])) < 0)
printf(2, "dup failed\n");
if(close(fdarray[0]) < 0)
printf(2, "close fdarray[0] failed\n");
if(close(fdarray[1]) < 0)
printf(2, "close fdarray[1] failed\n");
}
if(ioredirection(c->iolist, c->io - c->iolist) < 0)
exit();
if((r = exec(c->argv0buf, (char**) c->argv)) < 0) {
printf(2, "exec %s: %d\n", c->argv[0], r);
exit();
}
} else if(pid > 0) {
int p;
if(debug)
printf(2, "[%d] FORKED child %d\n", getpid(), pid);
if(c > cmdlist && (c-1)->token == '|') {
close(fdarray[0]);
close(fdarray[1]);
}
if(c->token != '|') {
if(debug)
printf(2, "[%d] WAIT for children\n", getpid());
do {
p = wait();
if(debug)
printf(2, "[%d] WAIT child %d finished\n", getpid(), p);
} while(p > 0);
if(debug)
printf(2, "[%d] wait finished\n", getpid());
}
}
}
cmd = malloc(sizeof(*cmd));
memset(cmd, 0, sizeof(*cmd));
cmd->type = BACK;
cmd->cmd = subcmd;
return (struct cmd*)cmd;
}
// Parsing
char whitespace[] = " \t\r\n\v";
char symbols[] = "<|>&;()";
int
ioredirection(struct ionode *iolist, int nio)
peek(char **ps, char *es, char *toks)
{
int fd;
struct ionode *io;
char *s;
s = *ps;
while(s < es && strchr(whitespace, *s))
s++;
*ps = s;
return *s && strchr(toks, *s);
}
for(io = iolist; io < &iolist[nio]; io++) {
switch(io->token) {
case '<':
if(close(0) < 0)
printf(2, "close 0 failed\n");
if((fd = open(io->s, O_RDONLY)) < 0) {
printf(2, "failed to open %s for read: %d", io->s, fd);
return -1;
}
if(debug)
printf(2, "redirect 0 from %s\n", io->s);
break;
case '>':
if(close(1) < 0)
printf(2, "close 1 failed\n");
if((fd = open(io->s, O_WRONLY|O_CREATE)) < 0) {
printf(2, "failed to open %s for write: %d", io->s, fd);
exit();
}
if(debug)
printf(2, "redirect 1 to %s\n", io->s);
break;
int
gettoken(char **ps, char *es, char **q, char **eq)
{
char *s;
int ret;
s = *ps;
while(s < es && strchr(whitespace, *s))
s++;
if(q)
*q = s;
ret = *s;
switch(*s){
case 0:
break;
case '|':
case '(':
case ')':
case ';':
case '&':
case '<':
s++;
break;
case '>':
s++;
if(*s == '>'){
ret = '+';
s++;
}
break;
default:
ret = 'a';
while(s < es && !strchr(whitespace, *s) && !strchr(symbols, *s))
s++;
break;
}
return 0;
if(eq)
*eq = s;
while(s < es && strchr(whitespace, *s))
s++;
*ps = s;
return ret;
}
// 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.
void nulterminate(struct cmd*);
struct cmd *parseline(char**, char*);
struct cmd *parsepipe(char**, char*);
struct cmd *parseredirs(struct cmd*, char**, char*);
struct cmd *parseblock(char**, char*);
struct cmd *parseexec(char**, char*);
int
gettoken(char *s, char **p1)
struct cmd*
parsecmd(char *s)
{
static int c, nc;
static char *np1, *np2;
if(s) {
nc = _gettoken(s, &np1, &np2);
return 0;
char *es;
struct cmd *cmd;
es = s + strlen(s);
cmd = parseline(&s, es);
peek(&s, es, "");
if(s != es){
printf(2, "leftovers: %s\n", s);
panic("syntax");
}
c = nc;
*p1 = np1;
nc = _gettoken(np2, &np1, &np2);
return c;
nulterminate(cmd);
return cmd;
}
// 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)
struct cmd*
parseline(char **ps, char *es)
{
int t;
struct cmd *cmd;
if(s == 0) {
if(debug > 1)
printf(2, "GETTOKEN 0\n");
return 0;
cmd = parsepipe(ps, es);
while(peek(ps, es, "&")){
gettoken(ps, es, 0, 0);
cmd = backcmd(cmd);
}
if(peek(ps, es, ";")){
gettoken(ps, es, 0, 0);
cmd = listcmd(cmd, parseline(ps, es));
}
return cmd;
}
if(debug > 1)
printf(2, "GETTOKEN: %s\n", s);
struct cmd*
parsepipe(char **ps, char *es)
{
struct cmd *cmd;
cmd = parseexec(ps, es);
if(peek(ps, es, "|")){
gettoken(ps, es, 0, 0);
cmd = pipecmd(cmd, parsepipe(ps, es));
}
return cmd;
}
*p1 = 0;
*p2 = 0;
struct cmd*
parseblock(char **ps, char *es)
{
struct cmd *cmd;
if(!peek(ps, es, "("))
panic("parseblock");
gettoken(ps, es, 0, 0);
cmd = parseline(ps, es);
if(!peek(ps, es, ")"))
panic("syntax - missing )");
gettoken(ps, es, 0, 0);
cmd = parseredirs(cmd, ps, es);
return cmd;
}
while(strchr(WHITESPACE, *s))
*s++ = 0;
if(*s == 0) {
if(debug > 1)
printf(2, "EOL\n");
return 0;
}
if(strchr(SYMBOLS, *s)) {
t = *s;
*p1 = s;
*s++ = 0;
*p2 = s;
if(debug > 1)
printf(2, "TOK %c\n", t);
return t;
struct cmd*
parseredirs(struct cmd *cmd, char **ps, char *es)
{
int tok;
char *q, *eq;
while(peek(ps, es, "<>")){
tok = gettoken(ps, es, 0, 0);
if(gettoken(ps, es, &q, &eq) != 'a')
panic("missing file for redirection");
switch(tok){
case '<':
cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
break;
case '>':
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
break;
case '+': // >>
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
break;
}
}
*p1 = s;
while(*s && !strchr(WHITESPACE SYMBOLS, *s))
s++;
*p2 = s;
if(debug > 1) {
t = **p2;
**p2 = 0;
printf(2, "WORD: %s\n", *p1);
**p2 = t;
return cmd;
}
struct cmd*
parseexec(char **ps, char *es)
{
char *q, *eq;
int tok, argc;
struct execcmd *cmd;
struct cmd *ret;
if(peek(ps, es, "("))
return parseblock(ps, es);
ret = execcmd();
cmd = (struct execcmd*)ret;
argc = 0;
ret = parseredirs(ret, ps, es);
while(!peek(ps, es, "|)&;")){
if((tok=gettoken(ps, es, &q, &eq)) == 0)
break;
if(tok != 'a')
panic("syntax");
cmd->argv[argc] = q;
cmd->eargv[argc] = eq;
argc++;
if(argc >= MAXARGS)
panic("too many args");
ret = parseredirs(ret, ps, es);
}
return 'w';
cmd->argv[argc] = 0;
cmd->eargv[argc] = 0;
return ret;
}
// NUL-terminate all the counted strings.
void
nulterminate(struct cmd *cmd)
{
int i;
struct backcmd *bcmd;
struct execcmd *ecmd;
struct listcmd *lcmd;
struct pipecmd *pcmd;
struct redircmd *rcmd;
if(cmd == 0)
return;
switch(cmd->type){
case EXEC:
ecmd = (struct execcmd*)cmd;
for(i=0; ecmd->argv[i]; i++)
*ecmd->eargv[i] = 0;
break;
case REDIR:
rcmd = (struct redircmd*)cmd;
nulterminate(rcmd->cmd);
*rcmd->efile = 0;
break;
case PIPE:
pcmd = (struct pipecmd*)cmd;
nulterminate(pcmd->left);
nulterminate(pcmd->right);
break;
case LIST:
lcmd = (struct listcmd*)cmd;
nulterminate(lcmd->left);
nulterminate(lcmd->right);
break;
case BACK:
bcmd = (struct backcmd*)cmd;
nulterminate(bcmd->cmd);
break;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment