/* sh - simple shell - great for early stages of porting */#include "u.h"#include "libc.h" #define MAXLINE 200 /* maximum line length */#define WORD 256 /* token code for words */#define EOF -1 /* token code for end of file */#define ispunct(c) (c=='|' || c=='&' || c==';' || c=='<' || \ c=='>' || c=='(' || c==')' || c=='\n')#define isspace(c) (c==' ' || c=='\t')#define execute(np) (np? (*(np)->op)(np) : 0) typedef struct Node Node;struct Node{ /* parse tree node */ int (*op)(Node *); /* operator function */ Node *args[2]; /* argument nodes */ char *argv[100]; /* argument pointers */ char *io[3]; /* i/o redirection */}; Node nodes[25]; /* node pool */Node *nfree; /* next available node */char strspace[10*MAXLINE]; /* string storage */char *sfree; /* next free character in strspace */int t; /* current token code */char *token; /* current token text (in strspace) */int putback = 0; /* lookahead */Waitmsg status; /* exit status of most recent command */int cflag = 0; /* command is argument to sh */int tflag = 0; /* read only one line */int interactive = 0; /* prompt */char *cflagp; /* command line for cflag */char *path[] ={"/bin", 0}; Node *alloc(int (*op)(Node *));int builtin(Node *np);Node *command(void);int getch(void);int gettoken(void);Node *list(void);void error(char *s, char *t);Node *pipeline(void);void redirect(Node *np);int setio(Node *np);Node *simple(void);int xpipeline(Node *np);int xsimple(Node *np);int xsubshell(Node *np);int xnowait(Node *np);int xwait(Node *np); main(int argc, char *argv[]){ Node *np; if(argc>1 && strcmp(argv[1], "-t")==0) tflag++; else if(argc>2 && strcmp(argv[1], "-c")==0){ cflag++; cflagp = argv[2]; }else if(argc>1){ close(0); if(open(argv[1], 0) != 0){ error(": can't open", argv[1]); exits("argument"); } }else interactive = 1; for(;;){ if(interactive) fprint(2, "%d$ ", getpid()); nfree = nodes; sfree = strspace; if((t=gettoken()) == EOF) break; if(t != '\n') if(np = list()) execute(np); else error("syntax error", ""); while(t!=EOF && t!='\n') /* flush syntax errors */ t = gettoken(); } exits(status.msg);} /* alloc - allocate for op and return a node */Node*alloc(int (*op)(Node *)){ if(nfree < nodes+sizeof(nodes)){ nfree->op = op; nfree->args[0] = nfree->args[1] = 0; nfree->argv[0] = nfree->argv[1] = 0; nfree->io[0] = nfree->io[1] = nfree->io[2] = 0; return nfree++; } error("node storage overflow", ""); exits("node storage overflow");} /* builtin - check np for builtin command and, if found, execute it */intbuiltin(Node *np){ int n = 0; char *s, name[MAXLINE]; if(np->argv[1]) n = strtoul(np->argv[1], 0, 0); if(strcmp(np->argv[0], "cd") == 0){ if(chdir(np->argv[1]? np->argv[1] : "/") == -1) error(": bad directory", np->argv[0]); return 1; }else if(strcmp(np->argv[0], "exit") == 0) exits(np->argv[1]? np->argv[1] : status.msg); else if(strcmp(np->argv[0], "bind") == 0){ if(np->argv[1]==0 || np->argv[2]==0) error("usage: bind new old", ""); else if(bind(np->argv[1], np->argv[2], 0)==-1) error("bind failed", ""); return 1;#ifdef asdf }else if(strcmp(np->argv[0], "unmount") == 0){ if(np->argv[1] == 0) error("usage: unmount [new] old", ""); else if(np->argv[2] == 0){ if(unmount((char *)0, np->argv[1]) == -1) error("unmount:", ""); }else if(unmount(np->argv[1], np->argv[2]) == -1) error("unmount", ""); return 1;#endif }else if(strcmp(np->argv[0], "wait") == 0){ while(wait(&status) != -1) if(n && status.pid==n){ n = 0; break; } if(n) error("wait error", ""); return 1; }else if(strcmp(np->argv[0], "newpgrp") == 0){ int i; i = forkpgrp(0); if(i < 0) error("forkpgrp error", ""); else fprint(2, "%d\n", i); return 1; }else if(strcmp(np->argv[0], "exec") == 0){ redirect(np); if(np->argv[1] == (char *) 0) return 1; exec(np->argv[1], &np->argv[1]); n = np->argv[1][0]; if(n!='/' && n!='#' && (n!='.' || np->argv[1][1]!='/')) for(n = 0; path[n]; n++){ sprint(name, "%s/%s", path[n], np->argv[1]); exec(name, &np->argv[1]); } error(": not found", np->argv[1]); return 1; } return 0;} /* command - ( list ) [ ( < | > | >> ) word ]* | simple */Node*command(void){ Node *np; if(t != '(') return simple(); np = alloc(xsubshell); t = gettoken(); if((np->args[0]=list())==0 || t!=')') return 0; while((t=gettoken())=='<' || t=='>') if(!setio(np)) return 0; return np;} /* getch - get next, possibly pushed back, input character */intgetch(void){ unsigned char c; static done=0; if(putback){ c = putback; putback = 0; }else if(tflag){ if(done || read(0, &c, 1)!=1){ done = 1; return EOF; } if(c == '\n') done = 1; }else if(cflag){ if(done) return EOF; if((c=*cflagp++) == 0){ done = 1; c = '\n'; } }else if(read(0, &c, 1) != 1) return EOF; return c;} /* gettoken - get next token into string space, return token code */intgettoken(void){ int c; while((c = getch()) != EOF) if(!isspace(c)) break; if(c==EOF || ispunct(c)) return c; token = sfree; do{ if(sfree >= strspace+sizeof(strspace) - 1){ error("string storage overflow", ""); exits("string storage overflow"); } *sfree++ = c; }while((c=getch()) != EOF && !ispunct(c) && !isspace(c)); *sfree++ = 0; putback = c; return WORD;} /* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ] (not LL(1), but ok) */Node*list(void){ Node *np, *np1; np = alloc(0); if((np->args[1]=pipeline()) == 0) return 0; while(t==';' || t=='&'){ np->op = (t==';')? xwait : xnowait; t = gettoken(); if(t==')' || t=='\n') /* tests ~first(pipeline) */ break; np1 = alloc(0); np1->args[0] = np; if((np1->args[1]=pipeline()) == 0) return 0; np = np1; } if(np->op == 0) np->op = xwait; return np;} /* error - print error message s, prefixed by t */voiderror(char *s, char *t){ char buf[ERRLEN]; fprint(2, "%s%s", t, s); errstr(buf); fprint(2, ": %s\n", buf);} /* pipeline - command ( | command )* */Node*pipeline(void){ Node *np, *np1; if((np=command()) == 0) return 0; while(t == '|'){ np1 = alloc(xpipeline); np1->args[0] = np; t = gettoken(); if((np1->args[1]=command()) == 0) return 0; np = np1; } return np;} /* redirect - redirect i/o according to np->io[] values */voidredirect(Node *np){ int fd; if(np->io[0]){ if((fd = open(np->io[0], 0)) < 0){ error(": can't open", np->io[0]); exits("open"); } dup(fd, 0); close(fd); } if(np->io[1]){ if((fd = create(np->io[1], 1, 0666L)) < 0){ error(": can't create", np->io[1]); exits("create"); } dup(fd, 1); close(fd); } if(np->io[2]){ if((fd = open(np->io[2], 1)) < 0 && (fd = create(np->io[2], 1, 0666L)) < 0){ error(": can't write", np->io[2]); exits("write"); } dup(fd, 1); close(fd); seek(1, 0, 2); }} /* setio - ( < | > | >> ) word; fill in np->io[] */intsetio(Node *np){ if(t == '<'){ t = gettoken(); np->io[0] = token; }else if(t == '>'){ t = gettoken(); if(t == '>'){ t = gettoken(); np->io[2] = token; }else np->io[1] = token; }else return 0; if(t != WORD) return 0; return 1;} /* simple - word ( [ < | > | >> ] word )* */Node*simple(void){ Node *np; int n = 1; if(t != WORD) return 0; np = alloc(xsimple); np->argv[0] = token; while((t = gettoken())==WORD || t=='<' || t=='>') if(t == WORD) np->argv[n++] = token; else if(!setio(np)) return 0; np->argv[n] = 0; return np;} /* xpipeline - execute cmd | cmd */intxpipeline(Node *np){ int pid, fd[2]; if(pipe(fd) < 0){ error("can't create pipe", ""); return 0; } if((pid=fork()) == 0){ /* left side; redirect stdout */ dup(fd[1], 1); close(fd[0]); close(fd[1]); execute(np->args[0]); exits(status.msg); }else if(pid == -1){ error("can't create process", ""); return 0; } if((pid=fork()) == 0){ /* right side; redirect stdin */ dup(fd[0], 0); close(fd[0]); close(fd[1]); pid = execute(np->args[1]); /*BUG: this is wrong sometimes*/ if(pid > 0) while(wait(&status)!=-1 && status.pid!=pid) ; exits(0); }else if(pid == -1){ error("can't create process", ""); return 0; } close(fd[0]); /* avoid using up fd's */ close(fd[1]); return pid;} /* xsimple - execute a simple command */intxsimple(Node *np){ char name[MAXLINE]; int pid, i; if(builtin(np)) return 0; if(pid = fork()){ if(pid == -1) error(": can't create process", np->argv[0]); return pid; } redirect(np); /* child process */ exec(np->argv[0], &np->argv[0]); i = np->argv[0][0]; if(i!='/' && i!='#' && (i!='.' || np->argv[0][1]!='/')) for(i = 0; path[i]; i++){ sprint(name, "%s/%s", path[i], np->argv[0]); exec(name, &np->argv[0]); } error(": not found", np->argv[0]); exits("not found");} /* xsubshell - execute (cmd) */intxsubshell(Node *np){ int pid; if(pid = fork()){ if(pid == -1) error("can't create process", ""); return pid; } redirect(np); /* child process */ execute(np->args[0]); exits(status.msg);} /* xnowait - execute cmd & */intxnowait(Node *np){ int pid; execute(np->args[0]); pid = execute(np->args[1]); if(interactive) fprint(2, "%d\n", pid); return 0;} /* xwait - execute cmd ; */int xwait(Node *np){ int pid; execute(np->args[0]); pid = execute(np->args[1]); if(pid > 0){ while(wait(&status) != -1 && status.pid != pid) ; if(status.pid != pid) error("wait error", ""); } return 0;}
/* sh - simple shell - great for early stages of porting */
#include "u.h"
#include "libc.h"
#define MAXLINE 200 /* maximum line length */
#define WORD 256 /* token code for words */
#define EOF -1 /* token code for end of file */
#define ispunct(c) (c=='|' || c=='&' || c==';' || c=='<' || \
c=='>' || c=='(' || c==')' || c=='\n')
#define isspace(c) (c==' ' || c=='\t')
#define execute(np) (np? (*(np)->op)(np) : 0)
typedef struct Node Node;
struct Node{ /* parse tree node */
int (*op)(Node *); /* operator function */
Node *args[2]; /* argument nodes */
char *argv[100]; /* argument pointers */
char *io[3]; /* i/o redirection */
};
Node nodes[25]; /* node pool */
Node *nfree; /* next available node */
char strspace[10*MAXLINE]; /* string storage */
char *sfree; /* next free character in strspace */
int t; /* current token code */
char *token; /* current token text (in strspace) */
int putback = 0; /* lookahead */
Waitmsg status; /* exit status of most recent command */
int cflag = 0; /* command is argument to sh */
int tflag = 0; /* read only one line */
int interactive = 0; /* prompt */
char *cflagp; /* command line for cflag */
char *path[] ={"/bin", 0};
Node *alloc(int (*op)(Node *));
int builtin(Node *np);
Node *command(void);
int getch(void);
int gettoken(void);
Node *list(void);
void error(char *s, char *t);
Node *pipeline(void);
void redirect(Node *np);
int setio(Node *np);
Node *simple(void);
int xpipeline(Node *np);
int xsimple(Node *np);
int xsubshell(Node *np);
int xnowait(Node *np);
int xwait(Node *np);
main(int argc, char *argv[])
{
Node *np;
if(argc>1 && strcmp(argv[1], "-t")==0)
tflag++;
else if(argc>2 && strcmp(argv[1], "-c")==0){
cflag++;
cflagp = argv[2];
}else if(argc>1){
close(0);
if(open(argv[1], 0) != 0){
error(": can't open", argv[1]);
exits("argument");
}
}else
interactive = 1;
for(;;){
if(interactive)
fprint(2, "%d$ ", getpid());
nfree = nodes;
sfree = strspace;
if((t=gettoken()) == EOF)
break;
if(t != '\n')
if(np = list())
execute(np);
else
error("syntax error", "");
while(t!=EOF && t!='\n') /* flush syntax errors */
t = gettoken();
}
exits(status.msg);
}
/* alloc - allocate for op and return a node */
Node*
alloc(int (*op)(Node *))
{
if(nfree < nodes+sizeof(nodes)){
nfree->op = op;
nfree->args[0] = nfree->args[1] = 0;
nfree->argv[0] = nfree->argv[1] = 0;
nfree->io[0] = nfree->io[1] = nfree->io[2] = 0;
return nfree++;
}
error("node storage overflow", "");
exits("node storage overflow");
}
/* builtin - check np for builtin command and, if found, execute it */
int
builtin(Node *np)
{
int n = 0;
char *s, name[MAXLINE];
if(np->argv[1])
n = strtoul(np->argv[1], 0, 0);
if(strcmp(np->argv[0], "cd") == 0){
if(chdir(np->argv[1]? np->argv[1] : "/") == -1)
error(": bad directory", np->argv[0]);
return 1;
}else if(strcmp(np->argv[0], "exit") == 0)
exits(np->argv[1]? np->argv[1] : status.msg);
else if(strcmp(np->argv[0], "bind") == 0){
if(np->argv[1]==0 || np->argv[2]==0)
error("usage: bind new old", "");
else if(bind(np->argv[1], np->argv[2], 0)==-1)
error("bind failed", "");
return 1;
#ifdef asdf
}else if(strcmp(np->argv[0], "unmount") == 0){
if(np->argv[1] == 0)
error("usage: unmount [new] old", "");
else if(np->argv[2] == 0){
if(unmount((char *)0, np->argv[1]) == -1)
error("unmount:", "");
}else if(unmount(np->argv[1], np->argv[2]) == -1)
error("unmount", "");
return 1;
#endif
}else if(strcmp(np->argv[0], "wait") == 0){
while(wait(&status) != -1)
if(n && status.pid==n){
n = 0;
break;
}
if(n)
error("wait error", "");
return 1;
}else if(strcmp(np->argv[0], "newpgrp") == 0){
int i;
i = forkpgrp(0);
if(i < 0)
error("forkpgrp error", "");
else
fprint(2, "%d\n", i);
return 1;
}else if(strcmp(np->argv[0], "exec") == 0){
redirect(np);
if(np->argv[1] == (char *) 0)
return 1;
exec(np->argv[1], &np->argv[1]);
n = np->argv[1][0];
if(n!='/' && n!='#' && (n!='.' || np->argv[1][1]!='/'))
for(n = 0; path[n]; n++){
sprint(name, "%s/%s", path[n], np->argv[1]);
exec(name, &np->argv[1]);
}
error(": not found", np->argv[1]);
return 1;
}
return 0;
}
/* command - ( list ) [ ( < | > | >> ) word ]* | simple */
Node*
command(void)
{
Node *np;
if(t != '(')
return simple();
np = alloc(xsubshell);
t = gettoken();
if((np->args[0]=list())==0 || t!=')')
return 0;
while((t=gettoken())=='<' || t=='>')
if(!setio(np))
return 0;
return np;
}
/* getch - get next, possibly pushed back, input character */
int
getch(void)
{
unsigned char c;
static done=0;
if(putback){
c = putback;
putback = 0;
}else if(tflag){
if(done || read(0, &c, 1)!=1){
done = 1;
return EOF;
}
if(c == '\n')
done = 1;
}else if(cflag){
if(done)
return EOF;
if((c=*cflagp++) == 0){
done = 1;
c = '\n';
}
}else if(read(0, &c, 1) != 1)
return EOF;
return c;
}
/* gettoken - get next token into string space, return token code */
int
gettoken(void)
{
int c;
while((c = getch()) != EOF)
if(!isspace(c))
break;
if(c==EOF || ispunct(c))
return c;
token = sfree;
do{
if(sfree >= strspace+sizeof(strspace) - 1){
error("string storage overflow", "");
exits("string storage overflow");
}
*sfree++ = c;
}while((c=getch()) != EOF && !ispunct(c) && !isspace(c));
*sfree++ = 0;
putback = c;
return WORD;
}
/* list - pipeline ( ( ; | & ) pipeline )* [ ; | & ] (not LL(1), but ok) */
Node*
list(void)
{
Node *np, *np1;
np = alloc(0);
if((np->args[1]=pipeline()) == 0)
return 0;
while(t==';' || t=='&'){
np->op = (t==';')? xwait : xnowait;
t = gettoken();
if(t==')' || t=='\n') /* tests ~first(pipeline) */
break;
np1 = alloc(0);
np1->args[0] = np;
if((np1->args[1]=pipeline()) == 0)
return 0;
np = np1;
}
if(np->op == 0)
np->op = xwait;
return np;
}
/* error - print error message s, prefixed by t */
void
error(char *s, char *t)
{
char buf[ERRLEN];
fprint(2, "%s%s", t, s);
errstr(buf);
fprint(2, ": %s\n", buf);
}
/* pipeline - command ( | command )* */
Node*
pipeline(void)
{
Node *np, *np1;
if((np=command()) == 0)
return 0;
while(t == '|'){
np1 = alloc(xpipeline);
np1->args[0] = np;
t = gettoken();
if((np1->args[1]=command()) == 0)
return 0;
np = np1;
}
return np;
}
/* redirect - redirect i/o according to np->io[] values */
void
redirect(Node *np)
{
int fd;
if(np->io[0]){
if((fd = open(np->io[0], 0)) < 0){
error(": can't open", np->io[0]);
exits("open");
}
dup(fd, 0);
close(fd);
}
if(np->io[1]){
if((fd = create(np->io[1], 1, 0666L)) < 0){
error(": can't create", np->io[1]);
exits("create");
}
dup(fd, 1);
close(fd);
}
if(np->io[2]){
if((fd = open(np->io[2], 1)) < 0 && (fd = create(np->io[2], 1, 0666L)) < 0){
error(": can't write", np->io[2]);
exits("write");
}
dup(fd, 1);
close(fd);
seek(1, 0, 2);
}
}
/* setio - ( < | > | >> ) word; fill in np->io[] */
int
setio(Node *np)
{
if(t == '<'){
t = gettoken();
np->io[0] = token;
}else if(t == '>'){
t = gettoken();
if(t == '>'){
t = gettoken();
np->io[2] = token;
}else
np->io[1] = token;
}else
return 0;
if(t != WORD)
return 0;
return 1;
}
/* simple - word ( [ < | > | >> ] word )* */
Node*
simple(void)
{
Node *np;
int n = 1;
if(t != WORD)
return 0;
np = alloc(xsimple);
np->argv[0] = token;
while((t = gettoken())==WORD || t=='<' || t=='>')
if(t == WORD)
np->argv[n++] = token;
else if(!setio(np))
return 0;
np->argv[n] = 0;
return np;
}
/* xpipeline - execute cmd | cmd */
int
xpipeline(Node *np)
{
int pid, fd[2];
if(pipe(fd) < 0){
error("can't create pipe", "");
return 0;
}
if((pid=fork()) == 0){ /* left side; redirect stdout */
dup(fd[1], 1);
close(fd[0]);
close(fd[1]);
execute(np->args[0]);
exits(status.msg);
}else if(pid == -1){
error("can't create process", "");
return 0;
}
if((pid=fork()) == 0){ /* right side; redirect stdin */
dup(fd[0], 0);
close(fd[0]);
close(fd[1]);
pid = execute(np->args[1]); /*BUG: this is wrong sometimes*/
if(pid > 0)
while(wait(&status)!=-1 && status.pid!=pid)
;
exits(0);
}else if(pid == -1){
error("can't create process", "");
return 0;
}
close(fd[0]); /* avoid using up fd's */
close(fd[1]);
return pid;
}
/* xsimple - execute a simple command */
int
xsimple(Node *np)
{
char name[MAXLINE];
int pid, i;
if(builtin(np))
return 0;
if(pid = fork()){
if(pid == -1)
error(": can't create process", np->argv[0]);
return pid;
}
redirect(np); /* child process */
exec(np->argv[0], &np->argv[0]);
i = np->argv[0][0];
if(i!='/' && i!='#' && (i!='.' || np->argv[0][1]!='/'))
for(i = 0; path[i]; i++){
sprint(name, "%s/%s", path[i], np->argv[0]);
exec(name, &np->argv[0]);
}
error(": not found", np->argv[0]);
exits("not found");
}
/* xsubshell - execute (cmd) */
int
xsubshell(Node *np)
{
int pid;
if(pid = fork()){
if(pid == -1)
error("can't create process", "");
return pid;
}
redirect(np); /* child process */
execute(np->args[0]);
exits(status.msg);
}
/* xnowait - execute cmd & */
int
xnowait(Node *np)
{
int pid;
execute(np->args[0]);
pid = execute(np->args[1]);
if(interactive)
fprint(2, "%d\n", pid);
return 0;
}
/* xwait - execute cmd ; */
int xwait(Node *np)
{
int pid;
execute(np->args[0]);
pid = execute(np->args[1]);
if(pid > 0){
while(wait(&status) != -1 && status.pid != pid)
;
if(status.pid != pid)
error("wait error", "");
}
return 0;
}