Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/* 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)             (ignored = (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 */
char    status[256];            /* 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};
int     ignored;

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);

void
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);
}

/* 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");
        return nil;
}

/* builtin - check np for builtin command and, if found, execute it */
int
builtin(Node *np)
{
        int n = 0;
        char name[MAXLINE];
        Waitmsg *wmsg;

        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);
        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((wmsg = wait()) != nil){
                        strncpy(status, wmsg->msg, sizeof(status)-1);
                        if(n && wmsg->pid==n){
                                n = 0;
                                free(wmsg);
                                break;
                        }
                        free(wmsg);
                }
                if(n)
                        error("wait error", "");
                return 1;
        }else if(strcmp(np->argv[0], "rfork") == 0){
                char *p;
                int mask;

                p = np->argv[1];
                if(p == 0 || *p == 0)
                        p = "ens";
                mask = 0;

                while(*p)
                        switch(*p++){
                        case 'n': mask |= RFNAMEG; break;
                        case 'N': mask |= RFCNAMEG; break;
                        case 'e': mask |= RFENVG; break;
                        case 'E': mask |= RFCENVG; break;
                        case 's': mask |= RFNOTEG; break;
                        case 'f': mask |= RFFDG; break;
                        case 'F': mask |= RFCFDG; break;
                        case 'm': mask |= RFNOMNT; break;
                        default: error(np->argv[1], "bad rfork flag");
                        }
                rfork(mask);

                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[256];

        fprint(2, "%s%s", t, s);
        errstr(buf, sizeof 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);
        }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(waitpid()!=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");
        return -1;              // suppress compiler warnings
}

/* 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);
        return -1;              // suppress compiler warnings
}

/* 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;
        Waitmsg *wmsg;

        execute(np->args[0]);
        pid = execute(np->args[1]);
        if(pid > 0){
                while((wmsg = wait()) != nil){
                        if(wmsg->pid == pid)
                                break;
                        free(wmsg);
                }
                if(wmsg == nil)
                        error("wait error", "");
                else {
                        strncpy(status, wmsg->msg, sizeof(status)-1);
                        free(wmsg);
                }
        }
        return 0;
}