summaryrefslogtreecommitdiff
path: root/sys/src/cmd/sh.C
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/sh.C
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/sh.C')
-rwxr-xr-xsys/src/cmd/sh.C509
1 files changed, 509 insertions, 0 deletions
diff --git a/sys/src/cmd/sh.C b/sys/src/cmd/sh.C
new file mode 100755
index 000000000..0a7eb6ba7
--- /dev/null
+++ b/sys/src/cmd/sh.C
@@ -0,0 +1,509 @@
+/* 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;
+}