diff options
| author | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
|---|---|---|
| committer | Charles.Forsyth <devnull@localhost> | 2006-12-22 21:39:35 +0000 |
| commit | 74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch) | |
| tree | c6e220ba61db3a6ea4052e6841296d829654e664 /utils/rcsh | |
| parent | 46439007cf417cbd9ac8049bb4122c890097a0fa (diff) | |
20060303
Diffstat (limited to 'utils/rcsh')
| -rw-r--r-- | utils/rcsh/Nt.c | 549 | ||||
| -rw-r--r-- | utils/rcsh/code.c | 486 | ||||
| -rw-r--r-- | utils/rcsh/exec.c | 802 | ||||
| -rw-r--r-- | utils/rcsh/glob.c | 286 | ||||
| -rw-r--r-- | utils/rcsh/here.c | 145 | ||||
| -rw-r--r-- | utils/rcsh/io.c | 238 | ||||
| -rw-r--r-- | utils/rcsh/lex.c | 398 | ||||
| -rw-r--r-- | utils/rcsh/main.c | 228 | ||||
| -rw-r--r-- | utils/rcsh/mkfile | 53 | ||||
| -rw-r--r-- | utils/rcsh/pcmd.c | 110 | ||||
| -rw-r--r-- | utils/rcsh/pfnc.c | 70 | ||||
| -rw-r--r-- | utils/rcsh/rc.h | 295 | ||||
| -rw-r--r-- | utils/rcsh/rcmain | 29 | ||||
| -rw-r--r-- | utils/rcsh/rcpath | 13 | ||||
| -rw-r--r-- | utils/rcsh/simple.c | 528 | ||||
| -rw-r--r-- | utils/rcsh/syn.y | 90 | ||||
| -rw-r--r-- | utils/rcsh/trap.c | 42 | ||||
| -rw-r--r-- | utils/rcsh/tree.c | 140 | ||||
| -rw-r--r-- | utils/rcsh/var.c | 240 | ||||
| -rw-r--r-- | utils/rcsh/word.c | 173 |
20 files changed, 4915 insertions, 0 deletions
diff --git a/utils/rcsh/Nt.c b/utils/rcsh/Nt.c new file mode 100644 index 00000000..dc6ad8a9 --- /dev/null +++ b/utils/rcsh/Nt.c @@ -0,0 +1,549 @@ +#include "rc.h" +#include <windows.h> + +enum { + Nchild = 100, +}; + +typedef struct Child Child; + +struct Child { + int pid; + HANDLE handle; +}; + +static Child child[Nchild]; + +static void +winerror(void) +{ + int e, r; + char buf[100], *p, *q; + + e = GetLastError(); + + r = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + buf, sizeof(buf), 0); + + if(r == 0) + snprint(buf, sizeof(buf), "windows error %d", e); + + for(p=q=buf; *p; p++) { + if(*p == '\r') + continue; + if(*p == '\n') + *q++ = ' '; + else + *q++ = *p; + } + *q = 0; + errstr(buf, sizeof buf); +} + +static int +badentry(char *filename) +{ + if(*filename == 0) + return 1; + if(filename[0] == '.'){ + if(filename[1] == 0) + return 1; + if(filename[1] == '.' && filename[2] == 0) + return 1; + } + return 0; +} + +Direntry* +readdirect(char *path) +{ + long n; + HANDLE h; + Direntry *d; + char fullpath[MAX_PATH]; + WIN32_FIND_DATA data; + + snprint(fullpath, MAX_PATH, "%s\\*.*", path); + + h = FindFirstFile(fullpath, &data); + if(h == INVALID_HANDLE_VALUE) + return 0; + + n = 0; + d = 0; + for(;;){ + if(!badentry(data.cFileName)){ + d = realloc(d, (n+2)*sizeof(Direntry)); + if(d == 0){ + werrstr("memory allocation"); + return 0; + } + d[n].name = malloc(strlen(data.cFileName)+1); + if(d[n].name == 0){ + werrstr("memory allocation"); + return 0; + } + strcpy(d[n].name, data.cFileName); + if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + d[n].isdir = 1; + else + d[n].isdir = 0; + n++; + } + if(FindNextFile(h, &data) == 0) + break; + } + FindClose(h); + if(d){ + d[n].name = 0; + d[n].isdir = 0; + } + return d; +} + +void +fatal(char *fmt, ...) +{ + char buf[512]; + va_list arg; + + va_start(arg, fmt); + vseprint(buf, buf+sizeof(buf), fmt, arg); + va_end(arg); + + fprint(2, "rc: %s\n", buf); + _exits(buf); +} + +static int +tas(int *p) +{ + int v; + + _asm { + mov eax, p + mov ebx, 1 + xchg ebx, [eax] + mov v, ebx + } + + return v; +} + +static void +lock(Lock *lk) +{ + int i; + + /* easy case */ + if(!tas(&lk->val)) + return; + + /* for muli processor machines */ + for(i=0; i<100; i++) + if(!tas(&lk->val)) + return; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); + for(;;) { + for(i=0; i<10000; i++) { + Sleep(0); + if(!tas(&lk->val)) { + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL); + return; + } + } + } +} + +static void +unlock(Lock *lk) +{ + lk->val = 0; +} + +int +refinc(Ref *r) +{ + int i; + + lock(&r->lk); + i = r->ref; + r->ref++; + unlock(&r->lk); + return i; +} + +int +refdec(Ref *r) +{ + int i; + + lock(&r->lk); + r->ref--; + i = r->ref; + unlock(&r->lk); + + return i; +} + +/* + * windows quoting rules - I think + * Words are seperated by space or tab + * Words containing a space or tab can be quoted using " + * 2N backslashes + " ==> N backslashes and end quote + * 2N+1 backslashes + " ==> N backslashes + literal " + * N backslashes not followed by " ==> N backslashes + */ +static char * +dblquote(char *cmd, char *s) +{ + int nb; + char *p; + + for(p=s; *p; p++) + if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"') + break; + + if(*p == 0){ /* easy case */ + strcpy(cmd, s); + return cmd+(p-s); + } + + *cmd++ = '"'; + for(;;) { + for(nb=0; *s=='\\'; nb++) + *cmd++ = *s++; + + if(*s == 0) { /* trailing backslashes -> 2N */ + while(nb-- > 0) + *cmd++ = '\\'; + break; + } + + if(*s == '"') { /* literal quote -> 2N+1 backslashes */ + while(nb-- > 0) + *cmd++ = '\\'; + *cmd++ = '\\'; /* escape the quote */ + } + *cmd++ = *s++; + } + + *cmd++ = '"'; + *cmd = 0; + + return cmd; +} + +static char * +proccmd(char **argv) +{ + int i, n; + char *cmd, *p; + + /* conservatively calculate length of command; + * backslash expansion can cause growth in dblquote(). + */ + for(i=0,n=0; argv[i]; i++) { + n += 2*strlen(argv[i]); + } + n++; + + cmd = malloc(n); + for(i=0,p=cmd; argv[i]; i++) { + p = dblquote(p, argv[i]); + *p++ = ' '; + } + if(p != cmd) + p--; + *p = 0; + + return cmd; +} + +static char * +exportenv(char **e) +{ + int i, j, n; + char *buf; + + if(e == 0 || *e == 0) + return 0; + + buf = 0; + n = 0; + for(i = 0; *e; e++, i++) { + j = strlen(*e)+1; + buf = realloc(buf, n+j); + strcpy(buf+n, *e); + n += j; + } + /* final null */ + buf = realloc(buf, n+1); + buf[n] = 0; + + return buf; +} + +static int +setpath(char *path, char *file) +{ + char *p, *last, tmp[MAX_PATH+1]; + int n; + + if(strlen(file) >= MAX_PATH){ + werrstr("file name too long"); + return -1; + } + strcpy(tmp, file); + + for(p=tmp; *p; p++) { + if(*p == '/') + *p = '\\'; + } + + if(tmp[0] != 0 && tmp[1] == ':') { + if(tmp[2] == 0) { + tmp[2] = '\\'; + tmp[3] = 0; + } else if(tmp[2] != '\\') { + /* don't allow c:foo - only c:\foo */ + werrstr("illegal file name"); + return -1; + } + } + + path[0] = 0; + n = GetFullPathName(tmp, MAX_PATH, path, &last); + if(n >= MAX_PATH) { + werrstr("file name too long"); + return -1; + } + if(n == 0 && tmp[0] == '\\' && tmp[1] == '\\' && tmp[2] != 0) { + strcpy(path, tmp); + return -1; + } + + if(n == 0) { + werrstr("bad file name"); + return -1; + } + + for(p=path; *p; p++) { + if(*p < 32 || *p == '*' || *p == '?') { + werrstr("file not found"); + return -1; + } + } + + /* get rid of trailling \ */ + if(path[n-1] == '\\') { + if(n <= 2) { + werrstr("illegal file name"); + return -1; + } + path[n-1] = 0; + n--; + } + + if(path[1] == ':' && path[2] == 0) { + path[2] = '\\'; + path[3] = '.'; + path[4] = 0; + return -1; + } + + if(path[0] != '\\' || path[1] != '\\') + return 0; + + for(p=path+2,n=0; *p; p++) + if(*p == '\\') + n++; + if(n == 0) + return -1; + if(n == 1) + return -1; + return 0; +} + + +static int +execpath(char *path, char *file) +{ + int n; + + if(setpath(path, file) < 0) + return 0; + + n = strlen(path)-4; + if(path[n] == '.') { + if(GetFileAttributes(path) != -1) + return 1; + } + strncat(path, ".exe", MAX_PATH); + path[MAX_PATH-1] = 0; + if(GetFileAttributes(path) != -1) + return 1; + return 0; +} + +static HANDLE +fdexport(int fd) +{ + HANDLE h, r; + + if(fd < 0) + return INVALID_HANDLE_VALUE; + + h = (HANDLE)_get_osfhandle(fd); + if(h < 0) + return INVALID_HANDLE_VALUE; + + if(!DuplicateHandle(GetCurrentProcess(), h, + GetCurrentProcess(), &r, DUPLICATE_SAME_ACCESS, + 1, DUPLICATE_SAME_ACCESS)) + return INVALID_HANDLE_VALUE; + return r; +} + +static int +addchild(int pid, HANDLE handle) +{ + int i; + + for(i=0; i<Nchild; i++) { + if(child[i].handle == 0) { + child[i].handle = handle; + child[i].pid = pid; + return 1; + } + } + werrstr("child table full"); + return 0; +} + +int +procwait(uint pid) +{ + HANDLE h; + int i, exit; + + if(pid == 0) + return 0; + + h = 0; + for(i = 0; i < Nchild; i++){ + if(child[i].pid == pid){ + h = child[i].handle; + child[i].pid = 0; + child[i].handle = 0; + break; + } + } + + if(h == 0){ /* we don't know about this one - let the system try to find it */ + h = OpenProcess(PROCESS_ALL_ACCESS, 0, pid); + if(h == 0) + return 0; /* can't find it */ + } + + if(WaitForSingleObject(h, INFINITE) == WAIT_FAILED) { + winerror(); + fatal("procwait: "); + } + + if(!GetExitCodeProcess(h, &exit)) { + winerror(); + exit = 1; + } + + CloseHandle(h); + return exit; +} + +uint +proc(char **argv, int stdin, int stdout, int stderr) +{ + char *p, *arg0, *q, buf[MAX_PATH], path[MAX_PATH], *cmd, *eb; + STARTUPINFO si; + PROCESS_INFORMATION pi; + int r, found, full; + extern char **_environ; + Word *w; + + arg0 = argv[0]; + if(arg0 == 0) { + werrstr("null argv[0]"); + return 0; + } + + full = arg0[0] == '\\' || arg0[0] == '/' || arg0[0] == '.'; + found = execpath(path, arg0); + + if(!found && !full) { + w = vlook("path")->val; + if(w) + p = w->word; + else + p = getenv("path"); + for(; p && *p; p = q){ + q = strchr(p, ';'); + if(q) + *q = 0; + snprint(buf, sizeof(buf), "%s/%s", p, arg0); + if(q) + *q++ = ';'; + found = execpath(path, buf); + if(found) + break; + } + } + + if(!found) { + werrstr("file not found"); + return 0; + } + + memset(&si, 0, sizeof(si)); + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES; + si.wShowWindow = SW_SHOW; + si.hStdInput = fdexport(stdin); + si.hStdOutput = fdexport(stdout); + si.hStdError = fdexport(stderr); + + eb = exportenv(_environ); + + cmd = proccmd(argv); + + r = CreateProcess(path, cmd, 0, 0, 1, 0, eb, 0, &si, &pi); + + /* allow child to run */ + Sleep(0); + + free(cmd); + free(eb); + + CloseHandle(si.hStdInput); + CloseHandle(si.hStdOutput); + CloseHandle(si.hStdError); + + if(!r) { + winerror(); + return 0; + } + + CloseHandle(pi.hThread); + + if(addchild(pi.dwProcessId, pi.hProcess) == 0) + return 0; + + return pi.dwProcessId; +} + +int +pipe(int *fd) +{ + return _pipe(fd, 0, _O_BINARY); +} diff --git a/utils/rcsh/code.c b/utils/rcsh/code.c new file mode 100644 index 00000000..06aea6ff --- /dev/null +++ b/utils/rcsh/code.c @@ -0,0 +1,486 @@ +#include "rc.h" +#include "y.tab.h" + +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] +#define emitf(x) ((codep!=ncode || morecode()), codebuf[codep].f=(x), codep++) +#define emiti(x) ((codep!=ncode || morecode()), codebuf[codep].i=(x), codep++) +#define emits(x) ((codep!=ncode || morecode()), codebuf[codep].s=(x), codep++) + +void stuffdot(int); +char *fnstr(Tree*); +void outcode(Tree*, int); +void codeswitch(Tree*, int); +int iscase(Tree*); +Code *codecopy(Code*); +void codefree(Code*); + +int codep, ncode; +Code *codebuf; + +int +morecode(void) +{ + ncode+=100; + codebuf=realloc(codebuf, ncode*sizeof codebuf[0]); + if(codebuf==0) + panic("Can't realloc %d bytes in morecode!", + ncode*sizeof codebuf[0]); + return 0; /* not used */ +} + +void +stuffdot(int a) +{ + if(a<0 || codep<=a) panic("Bad address %d in stuffdot", a); + codebuf[a].i=codep; +} + +int +compile(Tree *t) +{ + ncode=100; + codebuf=malloc(ncode*sizeof codebuf[0]); + codep=0; + emiti(0); /* reference count */ + outcode(t, flag['e']?1:0); + if(nerror){ + free(codebuf); + return 0; + } +/* readhere(); */ + emitf(Xreturn); + emitf(0); + return 1; +} + +void +cleanhere(char *f) +{ + emitf(Xdelhere); + emits(strdup(f)); +} + +char * +fnstr(Tree *t) +{ + Io *f=openstr(); + char *v; + extern char nl; + char svnl=nl; + + nl=';'; + pfmt(f, "%t", t); + nl=svnl; + v=f->strp; + f->strp=0; + closeio(f); + return v; +} + +void +outcode(Tree *t, int eflag) +{ + int p, q; + Tree *tt; + + if(t==0) + return; + if(t->type != NOT && t->type != ';') + runq->iflast=0; + switch(t->type){ + default: + pfmt(err, "bad type %d in outcode\n", t->type); + break; + case '$': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xdol); + break; + case '"': + emitf(Xmark); + outcode(c0, eflag); + emitf(Xqdol); + break; + case SUB: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmark); + outcode(c1, eflag); + emitf(Xsub); + break; + case '&': + emitf(Xasync); + emits(fnstr(c0)); +/* + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); +*/ + break; + case ';': + outcode(c0, eflag); + outcode(c1, eflag); + break; + case '^': + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xconc); + break; + case '`': + emitf(Xbackq); + emits(fnstr(c0)); +/* + p=emiti(0); + outcode(c0, 0); + emitf(Xexit); + stuffdot(p); +*/ + break; + case ANDAND: + outcode(c0, 0); + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case ARGLIST: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case BANG: + outcode(c0, eflag); + emitf(Xbang); + break; + case PCMD: + case BRACE: + outcode(c0, eflag); + break; + case COUNT: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xcount); + break; + case FN: + emitf(Xmark); + outcode(c0, eflag); + if(c1){ + emitf(Xfn); + p=emiti(0); + emits(fnstr(c1)); + outcode(c1, eflag); + emitf(Xunlocal); /* get rid of $* */ + emitf(Xreturn); + stuffdot(p); + } + else + emitf(Xdelfn); + break; + case IF: + outcode(c0, 0); + emitf(Xif); + p=emiti(0); + outcode(c1, eflag); + emitf(Xwastrue); + stuffdot(p); + break; + case NOT: + if(!runq->iflast) yyerror("`if not' does not follow `if(...)'"); + emitf(Xifnot); + p=emiti(0); + outcode(c0, eflag); + stuffdot(p); + break; + case OROR: + outcode(c0, 0); + emitf(Xfalse); + p=emiti(0); + outcode(c1, eflag); + stuffdot(p); + break; + case PAREN: + outcode(c0, eflag); + break; + case SIMPLE: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xsimple); + if(eflag) emitf(Xeflag); + break; + case SUBSHELL: + emitf(Xsubshell); + emits(fnstr(c0)); +/* + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); +*/ + if(eflag) emitf(Xeflag); + break; + case SWITCH: + codeswitch(t, eflag); + break; + case TWIDDLE: + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xmatch); + if(eflag) emitf(Xeflag); + break; + case WHILE: + q=codep; + emitf(Xsettrue); + outcode(c0, 0); + emitf(Xtrue); + p=emiti(0); + outcode(c1, eflag); + emitf(Xjump); + emiti(q); + stuffdot(p); + break; + case WORDS: + outcode(c1, eflag); + outcode(c0, eflag); + break; + case FOR: + emitf(Xmark); + if(c1){ + outcode(c1, eflag); + emitf(Xglob); + } + else{ + emitf(Xmark); + emitf(Xword); + emits(strdup("*")); + emitf(Xdol); + } + emitf(Xmark); /* dummy value for Xlocal */ + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + p=emitf(Xfor); + q=emiti(0); + outcode(c2, eflag); + emitf(Xjump); + emiti(p); + stuffdot(q); + emitf(Xunlocal); + break; + case WORD: + emitf(Xword); + emits(strdup(t->str)); + break; + case DUP: + if(t->rtype==DUPFD) { + emitf(Xdup); + emiti(t->fd0); + emiti(t->fd1); + } else { /* t->rtype == CLOSE */ + emitf(Xclose); + emiti(t->fd0); + } + outcode(c1, eflag); + emitf(Xpopredir); + break; +/* + case PIPEFD: + emitf(Xpipefd); + emiti(t->rtype); + p=emiti(0); + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); + break; +*/ + case REDIR: + emitf(Xmark); + outcode(c0, eflag); + emitf(Xglob); + switch(t->rtype){ + case APPEND: + emitf(Xappend); + break; + case WRITE: + emitf(Xwrite); + break; + case READ: + case HERE: + emitf(Xread); + break; + } + emiti(t->fd0); + outcode(c1, eflag); + emitf(Xpopredir); + break; + case '=': + tt=t; + for(;t && t->type=='=';t=c2) + ; + if(t){ + for(t=tt;t->type=='=';t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xlocal); + } + t=tt; + outcode(c2, eflag); + for(;t->type=='=';t=c2) + emitf(Xunlocal); + } + else{ + for(t=tt;t;t=c2){ + emitf(Xmark); + outcode(c1, eflag); + emitf(Xmark); + outcode(c0, eflag); + emitf(Xassign); + } + } + t=tt; /* so tests below will work */ + break; + case PIPE: + emitf(Xpipe); + emiti(t->fd0); + emiti(t->fd1); + emits(fnstr(c0)); + q=emiti(0); +/* + outcode(c0, eflag); + emitf(Xexit); + stuffdot(p); +*/ + outcode(c1, eflag); + emitf(Xreturn); + stuffdot(q); + emitf(Xpipewait); + break; + } + if(t->type!=NOT && t->type!=';') + runq->iflast=t->type==IF; + else if(c0) runq->iflast=c0->type==IF; +} + +/* + * switch code looks like this: + * Xmark + * (get switch value) + * Xjump 1f + * out: Xjump leave + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: Xmark + * (get case values) + * Xcase 1f + * (commands) + * Xjump out + * 1: + * leave: + * Xpopm + */ +void +codeswitch(Tree *t, int eflag) +{ + int leave; /* patch jump address to leave switch */ + int out; /* jump here to leave switch */ + int nextcase; /* patch jump address to next case */ + Tree *tt; + if(c1->child[0]->type!=';' + || !iscase(c1->child[0]->child[0])){ + yyerror("case missing in switch"); + return; + } + emitf(Xmark); + outcode(c0, eflag); + emitf(Xjump); + nextcase=emiti(0); + out=emitf(Xjump); + leave=emiti(0); + stuffdot(nextcase); + t=c1->child[0]; + while(t->type==';'){ + tt=c1; + emitf(Xmark); + for(t=c0->child[0];t->type==ARGLIST;t=c0) outcode(c1, eflag); + emitf(Xcase); + nextcase=emiti(0); + t=tt; + for(;;){ + if(t->type==';'){ + if(iscase(c0)) break; + outcode(c0, eflag); + t=c1; + } + else{ + outcode(t, eflag); + break; + } + } + emitf(Xjump); + emiti(out); + stuffdot(nextcase); + } + stuffdot(leave); + emitf(Xpopm); +} + +int +iscase(Tree *t) +{ + if(t->type!=SIMPLE) + return 0; + do + t=c0; + while(t->type==ARGLIST); + + return t->type==WORD && !t->quoted && strcmp(t->str, "case")==0; +} + +Code * +codecopy(Code *cp) +{ + cp[0].i++; + return cp; +} + +void +codefree(Code *cp) +{ + Code *p; + + if(--cp[0].i!=0) + return; + + for(p=cp+1;p->f;){ + if(p->f==Xappend || p->f==Xclose || p->f==Xread || p->f==Xwrite + || p->f==Xasync || p->f==Xcase || p->f==Xfalse + || p->f==Xfor || p->f==Xjump + || p->f==Xsubshell || p->f==Xtrue) + p+=2; + else if(p->f==Xdup || p->f==Xpipefd) + p+=3; + else if(p->f==Xpipe) { + free(p[3].s); + p+=5; + } else if(p->f==Xword || p->f==Xdelhere || p->f==Xbackq) { + free(p[1].s); + p+=2; + } else if(p->f==Xfn){ + free(p[2].s); + p+=3; + } else + p++; + } + + free(cp); +} diff --git a/utils/rcsh/exec.c b/utils/rcsh/exec.c new file mode 100644 index 00000000..66112d80 --- /dev/null +++ b/utils/rcsh/exec.c @@ -0,0 +1,802 @@ +#include "rc.h" + +extern char *argv0; + +int ifnot; +int eflagok; + +/* + * Opcode routines + * Arguments on stack (...) + * Arguments in line [...] + * Code in line with jump around {...} + * + * Xappend(file)[fd] open file to append + * Xassign(name, val) assign val to name + * Xasync{... Xexit} make thread for {}, no wait + * Xbackq{... Xreturn} make thread for {}, push stdout + * Xbang complement condition + * Xcase(pat, value){...} exec code on match, leave (value) on + * stack + * Xclose[i] close file descriptor + * Xconc(left, right) concatenate, push results + * Xcount(name) push var count + * Xdelfn(name) delete function definition + * Xdeltraps(names) delete named traps + * Xdol(name) get variable value + * Xqdol(name) concatenate variable components + * Xdup[i j] dup file descriptor + * Xexit rc exits with status + * Xfalse{...} execute {} if false + * Xfn(name){... Xreturn} define function + * Xfor(var, list){... Xreturn} for loop + * Xjump[addr] goto + * Xlocal(name, val) create local variable, assign value + * Xmark mark stack + * Xmatch(pat, str) match pattern, set status + * Xpipe[i j]{... Xreturn}{... Xreturn} construct a pipe between 2 new threads, + * wait for both + * Xpipefd[type]{... Xreturn} connect {} to pipe (input or output, + * depending on type), push /dev/fd/?? + * Xpopm(value) pop value from stack + * Xread(file)[fd] open file to read + * Xsettraps(names){... Xreturn} define trap functions + * Xshowtraps print trap list + * Xsimple(args) run command and wait + * Xreturn kill thread + * Xsubshell{... Xexit} execute {} in a subshell and wait + * Xtrue{...} execute {} if true + * Xunlocal delete local variable + * Xword[string] push string + * Xwrite(file)[fd] open file to write + */ + +static char ** +rcargv(char *s) +{ + char *flags; + + if(flag['e']) + flags = "-Se"; + else + flags = "-S"; + return procargv(argv0, flags, "-c", s, vlook("*")->val); +} + +void +Xappend(void) +{ + char *file; + int f; + + switch(count(runq->argv->words)){ + default: Xerror(">> requires singleton"); return; + case 0: Xerror(">> requires file"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=open(file, 1))<0 && (f=create(file, 1, 0666))<0){ + Xperror(file); + return; + } + seek(f, 0L, 2); + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} + +void +Xassign(void) +{ + Var *v; + + if(count(runq->argv->words)!=1){ + Xerror("variable name not singleton!"); + return; + } + deglob(runq->argv->words->word); + v=vlook(runq->argv->words->word); + poplist(); + globlist(); + freewords(v->val); + v->val=runq->argv->words; + v->changed=1; + runq->argv->words=0; + poplist(); +} + +void +Xasync(void) +{ + uint pid; + char buf[20], **argv; + + updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = proc(argv, -1, 1, 2); + free(argv); + + if(pid == 0) { + Xerror("proc failed"); + return; + } + + runq->pc++; + sprint(buf, "%d", pid); + setvar("apid", newword(buf, (Word *)0)); +} + +void +Xbackq(void) +{ + char wd[8193], **argv; + int c; + char *s, *ewd=&wd[8192], *stop; + Io *f; + Var *ifs=vlook("ifs"); + Word *v, *nextv; + int pfd[2]; + int pid; + + stop = ifs->val?ifs->val->word:""; + if(pipe(pfd)<0){ + Xerror("can't make pipe"); + return; + } + + updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = proc(argv, -1, pfd[1], 2); + free(argv); + + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + f = openfd(pfd[0]); + s = wd; + v = 0; + while((c=rchr(f))!=EOF){ + if(strchr(stop, c) || s==ewd){ + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + s=wd; + } + } + else *s++=c; + } + if(s!=wd){ + *s='\0'; + v=newword(wd, v); + } + closeio(f); + waitfor(pid); + /* v points to reversed arglist -- reverse it onto argv */ + while(v){ + nextv=v->next; + v->next=runq->argv->words; + runq->argv->words=v; + v=nextv; + } + runq->pc++; +} + +void +Xbang(void) +{ + setstatus(truestatus()?"false":""); +} + +void +Xcase(void) +{ + Word *p; + char *s; + int ok=0; + + s=list2str(runq->argv->next->words); + for(p=runq->argv->words;p;p=p->next){ + if(match(s, p->word, '\0')){ + ok=1; + break; + } + } + free(s); + if(ok) + runq->pc++; + else + runq->pc=runq->code[runq->pc].i; + poplist(); +} + +void +Xclose(void) +{ + pushredir(RCLOSE, runq->code[runq->pc].i, 0); + runq->pc++; +} + +void +Xconc(void) +{ + Word *lp=runq->argv->words; + Word *rp=runq->argv->next->words; + Word *vp=runq->argv->next->next->words; + int lc=count(lp), rc=count(rp); + + if(lc!=0 || rc!=0){ + if(lc==0 || rc==0){ + Xerror("null list in concatenation"); + return; + } + if(lc!=1 && rc!=1 && lc!=rc){ + Xerror("mismatched list lengths in concatenation"); + return; + } + vp=conclist(lp, rp, vp); + } + poplist(); + poplist(); + runq->argv->words=vp; +} + +void +Xcount(void) +{ + Word *a; + char *s, *t; + int n; + char num[12]; + + if(count(runq->argv->words)!=1){ + Xerror("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + n=0; + for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; + if(n==0 || *t){ + a=vlook(s)->val; + sprint(num, "%d", count(a)); + } + else{ + a=vlook("*")->val; + sprint(num, "%d", a && 1<=n && n<=count(a)?1:0); + } + poplist(); + pushword(num); +} + +void +Xdelfn(void) +{ + Var *v; + Word *a; + + for(a=runq->argv->words;a;a=a->next){ + v=gvlook(a->word); + if(v->fn) + codefree(v->fn); + v->fn=0; + v->fnchanged=1; + } + poplist(); +} + +void +Xdelhere(void) +{ + Var *v; + Word *a; + + for(a=runq->argv->words;a;a=a->next){ + v=gvlook(a->word); + if(v->fn) codefree(v->fn); + v->fn=0; + v->fnchanged=1; + } + poplist(); +} + +void +Xdol(void) +{ + Word *a, *star; + char *s, *t; + int n; + + if(count(runq->argv->words)!=1){ + Xerror("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + n=0; + for(t=s;'0'<=*t && *t<='9';t++) n=n*10+*t-'0'; + a=runq->argv->next->words; + if(n==0 || *t) + a=copywords(vlook(s)->val, a); + else{ + star=vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star=star->next; + a=newword(star->word, a); + } + } + poplist(); + runq->argv->words=a; +} + +void +Xdup(void) +{ + pushredir(RDUP, runq->code[runq->pc].i, runq->code[runq->pc+1].i); + runq->pc+=2; +} + +void +Xeflag(void) +{ + if(eflagok && !truestatus()) + Xexit(); +} + +void +Xexit(void) +{ + Var *trapreq; + Word *starval; + char *c; + static int beenhere=0; + + if(truestatus()) + c = ""; + else + c = getstatus(); + + if(flag['S'] || beenhere) + exits(c); + + trapreq=vlook("sigexit"); + if(trapreq->fn){ + beenhere=1; + --runq->pc; + starval=vlook("*")->val; + start(trapreq->fn, trapreq->pc, (Var*)0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=copywords(starval, (Word*)0); + runq->local->changed=1; + runq->redir=runq->startredir=0; + } + + exits(c); +} + +void +Xfalse(void) +{ + if(truestatus()) + runq->pc=runq->code[runq->pc].i; + else + runq->pc++; +} + +void +Xfor(void) +{ + if(runq->argv->words==0) { + poplist(); + runq->pc=runq->code[runq->pc].i; + } else { + freelist(runq->local->val); + runq->local->val=runq->argv->words; + runq->local->changed=1; + runq->argv->words=runq->argv->words->next; + runq->local->val->next=0; + runq->pc++; + } +} + +void +Xfn(void) +{ + Var *v; + Word *a; + int end; + + end=runq->code[runq->pc].i; + for(a=runq->argv->words;a;a=a->next){ + v=gvlook(a->word); + if(v->fn) + codefree(v->fn); + v->fn=codecopy(runq->code); + v->pc=runq->pc+2; + v->fnchanged=1; + } + runq->pc=end; + poplist(); +} + +void +Xglob(void) +{ + globlist(); +} + +void +Xif(void) +{ + ifnot=1; + if(truestatus()) runq->pc++; + else runq->pc=runq->code[runq->pc].i; +} + +void +Xifnot(void) +{ + if(ifnot) + runq->pc++; + else + runq->pc=runq->code[runq->pc].i; +} + +void +Xjump(void) +{ + runq->pc=runq->code[runq->pc].i; +} + + +void +Xlocal(void) +{ + if(count(runq->argv->words)!=1){ + Xerror("variable name must be singleton\n"); + return; + } + deglob(runq->argv->words->word); + runq->local=newvar(strdup(runq->argv->words->word), runq->local); + runq->local->val=copywords(runq->argv->next->words, 0); + runq->local->changed=1; + poplist(); + poplist(); +} + + +void +Xmark(void) +{ + pushlist(); +} + +void +Xmatch(void) +{ + Word *p; + char *subject; + + subject=list2str(runq->argv->words); + setstatus("no match"); + for(p=runq->argv->next->words;p;p=p->next) { + if(match(subject, p->word, '\0')){ + setstatus(""); + break; + } + } + free(subject); + poplist(); + poplist(); +} + +void +Xpipe(void) +{ + Thread *p=runq; + int pc=p->pc, pid; + int lfd=p->code[pc].i; + int rfd=p->code[pc+1].i; + int pfd[2]; + char **argv; + + if(pipe(pfd)<0){ + Xperror("can't get pipe"); + return; + } + + updenv(); + + argv = rcargv(runq->code[pc+2].s); + pid = proc(argv, 0, pfd[1], 2); + free(argv); + close(pfd[1]); + + if(pid == 0) { + Xerror("proc failed"); + close(pfd[0]); + return; + } + + start(p->code, pc+4, runq->local); + pushredir(ROPEN, pfd[0], rfd); + p->pc=p->code[pc+3].i; + p->pid=pid; +} + +void +Xpipefd(void) +{ + fatal("Xpipefd"); +} + +void +Xpipewait(void) +{ + char status[NSTATUS+1]; + if(runq->pid==-1) + setstatus(concstatus(runq->status, getstatus())); + else{ + strncpy(status, getstatus(), NSTATUS); + status[NSTATUS]='\0'; + waitfor(runq->pid); + runq->pid=-1; + setstatus(concstatus(getstatus(), status)); + } +} + +void +Xpopm(void) +{ + poplist(); +} + +void +Xpopredir(void) +{ + Redir *rp=runq->redir; + + if(rp==0) + panic("turfredir null!", 0); + runq->redir=rp->next; + if(rp->type==ROPEN) + close(rp->from); + free((char *)rp); +} + +void +Xqdol(void) +{ + Word *a, *p; + char *s; + int n; + + if(count(runq->argv->words)!=1){ + Xerror("variable name not singleton!"); + return; + } + s=runq->argv->words->word; + deglob(s); + a=vlook(s)->val; + poplist(); + n=count(a); + if(n==0){ + pushword(""); + return; + } + for(p=a;p;p=p->next) n+=strlen(p->word); + s=malloc(n); + if(a){ + strcpy(s, a->word); + for(p=a->next;p;p=p->next){ + strcat(s, " "); + strcat(s, p->word); + } + } + else + s[0]='\0'; + pushword(s); + free(s); +} + +void +Xrdcmds(void) +{ + Thread *p=runq; + Word *prompt; + + flush(err); + nerror=0; + if(flag['s'] && !truestatus()) + pfmt(err, "status=%v\n", vlook("status")->val); + if(runq->iflag){ + prompt=vlook("prompt")->val; + if(prompt) + promptstr=prompt->word; + else + promptstr="% "; + } + interrupted=0; + if(yyparse()) { + if(!p->iflag || p->eof /* && !Eintr() */) { + if(p->cmdfile) + free(p->cmdfile); + closeio(p->cmdfd); + Xreturn(); /* should this be omitted? */ + } else { + if(interrupted){ + pchr(err, '\n'); + p->eof=0; + } + --p->pc; /* go back for next command */ + } + } else { + --p->pc; /* re-execute Xrdcmds after codebuf runs */ + start(codebuf, 1, runq->local); + } + freenodes(); +} + +void +Xread(void) +{ + char *file; + int f; + + switch(count(runq->argv->words)){ + default: Xerror("< requires singleton\n"); return; + case 0: Xerror("< requires file\n"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f=open(file, 0))<0){ + Xperror(file); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} + +void +Xreturn(void) +{ + Thread *p=runq; + + turfredir(); + while(p->argv) + poplist(); + codefree(p->code); + runq=p->ret; + free(p); + if(runq==0) + exits(truestatus()?"":getstatus()); +} + +void +Xsettrue(void) +{ + setstatus(""); +} + + +void +Xsub(void) +{ + Word *a, *v; + char *s; + if(count(runq->argv->next->words)!=1){ + Xerror("variable name not singleton!"); + return; + } + s=runq->argv->next->words->word; + deglob(s); + a=runq->argv->next->next->words; + v=vlook(s)->val; + a=subwords(v, count(v), runq->argv->words, a); + poplist(); + poplist(); + runq->argv->words=a; +} + +void +Xsubshell(void) +{ + char **argv; + uint pid; + + updenv(); + + argv = rcargv(runq->code[runq->pc].s); + pid = proc(argv, -1, 1, 2); + free(argv); + + if(pid == 0) { + Xerror("proc failed"); + return; + } + + waitfor(pid); + runq->pc++; +} + +void +Xtrue(void) +{ + if(truestatus()) + runq->pc++; + else + runq->pc=runq->code[runq->pc].i; +} + +void +Xunlocal(void) +{ + Var *v=runq->local, *hid; + + if(v==0) + panic("Xunlocal: no locals!", 0); + runq->local=v->next; + hid=vlook(v->name); + hid->changed=1; + free(v->name); + freewords(v->val); + free(v); +} + +void +Xwastrue(void) +{ + ifnot=0; +} + +void +Xwrite(void) +{ + char *file; + int f; + + switch(count(runq->argv->words)){ + default: Xerror("> requires singleton\n"); return; + case 0: Xerror("> requires file\n"); return; + case 1: break; + } + file=runq->argv->words->word; + if((f = create(file, 1, 0666))<0){ + Xperror(file); + return; + } + pushredir(ROPEN, f, runq->code[runq->pc].i); + runq->pc++; + poplist(); +} + +void +Xword(void) +{ + pushword(runq->code[runq->pc++].s); +} + +void +Xerror(char *s) +{ + pfmt(err, "rcsh: %s\n", s); + flush(err); + while(!runq->iflag) + Xreturn(); +} + +void +Xperror(char *s) +{ + pfmt(err, "rcsh: %s: %r\n", s); + flush(err); + while(!runq->iflag) + Xreturn(); +} diff --git a/utils/rcsh/glob.c b/utils/rcsh/glob.c new file mode 100644 index 00000000..7640eeb1 --- /dev/null +++ b/utils/rcsh/glob.c @@ -0,0 +1,286 @@ +#include "rc.h" + +char *globname; +Word *globv; + +int matchfn(char *s, char *p); +int globsize(char *p); + +/* + * delete all the GLOB marks from s, in place + */ +void +deglob(char *s) +{ + char *t=s; + do{ + if(*t==GLOB) t++; + *s++=*t; + }while(*t++); +} + +int +globcmp(const void *s, const void *t) +{ + return strcmp(*(char**)s, *(char**)t); +} + +void +globsort(Word *left, Word *right) +{ + char **list; + Word *a; + int n=0; + for(a=left;a!=right;a=a->next) n++; + list=(char **)malloc(n*sizeof(char *)); + for(a=left,n=0;a!=right;a=a->next,n++) list[n]=a->word; + qsort((void*)list, (size_t)n, sizeof(char*), globcmp); + for(a=left,n=0;a!=right;a=a->next,n++) a->word=list[n]; + free(list); +} + +/* + * Push names prefixed by globname and suffixed by a match of p onto the astack. + * namep points to the end of the prefix in globname. + */ +void +globdir(char *p, char *namep) +{ + char *t, *q, *newp; + Direntry *dp, *dq; + Dir *dir; + + /* scan the pattern looking for a component with a metacharacter in it */ + if(*p=='\0'){ + globv=newword(globname, globv); + return; + } + t=namep; + newp=p; + while(*newp){ + if(*newp==GLOB) + break; + *t=*newp++; + if(*t++=='/'){ + namep=t; + p=newp; + } + } + /* If we ran out of pattern, append the name if accessible */ + if(*newp=='\0'){ + *t='\0'; + if(access(globname, 0)==0) + globv=newword(globname, globv); + return; + } + /* read the directory and recur for any entry that matches */ + *namep='\0'; + t = globname; + if(*t == 0) + t = "."; + q = strdup(t); + if (q[strlen(q)-1] == '/') + q[strlen(q)-1] = 0; + if((dir=dirstat(q)) == nil || !(dir->mode&0x80000000)){ + free(dir); + return; + } + free(dir); + dq = readdirect(q); + if(dq == 0){ + fprint(2, "could not open %s: %r\n", q); + return; + } + while(*newp!='/' && *newp!='\0') + newp++; + for(dp = dq;dp->name; dp++){ + strcpy(namep, dp->name); + if(matchfn(namep, p)) + globdir(newp, namep+strlen(namep)); + free(dp->name); + } + free(dq); +} + +/* + * Push all file names matched by p on the current thread's stack. + * If there are no matches, the list consists of p. + */ +void +glob(char *p) +{ + Word *svglobv=globv; + int globlen=globsize(p); + + if(globlen == 0){ + deglob(p); + globv=newword(p, globv); + return; + } + globname=malloc(globlen); + globname[0]='\0'; + globdir(p, globname); + free(globname); + if(svglobv==globv){ + deglob(p); + globv=newword(p, globv); + } + else + globsort(globv, svglobv); +} + + +/* + * Do p and q point at equal utf codes + */ +int +equtf(char *p, char *q) +{ + if(*p!=*q) + return 0; + if(twobyte(*p)) return p[1]==q[1]; + if(threebyte(*p)){ + if(p[1]!=q[1]) return 0; + if(p[1]=='\0') return 1; /* broken code at end of string! */ + return p[2]==q[2]; + } + return 1; +} + +/* + * Return a pointer to the next utf code in the string, + * not jumping past nuls in broken utf codes! + */ +char * +nextutf(char *p) +{ + if(twobyte(*p)) + return p[1]=='\0'?p+1:p+2; + if(threebyte(*p)) + return p[1]=='\0'?p+1:p[2]=='\0'?p+2:p+3; + return p+1; +} + +/* + * Convert the utf code at *p to a unicode value + */ +int +unicode(char *p) +{ + int u=*p&0xff; + if(twobyte(u)) + return ((u&0x1f)<<6)|(p[1]&0x3f); + if(threebyte(u)) + return (u<<12)|((p[1]&0x3f)<<6)|(p[2]&0x3f); + return u; +} + +/* + * Does the string s match the pattern p + * . and .. are only matched by patterns starting with . + * * matches any sequence of characters + * ? matches any single character + * [...] matches the enclosed list of characters + */ +int +matchfn(char *s, char *p) +{ + if(s[0]=='.' && (s[1]=='\0' || s[1]=='.' && s[2]=='\0') && p[0]!='.') + return 0; + return match(s, p, '/'); +} + +int +match(char *s, char *p, int stop) +{ + int compl, hit, lo, hi, t, c; + + for(;*p!=stop && *p!='\0';s=nextutf(s),p=nextutf(p)) { + if(*p!=GLOB){ + if(!equtf(p, s)) return 0; + } + else switch(*++p){ + case GLOB: + if(*s!=GLOB) return 0; + break; + case '*': + for(;;){ + if(match(s, nextutf(p), stop)) return 1; + if(!*s) break; + s=nextutf(s); + } + return 0; + case '?': + if(*s=='\0') return 0; + break; + case '[': + if(*s=='\0') return 0; + c=unicode(s); + p++; + compl=*p=='~'; + if(compl) p++; + hit=0; + while(*p!=']'){ + if(*p=='\0') return 0; /* syntax error */ + lo=unicode(p); + p=nextutf(p); + if(*p!='-') hi=lo; + else{ + p++; + if(*p=='\0') return 0; /* syntax error */ + hi=unicode(p); + p=nextutf(p); + if(hi<lo){ t=lo; lo=hi; hi=t; } + } + if(lo<=c && c<=hi) hit=1; + } + if(compl) hit=!hit; + if(!hit) return 0; + break; + } + } + return *s=='\0'; +} + +void +globlist1(Word *gl) +{ + if(gl){ + globlist1(gl->next); + glob(gl->word); + } +} + +void +globlist(void) +{ + Word *a; + + globv=0; + globlist1(runq->argv->words); + poplist(); + pushlist(); + if(globv){ + for(a=globv;a->next;a=a->next); + a->next=runq->argv->words; + runq->argv->words=globv; + } +} + +#define NDIR 128 +int +globsize(char *p) +{ + ulong isglob=0, globlen=NDIR+1; + + for(;*p;p++){ + if(*p==GLOB){ + p++; + if(*p!=GLOB) isglob++; + globlen+=*p=='*'?NDIR:1; + } + else + globlen++; + } + return isglob?globlen:0; +} diff --git a/utils/rcsh/here.c b/utils/rcsh/here.c new file mode 100644 index 00000000..bd1f79cc --- /dev/null +++ b/utils/rcsh/here.c @@ -0,0 +1,145 @@ +#include "rc.h" +#include "y.tab.h" + +Here *here, **ehere; +int ser; + +char tmp[]="/tmp/here0000.0000"; +char hex[]="0123456789abcdef"; + +void psubst(Io*, char*); +void pstrs(Io*, Word*); + +void hexnum(char *p, int n) +{ + *p++=hex[(n>>12)&0xF]; + *p++=hex[(n>>8)&0xF]; + *p++=hex[(n>>4)&0xF]; + *p=hex[n&0xF]; +} + +Tree * +heredoc(Tree *tag) +{ + Here *h=new(Here); + + if(tag->type!=WORD) + yyerror("Bad here tag"); + h->next=0; + if(here) + *ehere=h; + else + here=h; + ehere=&h->next; + h->tag=tag; + hexnum(&tmp[9], getpid()); + hexnum(&tmp[14], ser++); + h->name=strdup(tmp); + return token(tmp, WORD); +} +/* + * bug: lines longer than NLINE get split -- this can cause spurious + * missubstitution, or a misrecognized EOF marker. + */ +#define NLINE 4096 +void +readhere(void) +{ + Here *h, *nexth; + Io *f; + char *s, *tag; + int c, subst; + char line[NLINE+1]; + + for(h=here;h;h=nexth){ + subst=!h->tag->quoted; + tag=h->tag->str; + c=create(h->name, 1, 0666); + if(c<0) yyerror("can't create here document"); + f=openfd(c); + s=line; + pprompt(); + while((c=rchr(runq->cmdfd))!=EOF){ + if(c=='\n' || s==&line[NLINE]){ + *s='\0'; + if(strcmp(line, tag)==0) break; + if(subst) psubst(f, line); + else pstr(f, line); + s=line; + if(c=='\n'){ + pprompt(); + pchr(f, c); + } + else *s++=c; + } + else *s++=c; + } + flush(f); + closeio(f); + cleanhere(h->name); + nexth=h->next; + free(h); + } + here=0; + doprompt=1; +} + +void +psubst(Io *f, char *s) +{ + char *t, *u; + int savec, n; + Word *star; + + while(*s){ + if(*s!='$'){ + if(0xa0<=(*s&0xff) && (*s&0xff)<=0xf5){ + pchr(f, *s++); + if(*s=='\0') break; + } + else if(0xf6<=(*s&0xff) && (*s&0xff)<=0xf7){ + pchr(f, *s++); + if(*s=='\0') break; + pchr(f, *s++); + if(*s=='\0') break; + } + pchr(f, *s++); + } + else{ + t=++s; + if(*t=='$') pchr(f, *t++); + else{ + while(*t && idchr(*t)) t++; + savec=*t; + *t='\0'; + n=0; + for(u=s;*u && '0'<=*u && *u<='9';u++) n=n*10+*u-'0'; + if(n && *u=='\0'){ + star=vlook("*")->val; + if(star && 1<=n && n<=count(star)){ + while(--n) star=star->next; + pstr(f, star->word); + } + } + else + pstrs(f, vlook(s)->val); + *t=savec; + if(savec=='^') t++; + } + s=t; + } + } +} + +void +pstrs(Io *f, Word *a) +{ + if(a){ + while(a->next && a->next->word){ + pstr(f, a->word); + pchr(f, ' '); + a=a->next; + } + pstr(f, a->word); + } +} diff --git a/utils/rcsh/io.c b/utils/rcsh/io.c new file mode 100644 index 00000000..6ac2555e --- /dev/null +++ b/utils/rcsh/io.c @@ -0,0 +1,238 @@ +#include "rc.h" + +int pfmtnest=0; + +void pdec(Io*, long); +void poct(Io*, ulong); +void phex(Io*, long); +void pquo(Io*, char*); +void pwrd(Io*, char*); +void pcmd(Io*, Tree*); +void pval(Io*, Word*); + +void +pfmt(Io *f, char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + pfmtnest++; + for(;*fmt;fmt++) { + if(*fmt!='%') pchr(f, *fmt); + else switch(*++fmt){ + case '\0': va_end(ap); return; + case 'c': pchr(f, va_arg(ap, int)); break; + case 'd': pdec(f, va_arg(ap, int)); break; + case 'o': poct(f, va_arg(ap, unsigned)); break; + case 'p': phex(f, (long)va_arg(ap, char *)); break; /*unportable*/ + case 'Q': pquo(f, va_arg(ap, char *)); break; + case 'q': pwrd(f, va_arg(ap, char *)); break; + case 'r': perr(f); break; + case 's': pstr(f, va_arg(ap, char *)); break; + case 't': pcmd(f, va_arg(ap, Tree *)); break; + case 'v': pval(f, va_arg(ap, Word *)); break; + default: pchr(f, *fmt); break; + } + } + va_end(ap); + if(--pfmtnest==0) flush(f); +} + +void +perr(Io *f) +{ + char err[ERRMAX]; + + err[0] = 0; + errstr(err, sizeof err); + pstr(f, err); + errstr(err, sizeof err); +} + +void +pquo(Io *f, char *s) +{ + pchr(f, '\''); + for(;*s;s++) + if(*s=='\'') pfmt(f, "''"); + else pchr(f, *s); + pchr(f, '\''); +} + +void +pwrd(Io *f, char *s) +{ + char *t; + for(t=s;*t;t++) + if(!wordchr(*t)) + break; + if(t==s || *t) + pquo(f, s); + else + pstr(f, s); +} + +void +phex(Io *f, long p) +{ + int n; + for(n=28;n>=0;n-=4) pchr(f, "0123456789ABCDEF"[(p>>n)&0xF]); +} + +void +pstr(Io *f, char *s) +{ + if(s==0) + s="(null)"; + while(*s) + pchr(f, *s++); +} + +void +pdec(Io *f, long n) +{ + if(n<0){ + n=-n; + if(n>=0){ + pchr(f, '-'); + pdec(f, n); + return; + } + /* n is two's complement minimum integer */ + n=1-n; + pchr(f, '-'); + pdec(f, n/10); + pchr(f, n%10+'1'); + return; + } + if(n>9) pdec(f, n/10); + pchr(f, n%10+'0'); +} + +void +poct(Io *f, ulong n) +{ + if(n>7) poct(f, n>>3); + pchr(f, (n&7)+'0'); +} + +void +pval(Io *f, Word *a) +{ + if(a){ + while(a->next && a->next->word){ + pwrd(f, a->word); + pchr(f, ' '); + a=a->next; + } + pwrd(f, a->word); + } +} + +int +fullbuf(Io *f, int c) +{ + flush(f); + return *f->bufp++=c; +} + +void +flush(Io *f) +{ + int n; + char *s; + if(f->strp){ + n=f->ebuf-f->strp; + f->strp=realloc(f->strp, n+101); + if(f->strp==0) + panic("Can't realloc %d bytes in flush!", n+101); + f->bufp=f->strp+n; + f->ebuf=f->bufp+100; + for(s=f->bufp;s<=f->ebuf;s++) *s='\0'; + } + else{ + n=f->bufp-f->buf; + if(n && write(f->fd, f->buf, n) < 0){ +/* write(3, "Write error\n", 12); + if(ntrap.ref) + dotrap(); +*/ + } + f->bufp=f->buf; + f->ebuf=f->buf+NBUF; + } +} + +Io * +openfd(int fd) +{ + Io *f = new(Io); + f->fd = fd; + f->bufp = f->ebuf = f->buf; + f->strp = 0; + return f; +} + +Io * +openstr(void) +{ + Io *f=new(struct Io); + char *s; + f->fd=-1; + f->bufp=f->strp=malloc(101); + f->ebuf=f->bufp+100; + for(s=f->bufp;s<=f->ebuf;s++) + *s='\0'; + return f; +} + +/* + * Open a corebuffer to read. EOF occurs after reading len + * characters from buf. + */ +Io * +opencore(char *s, int len) +{ + Io *f; + char *buf; + + f = new(Io); + buf = malloc(len); + f->fd = -1; + f->bufp = f->strp=buf; + f->ebuf = buf+len; + memmove(buf, s, len); + + return f; +} + +void +rewind(Io *io) +{ + if(io->fd==-1) { + io->bufp = io->strp; + } else { + io->bufp = io->ebuf = io->buf; + seek(io->fd, 0L, 0); + } +} + +void +closeio(Io *io) +{ + if(io->fd>=0) + close(io->fd); + if(io->strp) + free(io->strp); + free(io); +} + +int +emptybuf(Io *f) +{ + int n; + if(f->fd==-1 || (n=read(f->fd, f->buf, NBUF))<=0) return EOF; + f->bufp=f->buf; + f->ebuf=f->buf+n; + return *f->bufp++&0xff; +} diff --git a/utils/rcsh/lex.c b/utils/rcsh/lex.c new file mode 100644 index 00000000..d31a94d8 --- /dev/null +++ b/utils/rcsh/lex.c @@ -0,0 +1,398 @@ +#include "rc.h" +#include "y.tab.h" + +#define NTOK 8192 + +int getnext(void); + +int future=EOF; +int doprompt=1; +int inquote; +int nerror; +char *promptstr; + +char tok[NTOK]; + +int lastdol; /* was the last token read '$' or '$#' or '"'? */ +int lastword; /* was the last token read a word or compound word terminator? */ +int lastc; + +void +kinit(void) +{ + kenter(FOR, "for"); + kenter(IN, "in"); + kenter(WHILE, "while"); + kenter(IF, "if"); + kenter(NOT, "not"); + kenter(TWIDDLE, "~"); + kenter(BANG, "!"); + kenter(SUBSHELL, "@"); + kenter(SWITCH, "switch"); + kenter(FN, "fn"); +} + +int +wordchr(int c) +{ + return !strchr("\n \t\r#;&|^$=`'{}()<>", c) && c!=EOF; +} + +int +idchr(int c) +{ + /* + * Formerly: + * return 'a'<=c && c<='z' || 'A'<=c && c<='Z' || '0'<=c && c<='9' + * || c=='_' || c=='*'; + */ + return c>' ' && !strchr("!\"#$%&'()+,-./:;<=>?@[\\]^`{|}~", c); +} + +/* + * Look ahead in the input stream + */ +int +nextc(void) +{ + if(future==EOF) + future=getnext(); + return future; +} + +/* + * Consume the lookahead character. + */ +int +advance(void) +{ + int c=nextc(); + lastc=future; + future=EOF; + return c; +} + +/* + * read a character from the input stream + */ +int +getnext(void) +{ + register int c; + static peekc=EOF; + if(peekc!=EOF){ + c=peekc; + peekc=EOF; + return c; + } + if(runq->eof) return EOF; + if(doprompt) + pprompt(); + c=rchr(runq->cmdfd); + if(!inquote && c=='\\'){ + c=rchr(runq->cmdfd); + if(c=='\n'){ + doprompt=1; + c=' '; + } + else{ + peekc=c; + c='\\'; + } + } + doprompt=doprompt || c=='\n' || c==EOF; + if(c==EOF) runq->eof++; + else if(flag['V'] || ndot>=2 && flag['v']) + pchr(err, c); + return c; +} + +void +pprompt(void) +{ + Var *prompt; + + if(runq->iflag){ + pstr(err, promptstr); + flush(err); + prompt=vlook("prompt"); + if(prompt->val && prompt->val->next) + promptstr=prompt->val->next->word; + else + promptstr="\t"; + } + runq->lineno++; + doprompt=0; +} + +void +skipwhite(void) +{ + int c; + for(;;){ + c=nextc(); + if(c=='#'){ /* Why did this used to be if(!inquote && c=='#') ?? */ + for(;;){ + c=nextc(); + if(c=='\n' || c==EOF) break; + advance(); + } + } + if(c==' ' || c=='\t' || c=='\r') advance(); + else return; + } +} + +void +skipnl(void) +{ + int c; + for(;;){ + skipwhite(); + c=nextc(); + if(c!='\n') return; + advance(); + } +} + +int +nextis(int c) +{ + if(nextc()==c){ + advance(); + return 1; + } + return 0; +} + +char * +addtok(char *p, int val) +{ + if(p==0) return 0; + if(p==&tok[NTOK]){ + *p=0; + yyerror("token buffer too short"); + return 0; + } + *p++=val; + return p; +} + +char * +addutf(char *p, int c) +{ + p=addtok(p, c); + if(twobyte(c)) /* 2-byte escape */ + return addtok(p, advance()); + if(threebyte(c)){ /* 3-byte escape */ + p=addtok(p, advance()); + return addtok(p, advance()); + } + return p; +} + +int +yylex(void) +{ + int c, d=nextc(); + char *w=tok; + Tree *t; + + yylval.tree=0; + /* + * Embarassing sneakiness: if the last token read was a quoted or unquoted + * WORD then we alter the meaning of what follows. If the next character + * is `(', we return SUB (a subscript paren) and consume the `('. Otherwise, + * if the next character is the first character of a simple or compound word, + * we insert a `^' before it. + */ + if(lastword){ + lastword=0; + if(d=='('){ + advance(); + strcpy(tok, "( [SUB]"); + return SUB; + } + if(wordchr(d) || d=='\'' || d=='`' || d=='$' || d=='"'){ + strcpy(tok, "^"); + return '^'; + } + } + inquote=0; + skipwhite(); + switch(c=advance()){ + case EOF: + lastdol=0; + strcpy(tok, "EOF"); + return EOF; + case '$': + lastdol=1; + if(nextis('#')){ + strcpy(tok, "$#"); + return COUNT; + } + if(nextis('"')){ + strcpy(tok, "$\""); + return '"'; + } + strcpy(tok, "$"); + return '$'; + case '&': + lastdol=0; + if(nextis('&')){ + skipnl(); + strcpy(tok, "&&"); + return ANDAND; + } + strcpy(tok, "&"); + return '&'; + case '|': + lastdol=0; + if(nextis(c)){ + skipnl(); + strcpy(tok, "||"); + return OROR; + } + case '<': + case '>': + lastdol=0; + /* + * funny redirection tokens: + * redir: arrow | arrow '[' fd ']' + * arrow: '<' | '<<' | '>' | '>>' | '|' + * fd: digit | digit '=' | digit '=' digit + * digit: '0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' + * some possibilities are nonsensical and get a message. + */ + *w++=c; + t=newtree(); + switch(c){ + case '|': + t->type=PIPE; + t->fd0=1; + t->fd1=0; + break; + case '>': + t->type=REDIR; + if(nextis(c)){ + t->rtype=APPEND; + *w++=c; + } + else t->rtype=WRITE; + t->fd0=1; + break; + case '<': + t->type=REDIR; + if(nextis(c)){ + t->rtype=HERE; + *w++=c; + } + else t->rtype=READ; + t->fd0=0; + break; + } + if(nextis('[')){ + *w++='['; + c=advance(); + *w++=c; + if(c<'0' || '9'<c){ + RedirErr: + *w=0; + yyerror(t->type==PIPE?"pipe syntax" + :"redirection syntax"); + return EOF; + } + t->fd0=0; + do{ + t->fd0=t->fd0*10+c-'0'; + *w++=c; + c=advance(); + }while('0'<=c && c<='9'); + if(c=='='){ + *w++='='; + if(t->type==REDIR) + t->type=DUP; + c=advance(); + if('0'<=c && c<='9'){ + t->rtype=DUPFD; + t->fd1=t->fd0; + t->fd0=0; + do{ + t->fd0=t->fd0*10+c-'0'; + *w++=c; + c=advance(); + }while('0'<=c && c<='9'); + } + else{ + if(t->type==PIPE) goto RedirErr; + t->rtype=CLOSE; + } + } + if(c!=']' || t->type==DUP && (t->rtype==HERE || t->rtype==APPEND)) + goto RedirErr; + *w++=']'; + } + *w='\0'; + yylval.tree=t; + if(t->type==PIPE) skipnl(); + return t->type; + case '\'': + lastdol=0; + lastword=1; + inquote=1; + for(;;){ + c=advance(); + if(c==EOF) break; + if(c=='\''){ + if(nextc()!='\'') + break; + advance(); + } + w=addutf(w, c); + } + if(w!=0) *w='\0'; + t=token(tok, WORD); + t->quoted=1; + yylval.tree=t; + return t->type; + } + if(!wordchr(c)){ + lastdol=0; + tok[0]=c; + tok[1]='\0'; + return c; + } + for(;;){ + /* next line should have (char)c==GLOB, but ken's compiler is broken */ + if(c=='*' || c=='[' || c=='?' || c==(unsigned char)GLOB) + w=addtok(w, GLOB); + w=addutf(w, c); + c=nextc(); + if(lastdol?!idchr(c):!wordchr(c)) break; + advance(); + } +Out: + lastword=1; + lastdol=0; + if(w!=0) *w='\0'; + t=klook(tok); + if(t->type!=WORD) lastword=0; + t->quoted=0; + yylval.tree=t; + return t->type; +} + +void +yyerror(char *m) +{ + pfmt(err, "rc: "); + if(runq->cmdfile) pfmt(err, "file %s: ", runq->cmdfile); + if(!runq->iflag) pfmt(err, "line %d: ", runq->lineno); + if(tok[0] && tok[0]!='\n') pfmt(err, "token %q: ", tok); + pfmt(err, "%s\n", m); + flush(err); + lastword=0; + lastdol=0; + while(lastc!='\n' && lastc!=EOF) advance(); + nerror++; +} diff --git a/utils/rcsh/main.c b/utils/rcsh/main.c new file mode 100644 index 00000000..93cd2951 --- /dev/null +++ b/utils/rcsh/main.c @@ -0,0 +1,228 @@ +#include "rc.h" + +int flag[256]; +Io *err; +char *argv0; + +Thread *runq; +int ndot; + + +void +main(int argc, char *argv[]) +{ + int i; + Code bootstrap[17]; + char *cflag, *cp; + char rcmain[200]; + Var *infroot; + char **p; + + cflag = 0; + + /* default to interactive mode */ + flag['i']++; + + /* hack for DOS-style options, when rcsh started from MS-land */ + for (p = argv+1; *p && **p == '/'; p++) + **p = '-'; + + argv0 = *argv; + ARGBEGIN{ + default: + fprint(2, "usage: %s: [-seiIlvxr] [-c string] [file [args]]\n", argv0); + exits("usage"); + case 'e': flag['e']++; break; + case 'c': cflag = ARGF(); break; + case 'i': flag['i']++; break; + case 'I': flag['i'] = 0; break; + case 'l': flag['l']++; break; + case 'r': flag['r']++; break; + case 's': flag['s']++; break; + case 'S': flag['S']++; break; /* sub shell */ + case 'v': flag['v']++; break; + case 'V': flag['V']++; break; + case 'x': flag['x']++; break; + }ARGEND + + err = openfd(2); + + kinit(); + vinit(); + + cp = ROOT; + if(0 && strlen(argv0)) + sprint(rcmain, "%s/../lib/rcmain", argv0); + else{ + infroot = vlook("ROOT"); + if(infroot->val) + cp = infroot->val->word; + } + sprint(rcmain, "%s/utils/lib/rcmain", cp); + + setvar("rcname", newword(argv0, 0)); + if(cflag) + setvar("cflag", newword(cflag, 0)); + else + setvar("cflag", 0); + + /* bootstrap == . rcmain $* */ + i=0; + bootstrap[i++].i=1; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f=Xassign; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xmark; + bootstrap[i++].f=Xword; + bootstrap[i++].s="*"; + bootstrap[i++].f=Xdol; + bootstrap[i++].f=Xword; + bootstrap[i++].s=rcmain; + bootstrap[i++].f=Xword; + bootstrap[i++].s="."; + bootstrap[i++].f=Xsimple; + bootstrap[i++].f=Xexit; + bootstrap[i].i=0; + start(bootstrap, 1, 0); + pushlist(); + for(i=argc-1;i>=0;i--) + pushword(argv[i]); + + for(;;){ + if(flag['r']) pfnc(err, runq); + runq->pc++; + (*runq->code[runq->pc-1].f)(); + if(ntrap.ref) + dotrap(); + } +} + +void +panic(char *s, int n) +{ + pfmt(err, "rc: "); + pfmt(err, s, n); + pchr(err, '\n'); + flush(err); + pfmt(err, "aborting\n"); + flush(err); + exits("aborting"); +} + +void +setstatus(char *s) +{ + setvar("status", newword(s, 0)); +} + +char * +getstatus(void) +{ + Var *status=vlook("status"); + + return status->val?status->val->word:""; +} + +int +truestatus(void) +{ + char *s; + for(s=getstatus();*s;s++) + if(*s!='|' && *s!='0') return 0; + return 1; +} + +char * +concstatus(char *s, char *t) +{ + static char v[NSTATUS+1]; + int n=strlen(s); + strncpy(v, s, NSTATUS); + if(n<NSTATUS){ + v[n]='|'; + strncpy(v+n+1, t, NSTATUS-n-1); + } + v[NSTATUS]='\0'; + return v; +} + +/* + * Start executing the given code at the given pc with the given redirection + */ +void +start(Code *c, int pc, Var *local) +{ + Thread *p = new(Thread); + + memset(p, 0, sizeof(Thread)); + p->code = codecopy(c); + p->pc = pc; + if(runq) { + p->redir = runq->redir; + p->startredir = runq->redir; + } + p->local = local; + p->lineno = 1; + p->ret = runq; + runq=p; +} + +void +execcmds(Io *f) +{ + static Code rdcmds[4]; + static int first=1; + + if(first){ + rdcmds[0].i=1; + rdcmds[1].f=Xrdcmds; + rdcmds[2].f=Xreturn; + first=0; + } + start(rdcmds, 1, runq->local); + runq->cmdfd=f; + runq->iflast=0; +} + +void +waitfor(uint pid) +{ + int e; + char estr[64]; + + e = procwait(pid); + if(e != 0) { + sprint(estr, "error code %d", e); + setstatus(estr); + } else + setstatus(""); +} + +char ** +procargv(char *s0, char *s1, char *s2, char *s3, Word *w) +{ + int n, i; + Word *p; + char **argv; + + for(p=w,n=5; p; p=p->next,n++); + ; + + argv = malloc(n*sizeof(char*)); + i = 0; + if(s0) + argv[i++] = s0; + if(s1) + argv[i++] = s1; + if(s2) + argv[i++] = s2; + if(s3) + argv[i++] = s3; + for(p=w; p; p=p->next) + argv[i++] = p->word; + argv[i] = 0; + return argv; +} + diff --git a/utils/rcsh/mkfile b/utils/rcsh/mkfile new file mode 100644 index 00000000..b4c818cf --- /dev/null +++ b/utils/rcsh/mkfile @@ -0,0 +1,53 @@ +<../../mkconfig + +# +# this directory contains a stripped-down version of rc +# it is only build for Windows Nt and Windows 95 + +TARG=rcsh + +OFILES= code.$O\ + exec.$O\ + glob.$O\ + here.$O\ + io.$O\ + lex.$O\ + main.$O\ + $TARGMODEL.$O\ + pcmd.$O\ + pfnc.$O\ + simple.$O\ + trap.$O\ + tree.$O\ + var.$O\ + word.$O\ + y.tab.$O\ + +HFILES= rc.h\ + y.tab.h\ + +YFILES= syn.y + +LIBS=9 + +BIN=$ROOT/$OBJDIR/bin + +<$ROOT/mkfiles/mkone-$SHELLTYPE + +CFLAGS= $CFLAGS '-DROOT="'$ROOT'"' + +$BIN/%:Q: $O.out + echo $TARG must be installed manually on Windows systems + echo use: cp $O.out $target + cp $O.out $target + +install:V: $ROOT/utils/lib/rcmain + +$ROOT/utils/lib/rcmain:Q: rcmain + echo $prereq must be installed manually on Windows systems + echo use: cp $prereq $target + cp $prereq $target + +Posix.c Inferno.c:QV: + echo $TARG is only built on Windows NT or Windows 95 + exit 1 diff --git a/utils/rcsh/pcmd.c b/utils/rcsh/pcmd.c new file mode 100644 index 00000000..9c5eb8a6 --- /dev/null +++ b/utils/rcsh/pcmd.c @@ -0,0 +1,110 @@ +#include "rc.h" +#include "y.tab.h" + +char nl='\n'; /* change to semicolon for bourne-proofing */ + +#define c0 t->child[0] +#define c1 t->child[1] +#define c2 t->child[2] + +void +pdeglob(Io *f, char *s) +{ + while(*s){ + if(*s==GLOB) s++; + pchr(f, *s++); + } +} + +void +pcmd(Io *f, Tree *t) +{ + if(t==0) + return; + + switch(t->type){ + default: pfmt(f, "bad %d %p %p %p", t->type, c0, c1, c2); break; + case '$': pfmt(f, "$%t", c0); break; + case '"': pfmt(f, "$\"%t", c0); break; + case '&': pfmt(f, "%t&", c0); break; + case '^': pfmt(f, "%t^%t", c0, c1); break; + case '`': pfmt(f, "`%t", c0); break; + case ANDAND: pfmt(f, "%t && %t", c0, c1); break; + case ARGLIST: pfmt(f, "%t %t", c0, c1); break; + case BANG: pfmt(f, "! %t", c0); break; + case BRACE: pfmt(f, "{%t}", c0); break; + case COUNT: pfmt(f, "$#%t", c0); break; + case FN: pfmt(f, "fn %t %t", c0, c1); break; + case IF: pfmt(f, "if%t%t", c0, c1); break; + case NOT: pfmt(f, "if not %t", c0); break; + case OROR: pfmt(f, "%t || %t", c0, c1); break; + case PCMD: + case PAREN: pfmt(f, "(%t)", c0); break; + case SUB: pfmt(f, "$%t(%t)", c0, c1); break; + case SIMPLE: pfmt(f, "%t", c0); break; + case SUBSHELL: pfmt(f, "@ %t", c0); break; + case SWITCH: pfmt(f, "switch %t %t", c0, c1); break; + case TWIDDLE: pfmt(f, "~ %t %t", c0, c1); break; + case WHILE: pfmt(f, "while %t%t", c0, c1); break; + case ';': + if(c0){ + if(c1) pfmt(f, "%t%c%t", c0, nl, c1); + else pfmt(f, "%t", c0); + } + else pfmt(f, "%t", c1); + break; + case WORDS: + if(c0) pfmt(f, "%t ", c0); + pfmt(f, "%t", c1); + break; + case FOR: + pfmt(f, "for(%t", c0); + if(c1) pfmt(f, " in %t", c1); + pfmt(f, ")%t", c2); + break; + case WORD: + if(t->quoted) pfmt(f, "%Q", t->str); + else pdeglob(f, t->str); + break; + case DUP: + pfmt(f, ">[%d=", t->fd0); + if(t->rtype==DUPFD) + pfmt(f, "%d", t->fd1); + /* else t->rtype == CLOSE */ + pchr(f, ']'); + break; + case PIPEFD: + case REDIR: + switch(t->rtype){ + case HERE: + pchr(f, '<'); + case READ: + pchr(f, '<'); + if(t->fd0!=0) + pfmt(f, "[%d]", t->fd0); + break; + case APPEND: + pchr(f, '>'); + case WRITE: + pchr(f, '>'); + if(t->fd0!=1) + pfmt(f, "[%d]", t->fd0); + break; + } + pfmt(f, "%t", c0); + if(c1) pfmt(f, " %t", c1); + break; + case '=': + pfmt(f, "%t=%t", c0, c1); + if(c2) pfmt(f, " %t", c2); + break; + case PIPE: + pfmt(f, "%t|", c0); + if(t->fd1==0){ + if(t->fd0!=1) pfmt(f, "[%d]", t->fd0); + } + else pfmt(f, "[%d=%d]", t->fd0, t->fd1); + pfmt(f, "%t", c1); + break; + } +} diff --git a/utils/rcsh/pfnc.c b/utils/rcsh/pfnc.c new file mode 100644 index 00000000..4116b087 --- /dev/null +++ b/utils/rcsh/pfnc.c @@ -0,0 +1,70 @@ +#include "rc.h" + +struct{ + void (*f)(void); + char *name; +}fname[]={ + Xappend, "Xappend", + Xasync, "Xasync", + Xbang, "Xbang", + Xclose, "Xclose", + Xdup, "Xdup", + Xeflag, "Xeflag", + Xexit, "Xexit", + Xfalse, "Xfalse", + Xifnot, "Xifnot", + Xjump, "Xjump", + Xmark, "Xmark", + Xpopm, "Xpopm", + Xread, "Xread", + Xreturn, "Xreturn", + Xtrue, "Xtrue", + Xif, "Xif", + Xwastrue, "Xwastrue", + Xword, "Xword", + Xwrite, "Xwrite", + Xmatch, "Xmatch", + Xcase, "Xcase", + Xconc, "Xconc", + Xassign, "Xassign", + Xdol, "Xdol", + Xcount, "Xcount", + Xlocal, "Xlocal", + Xunlocal, "Xunlocal", + Xfn, "Xfn", + Xdelfn, "Xdelfn", + Xpipe, "Xpipe", + Xpipewait, "Xpipewait", + Xrdcmds, "Xrdcmds", + Xbackq, "Xbackq", + Xpipefd, "Xpipefd", + Xsubshell, "Xsubshell", + Xdelhere, "Xdelhere", + Xfor, "Xfor", + Xglob, "Xglob", + Xsimple, "Xsimple", + Xqdol, "Xqdol", + 0 +}; + +void +pfnc(Io *fd, Thread *t) +{ + int i; + void (*fn)(void)=t->code[t->pc].f; + List *a; + + pfmt(fd, "pid %d cycle %p %d ", getpid(), t->code, t->pc); + for(i=0;fname[i].f;i++) { + if(fname[i].f==fn) { + pstr(fd, fname[i].name); + break; + } + } + if(!fname[i].f) + pfmt(fd, "%p", fn); + for(a=t->argv;a;a=a->next) + pfmt(fd, " (%v)", a->words); + pchr(fd, '\n'); + flush(fd); +} diff --git a/utils/rcsh/rc.h b/utils/rcsh/rc.h new file mode 100644 index 00000000..f7d56746 --- /dev/null +++ b/utils/rcsh/rc.h @@ -0,0 +1,295 @@ +#include <lib9.h> + +#define Lock Rclock +#define Ref Rcref + +typedef union Code Code; +typedef struct Tree Tree; +typedef struct Thread Thread; +typedef struct Word Word; +typedef struct Var Var; +typedef struct List List; +typedef struct Redir Redir; +typedef struct Io Io; +typedef struct Here Here; +typedef struct Ref Ref; +typedef struct Lock Lock; +typedef struct Direntry Direntry; + +#define EOF (-1) +#define NBUF 512 + +/* values for Tree->rtype */ +#define APPEND 1 +#define WRITE 2 +#define READ 3 +#define HERE 4 +#define DUPFD 5 +#define CLOSE 6 + +/* + * redir types + */ +#define ROPEN 1 /* dup2(from, to); close(from); */ +#define RDUP 2 /* dup2(from, to); */ +#define RCLOSE 3 /* close(from); */ + +#define NSTATUS 64 /* length of status (from plan 9) */ + +#define IWS 0x01 /* inter word seperator when word lists are stored in env variables */ + +/* + * Glob character escape in strings: + * In a string, GLOB must be followed by *?[ or GLOB. + * GLOB* matches any string + * GLOB? matches any single character + * GLOB[...] matches anything in the brackets + * GLOBGLOB matches GLOB + */ +#define GLOB ((char)0x02) + +/* + * The first word of any code vector is a reference count. + * Always create a new reference to a code vector by calling codecopy(.). + * Always call codefree(.) when deleting a reference. + */ +union Code { + void (*f)(void); + int i; + char *s; +}; + + +struct Tree +{ + int type; + int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */ + char *str; + int quoted; + int iskw; + Tree *child[3]; + Tree *next; +}; + +struct Thread +{ + Code *code; /* code for this thread */ + int pc; /* code[pc] is the next instruction */ + List *argv; /* argument stack */ + Redir *redir; /* redirection stack */ + Redir *startredir; /* redir inheritance point */ + Var *local; /* list of local variables */ + char *cmdfile; /* file name in Xrdcmd */ + Io *cmdfd; /* file descriptor for Xrdcmd */ + int iflast; /* static `if not' checking */ + int eof; /* is cmdfd at eof? */ + int iflag; /* interactive? */ + int lineno; /* linenumber */ + int pid; /* process for Xpipewait to wait for */ + char status[NSTATUS]; /* status for Xpipewait */ + Tree *treenodes; /* tree nodes created by this process */ + Thread *ret; /* who continues when this finishes */ +}; + +struct Io +{ + int fd; + char *bufp; + char *ebuf; + char *strp; + char buf[NBUF]; +}; + +struct Var +{ + char *name; /* ascii name */ + Word *val; /* value */ + int changed; + Code *fn; /* pointer to function's code vector */ + int fnchanged; + int pc; /* pc of start of function */ + Var *next; /* next on hash or local list */ +}; + +struct Word +{ + char *word; + Word *next; +}; + +struct List +{ + Word *words; + List *next; +}; + +struct Redir +{ + char type; /* what to do */ + short from, to; /* what to do it to */ + Redir *next; /* what else to do (reverse order) */ +}; + +struct Here{ + Tree *tag; + char *name; + Here *next; +}; + +struct Lock { + int val; +}; + +struct Ref +{ + Lock lk; + int ref; +}; + +struct Direntry +{ + int isdir; + char *name; +}; + +/* main.c */ +void start(Code *c, int pc, Var *local); + +/* lex.c */ +void yyerror(char*); +int yylex(void); +int yyparse(void); +int wordchr(int); +int idchr(int); + +/* code.c */ +int compile(Tree*); +Code *codecopy(Code*); +void codefree(Code*); +void cleanhere(char *f); + +void skipnl(void); + +void panic(char*, int); + +/* var.c */ +void kinit(void); +void vinit(void); +Var *vlook(char*); +Var *gvlook(char*); +Var *newvar(char*, Var*); +void setvar(char*, Word*); +void updenv(void); +void kenter(int type, char *name); + +/* glob.c */ +void deglob(char*); +void globlist(void); +int match(char *s, char *p, int stop); + +/* main.c */ +void setstatus(char *s); +char *getstatus(void); +int truestatus(void); +void execcmds(Io*); +char *concstatus(char *s, char *t); +char **procargv(char*, char*, char*, char*, Word *w); + +void freewords(Word*); + +/* tree.c */ +Tree *newtree(void); +Tree *token(char*, int), *klook(char*), *tree1(int, Tree*); +Tree *tree2(int, Tree*, Tree*), *tree3(int, Tree*, Tree*, Tree*); +Tree *mung1(Tree*, Tree*), *mung2(Tree*, Tree*, Tree*); +Tree *mung3(Tree*, Tree*, Tree*, Tree*), *epimung(Tree*, Tree*); +Tree *simplemung(Tree*), *heredoc(Tree*); +void freetree(Tree*); +void freenodes(void); + +/* here.c */ +Tree *heredoc(Tree *tag); + +/* exec.c */ +extern void Xappend(void), Xasync(void), Xbackq(void), Xbang(void), Xclose(void); +extern void Xconc(void), Xcount(void), Xdelfn(void), Xdol(void), Xqdol(void), Xdup(void); +extern void Xexit(void), Xfalse(void), Xfn(void), Xfor(void), Xglob(void); +extern void Xjump(void), Xmark(void), Xmatch(void), Xpipe(void), Xread(void); +extern void Xunredir(void), Xstar(void), Xreturn(void), Xsubshell(void); +extern void Xtrue(void), Xword(void), Xwrite(void), Xpipefd(void), Xcase(void); +extern void Xlocal(void), Xunlocal(void), Xassign(void), Xsimple(void), Xpopm(void); +extern void Xrdcmds(void), Xwastrue(void), Xif(void), Xifnot(void), Xpipewait(void); +extern void Xdelhere(void), Xpopredir(void), Xsub(void), Xeflag(void), Xsettrue(void); +extern void Xerror(char*), Xperror(char*); + +/* word.c */ +Word *newword(char*, Word*); +void pushlist(void); +void poplist(void); +void pushword(char*); +void popword(void); +int count(Word*); +Word *copywords(Word*, Word*); +void pushredir(int, int, int); +void turfredir(void); +char *list2str(Word*); +void freelist(Word*); +Word *conclist(Word*, Word*, Word*); +Word *subwords(Word*, int, Word*, Word*); + +/* io.c */ +#define pchr(b, c) if((b)->bufp==(b)->ebuf)fullbuf((b), (c));else (*(b)->bufp++=(c)) +#define rchr(b) ((b)->bufp==(b)->ebuf?emptybuf(b):(*(b)->bufp++&0xff)) + +Io *openfd(int), *openstr(void), *opencore(char*, int); +int emptybuf(Io*); +void closeio(Io*); +void flush(Io*); +int fullbuf(Io*, int); + +void pfmt(Io*, char*, ...); +void perr(Io*); +void pstr(Io*, char*); +void pfnc(Io*, Thread*); + +void pprompt(void); + +/* trap.c */ +void dotrap(void); +void dointr(void); + +void waitfor(uint); + +/* nt.c */ + +Direntry* readdirect(char*); +void fatal(char*, ...); +uint proc(char**, int, int, int); +int procwait(uint); +int refinc(Ref*); +int refdec(Ref*); +int pipe(int*); + +/* + * onebyte(c), twobyte(c), threebyte(c) + * Is c the first character of a one- two- or three-byte utf sequence? + */ +#define onebyte(c) ((c&0x80)==0x00) +#define twobyte(c) ((c&0xe0)==0xc0) +#define threebyte(c) ((c&0xf0)==0xe0) + +#define new(type) ((type *)malloc(sizeof(type))) + + +extern Tree *cmdtree; +extern Thread *runq; +extern Io *err; +extern int flag[256]; +extern int doprompt; +extern char *promptstr; +extern int ndot; +extern int nerror; +extern Code *codebuf; +extern int eflagok; +extern int interrupted; +extern Ref ntrap; diff --git a/utils/rcsh/rcmain b/utils/rcsh/rcmain new file mode 100644 index 00000000..f74190ca --- /dev/null +++ b/utils/rcsh/rcmain @@ -0,0 +1,29 @@ +# rcmain: 9pm version +if(~ $#home 0) home=/ +if(~ $#ifs 0) ifs=' +' +switch($#prompt){ +case 0 +case 1 + prompt=('% ' ' ') +} +if(~ $rcname v.out) prompt=('broken! ' ' ') +if(! ~ $#cflag 0){ + if(flag l && test -r $home/lib/profile) . $home/lib/profile + status='' + eval $cflag +} +if not if(flag i){ + if(flag l && test -r $home/lib/profile) . $home/lib/profile + status='' + if(! ~ $#* 0) . $* + if not . -i 'stdin$' +} +if not { + if(~ $#* 0) . 'stdin$' + if not{ + status='' + . $* + } +} +exit $status diff --git a/utils/rcsh/rcpath b/utils/rcsh/rcpath new file mode 100644 index 00000000..cb19ff4f --- /dev/null +++ b/utils/rcsh/rcpath @@ -0,0 +1,13 @@ +# rcmain: 9pm version +PF='Program Files' +MVS='Microsoft Visual Studio' +PATH=D:/Users/Inferno/Nt/386/bin +PATH=$PATH';'D:/apps/mks/mksnt +PATH=$PATH';'C:/WINNT/system32 +PATH=$PATH';'C:/WINNT +PATH=$PATH';'D:/$PF/$MVS/Common/Tools/WinNT +PATH=$PATH';'D:/$PF/$MVS/Common/MSDev98/Bin +PATH=$PATH';'D:/$PF/$MVS/Common/Tools +PATH=$PATH';'D:/$PF/$MVS/VC98/bin +PATH=$PATH';'D:/MSSQL7/BINN +PATH=$PATH';'D:/$PF/Mts diff --git a/utils/rcsh/simple.c b/utils/rcsh/simple.c new file mode 100644 index 00000000..1c3a89b3 --- /dev/null +++ b/utils/rcsh/simple.c @@ -0,0 +1,528 @@ +#include "rc.h" + +typedef struct Builtin Builtin; + +struct Builtin +{ + char *name; + void (*fnc)(void); +}; + +int exitnext(void); +void execexec(void); +void execfunc(Var *func); +void execcd(void); +void execwhatis(void); +void execeval(void); +void execexit(void); +void execshift(void); +void execwait(void); +void execdot(void); +void execflag(void); + +Builtin builtin[]={ + "cd", execcd, + "whatis", execwhatis, + "eval", execeval, + "exec", execexec, /* but with popword first */ + "exit", execexit, + "shift", execshift, + "wait", execwait, + ".", execdot, + "flag", execflag, + 0 +}; + +int mapfd(int fd); + +void +Xsimple(void) +{ + Word *a; + Thread *p=runq; + Var *v; + Builtin *bp; + uint pid; + char **argv; + + globlist(); + a=runq->argv->words; + if(a==0){ + Xerror("empty argument list"); + return; + } + if(flag['x']) + pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ + + v=gvlook(a->word); + if(v->fn) + execfunc(v); + else{ + if(strcmp(a->word, "builtin")==0){ + if(count(a)==1){ + pfmt(err, "builtin: empty argument list\n"); + setstatus("empty arg list"); + poplist(); + return; + } + a=a->next; + popword(); + } + for(bp=builtin;bp->name;bp++) { + if(strcmp(a->word, bp->name)==0){ + (*bp->fnc)(); + return; + } + } + + updenv(); + argv = procargv(0, 0, 0, 0, runq->argv->words); + pid = proc(argv, mapfd(0), mapfd(1), mapfd(2)); + free(argv); + + if(pid == 0) + pfmt(err, "%s: %r\n", runq->argv->words->word); + else + waitfor(pid); + poplist(); +#ifdef XXX + if(exitnext()){ + /* fork and wait is redundant */ + pushword("exec"); + execexec(); + Xexit(); + } + else{ + flush(err); + updenv(); + switch(pid=fork()){ + case -1: + Xperror("try again"); + return; + case 0: + pushword("exec"); + execexec(); + Exit("can't exec"); + default: + poplist(); + /* interrupts don't get us out */ + while(Waitfor(pid, 1) < 0) + ; + } + } +#endif + } +} + +Word nullpath={ "", 0}; + +Word * +searchpath(char *w) +{ + Word *path; + + return &nullpath; + + if(strncmp(w, "/", 1) == 0 + || strncmp(w, "#", 1) == 0 + || *w && w[1] == ':' + || strncmp(w, "./", 2) == 0 + || strncmp(w, "../", 3) == 0 + || (path=vlook("path")->val) == 0) + path=&nullpath; + return path; +} + +/* + * Search through the following code to see if we're just going to exit. + */ +int +exitnext(void) +{ + Code *c=&runq->code[runq->pc]; + + while(c->f==Xpopredir) + c++; + return c->f==Xexit; +} + +#ifdef XXX + +void +doredir(Redir *rp) +{ + if(rp){ + doredir(rp->next); + switch(rp->type){ + case ROPEN: + if(rp->from!=rp->to){ + Dup(rp->from, rp->to); + close(rp->from); + } + break; + case RDUP: Dup(rp->from, rp->to); break; + case RCLOSE: close(rp->from); break; + } + } +} + + +#endif + +void +execexec(void) +{ + popword(); /* "exec" */ + if(runq->argv->words==0){ + Xerror("empty argument list"); + return; + } + fatal("execexec not done yet"); +/* + doredir(runq->redir); + Execute(runq->argv->words, searchpath(runq->argv->words->word)); +*/ + + poplist(); +} + +void +execfunc(Var *func) +{ + Word *starval; + + popword(); + starval=runq->argv->words; + runq->argv->words=0; + poplist(); + start(func->fn, func->pc, 0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=starval; + runq->local->changed=1; +} + +void +execcd(void) +{ + Word *a=runq->argv->words; + Word *cdpath; + char dir[512]; + + setstatus("can't cd"); + cdpath=vlook("cdpath")->val; + switch(count(a)){ + default: + pfmt(err, "Usage: cd [directory]\n"); + break; + case 2: + if(a->next->word[0]=='/' || cdpath==0) cdpath=&nullpath; + for(;cdpath;cdpath=cdpath->next){ + strcpy(dir, cdpath->word); + if(dir[0]) strcat(dir, "/"); + strcat(dir, a->next->word); + if(chdir(dir)>=0){ + if(strlen(cdpath->word) + && strcmp(cdpath->word, ".")!=0) + pfmt(err, "%s\n", dir); + setstatus(""); + break; + } + } + if(cdpath==0) pfmt(err, "Can't cd %s\n", a->next->word); + break; + case 1: + a=vlook("home")->val; + if(count(a)>=1){ + if(chdir(a->word)>=0) + setstatus(""); + else + pfmt(err, "Can't cd %s\n", a->word); + } + else + pfmt(err, "Can't cd -- $home empty\n"); + break; + } + poplist(); +} + +void +execexit(void) +{ + switch(count(runq->argv->words)){ + default: pfmt(err, "Usage: exit [status]\nExiting anyway\n"); + case 2: setstatus(runq->argv->words->next->word); + case 1: Xexit(); + } +} + +void +execflag(void) +{ + char *letter, *val; + switch(count(runq->argv->words)){ + case 2: + setstatus(flag[runq->argv->words->next->word[0]]?"":"flag not set"); + break; + case 3: + letter=runq->argv->words->next->word; + val=runq->argv->words->next->next->word; + if(strlen(letter)==1){ + if(strcmp(val, "+")==0){ + flag[letter[0]]=1; + break; + } + if(strcmp(val, "-")==0){ + flag[letter[0]]=0; + break; + } + } + default: + Xerror("Usage: flag [letter] [+-]"); + return; + } + poplist(); +} + +void +execshift(void) +{ + int n; + Word *a; + Var *star; + switch(count(runq->argv->words)){ + default: + pfmt(err, "Usage: shift [n]\n"); + setstatus("shift usage"); + poplist(); + return; + case 2: n=atoi(runq->argv->words->next->word); break; + case 1: n=1; break; + } + star=vlook("*"); + for(;n && star->val;--n){ + a=star->val->next; + free(star->val->word); + free(star->val); + star->val=a; + star->changed=1; + } + setstatus(""); + poplist(); +} + +int +octal(char *s) +{ + int n=0; + while(*s==' ' || *s=='\t' || *s=='\n') s++; + while('0'<=*s && *s<='7') n=n*8+*s++-'0'; + return n; +} + +void +execeval(void) +{ + char *cmdline, *s, *t; + int len=0; + Word *ap; + + if(count(runq->argv->words)<=1){ + Xerror("Usage: eval cmd ..."); + return; + } + eflagok=1; + for(ap=runq->argv->words->next;ap;ap=ap->next) + len+=1+strlen(ap->word); + cmdline=malloc(len); + s=cmdline; + for(ap=runq->argv->words->next;ap;ap=ap->next){ + for(t=ap->word;*t;) *s++=*t++; + *s++=' '; + } + s[-1]='\n'; + poplist(); + execcmds(opencore(cmdline, len)); + free(cmdline); +} + +void +execdot(void) +{ + int iflag=0; + int fd; + List *av; + Thread *p=runq; + char *zero; + char file[512]; + Word *path; + static int first=1; + static Code dotcmds[14]; + + if(first) { + dotcmds[0].i=1; + dotcmds[1].f=Xmark; + dotcmds[2].f=Xword; + dotcmds[3].s="0"; + dotcmds[4].f=Xlocal; + dotcmds[5].f=Xmark; + dotcmds[6].f=Xword; + dotcmds[7].s="*"; + dotcmds[8].f=Xlocal; + dotcmds[9].f=Xrdcmds; + dotcmds[10].f=Xunlocal; + dotcmds[11].f=Xunlocal; + dotcmds[12].f=Xreturn; + first=0; + } else + eflagok=1; + popword(); + if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ + iflag=1; + popword(); + } + /* get input file */ + if(p->argv->words==0){ + Xerror("Usage: . [-i] file [arg ...]"); + return; + } + zero=strdup(p->argv->words->word); + popword(); + strcpy(file, "**No file name**"); + fd = -1; + if(strcmp(zero, "stdin$") == 0) + fd = dup(0); + else{ + for(path=searchpath(zero);path;path=path->next){ + strcpy(file, path->word); + if(file[0]) + strcat(file, "/"); + strcat(file, zero); + if((fd=open(file, 0))>=0) + break; + } + } + if(fd<0){ + Xperror(file); + return; + } + /* set up for a new command loop */ + start(dotcmds, 1, 0); + pushredir(RCLOSE, fd, 0); + runq->cmdfile=zero; + runq->cmdfd=openfd(fd); + runq->iflag=iflag; + runq->iflast=0; + /* push $* value */ + pushlist(); + runq->argv->words=p->argv->words; + /* free caller's copy of $* */ + av=p->argv; + p->argv=av->next; + free(av); + /* push $0 value */ + pushlist(); + pushword(zero); + ndot++; +} + +void +execwhatis(void) +{ /* mildly wrong -- should fork before writing */ + Word *a, *b, *path; + Var *v; + Builtin *bp; + char file[512]; + Io out[1]; + int found, sep; + + a=runq->argv->words->next; + if(a==0){ + Xerror("Usage: whatis name ..."); + return; + } + setstatus(""); + out->fd=mapfd(1); + out->bufp=out->buf; + out->ebuf=&out->buf[NBUF]; + out->strp=0; + for(;a;a=a->next){ + v=vlook(a->word); + if(v->val){ + pfmt(out, "%s=", a->word); + if(v->val->next==0) + pfmt(out, "%q\n", v->val->word); + else{ + sep='('; + for(b=v->val;b && b->word;b=b->next){ + pfmt(out, "%c%q", sep, b->word); + sep=' '; + } + pfmt(out, ")\n"); + } + found=1; + } + else + found=0; + v=gvlook(a->word); + if(v->fn) pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + else{ + for(bp=builtin;bp->name;bp++) + if(strcmp(a->word, bp->name)==0){ + pfmt(out, "builtin %s\n", a->word); + break; + } + if(!bp->name){ + for(path=searchpath(a->word);path;path=path->next){ + strcpy(file, path->word); + if(file[0]) strcat(file, "/"); + strcat(file, a->word); +#ifdef XXX + if(Executable(file)){ + pfmt(out, "%s\n", file); + break; + } +#endif + } + if(!path && !found){ + pfmt(err, "%s: not found\n", a->word); + setstatus("not found"); + } + } + } + } + poplist(); + flush(err); +} + +void +execwait(void) +{ + fprint(2, "wait: not done yet"); + +#ifdef XXX + switch(count(runq->argv->words)){ + default: Xerror("Usage: wait [pid]"); return; + case 2: Waitfor(atoi(runq->argv->words->next->word), 0); break; + case 1: Waitfor(-1, 0); break; + } + poplist(); +#endif +} + +int +mapfd(int fd) +{ + Redir *rp; + for(rp=runq->redir;rp;rp=rp->next){ + switch(rp->type){ + case RCLOSE: + if(rp->from==fd) fd=-1; + break; + case RDUP: + case ROPEN: + if(rp->to==fd) fd=rp->from; + break; + } + } + return fd; +} diff --git a/utils/rcsh/syn.y b/utils/rcsh/syn.y new file mode 100644 index 00000000..b0201ef0 --- /dev/null +++ b/utils/rcsh/syn.y @@ -0,0 +1,90 @@ +%{ +#include "rc.h" +%} + +/* operator priorities -- lowest first */ +%left IF WHILE FOR SWITCH ')' NOT +%left ANDAND OROR +%left BANG SUBSHELL +%left PIPE +%left '^' +%right '$' COUNT '"' +%left SUB + +%term FOR IN WHILE IF NOT TWIDDLE BANG SUBSHELL SWITCH FN +%term WORD REDIR DUP PIPE SUB +%term SIMPLE ARGLIST WORDS BRACE PAREN PCMD PIPEFD /* not used in syntax */ + + +%union{ + Tree *tree; +}; + +%type<tree> line paren brace body cmdsa cmdsan assign epilog redir +%type<tree> cmd simple first word comword keyword words +%type<tree> NOT FOR IN WHILE IF TWIDDLE BANG SUBSHELL SWITCH FN +%type<tree> WORD REDIR DUP PIPE + +%% + +rc: { return 1;} +| line '\n' {return !compile($1);} +line: cmd +| cmdsa line {$$=tree2(';', $1, $2);} +body: cmd +| cmdsan body {$$=tree2(';', $1, $2);} +cmdsa: cmd ';' +| cmd '&' {$$=tree1('&', $1);} +cmdsan: cmdsa +| cmd '\n' +brace: '{' body '}' {$$=tree1(BRACE, $2);} +paren: '(' body ')' {$$=tree1(PCMD, $2);} +assign: first '=' word {$$=tree2('=', $1, $3);} +epilog: {$$=0;} +| redir epilog {$$=mung2($1, $1->child[0], $2);} +redir: REDIR word {$$=mung1($1, $1->rtype==HERE?heredoc($2):$2);} +| DUP +cmd: {$$=0;} +| brace epilog {$$=epimung($1, $2);} +| IF paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| IF NOT {skipnl();} cmd {$$=mung1($2, $4);} +| FOR '(' word IN words ')' {skipnl();} cmd + {$$=mung3($1, $3, tree1(PAREN, $5), $8);} +| FOR '(' word ')' {skipnl();} cmd + {$$=mung3($1, $3, 0, $6);} +| WHILE paren {skipnl();} cmd + {$$=mung2($1, $2, $4);} +| SWITCH word {skipnl();} brace + {$$=tree2(SWITCH, $2, $4);} +| simple {$$=simplemung($1);} +| TWIDDLE word words {$$=mung2($1, $2, $3);} +| cmd ANDAND cmd {$$=tree2(ANDAND, $1, $3);} +| cmd OROR cmd {$$=tree2(OROR, $1, $3);} +| cmd PIPE cmd {$$=mung2($2, $1, $3);} +| redir cmd %prec BANG {$$=mung2($1, $1->child[0], $2);} +| assign cmd %prec BANG {$$=mung3($1, $1->child[0], $1->child[1], $2);} +| BANG cmd {$$=mung1($1, $2);} +| SUBSHELL cmd {$$=mung1($1, $2);} +| FN words brace {$$=tree2(FN, $2, $3);} +| FN words {$$=tree1(FN, $2);} +simple: first +| simple word {$$=tree2(ARGLIST, $1, $2);} +| simple redir {$$=tree2(ARGLIST, $1, $2);} +first: comword +| first '^' word {$$=tree2('^', $1, $3);} +word: keyword {$1->type=WORD;} +| comword +| word '^' word {$$=tree2('^', $1, $3);} +comword: '$' word {$$=tree1('$', $2);} +| '$' word SUB words ')' {$$=tree2(SUB, $2, $4);} +| '"' word {$$=tree1('"', $2);} +| COUNT word {$$=tree1(COUNT, $2);} +| WORD +| '`' brace {$$=tree1('`', $2);} +| '(' words ')' {$$=tree1(PAREN, $2);} +/* | REDIR brace {$$=mung1($1, $2); $$->type=PIPEFD;} */ + +keyword: FOR|IN|WHILE|IF|NOT|TWIDDLE|BANG|SUBSHELL|SWITCH|FN +words: {$$=0;} +| words word {$$=tree2(WORDS, $1, $2);} diff --git a/utils/rcsh/trap.c b/utils/rcsh/trap.c new file mode 100644 index 00000000..7bae2cbc --- /dev/null +++ b/utils/rcsh/trap.c @@ -0,0 +1,42 @@ +#include "rc.h" + +int interrupted; +Ref ntrap; + +/* runs in a different thread */ +void +dointr(void) +{ + refinc(&ntrap); + interrupted = 1; +} + +void +dotrap(void) +{ + Var *trapreq; + Word *starval; + + while(refdec(&ntrap) >= 0) { + if(flag['S']) + exits(truestatus()?"":getstatus()); + starval=vlook("*")->val; + trapreq=vlook("sysint"); + if(trapreq->fn){ + start(trapreq->fn, trapreq->pc, (Var*)0); + runq->local=newvar(strdup("*"), runq->local); + runq->local->val=copywords(starval, (Word*)0); + runq->local->changed=1; + runq->redir=runq->startredir=0; + } else { + /* + * run the stack down until we uncover the + * command reading loop. Xreturn will exit + * if there is none (i.e. if this is not + * an interactive rc.) + */ + while(!runq->iflag) + Xreturn(); + } + } +} diff --git a/utils/rcsh/tree.c b/utils/rcsh/tree.c new file mode 100644 index 00000000..145f9c77 --- /dev/null +++ b/utils/rcsh/tree.c @@ -0,0 +1,140 @@ +#include "rc.h" +#include "y.tab.h" + +Tree *Treenodes; + +/* + * create and clear a new Tree node, and add it + * to the node list. + */ +Tree * +newtree(void) +{ + Tree *t=new(Tree); + t->iskw=0; + t->str=0; + t->child[0]=t->child[1]=t->child[2]=0; + t->next=Treenodes; + Treenodes=t; + return t; +} + +void +freenodes(void) +{ + Tree *t, *u; + for(t=Treenodes;t;t=u){ + u=t->next; + if(t->str) + free(t->str); + free(t); + } + Treenodes=0; +} + +Tree * +tree1(int type, Tree *c0) +{ + return tree3(type, c0, 0, 0); +} + +Tree * +tree2(int type, Tree *c0, Tree *c1) +{ + return tree3(type, c0, c1, 0); +} + +Tree * +tree3(int type, Tree *c0, Tree *c1, Tree *c2) +{ + Tree *t; + if(type==';'){ + if(c0==0) return c1; + if(c1==0) return c0; + } + t=newtree(); + t->type=type; + t->child[0]=c0; + t->child[1]=c1; + t->child[2]=c2; + return t; +} + +Tree * +mung1(Tree *t, Tree *c0) +{ + t->child[0]=c0; + return t; +} + +Tree * +mung2(Tree *t, Tree *c0, Tree *c1) +{ + t->child[0]=c0; + t->child[1]=c1; + return t; +} + +Tree * +mung3(Tree *t, Tree *c0, Tree *c1, Tree *c2) +{ + t->child[0]=c0; + t->child[1]=c1; + t->child[2]=c2; + return t; +} + +Tree * +epimung(Tree *comp, Tree *epi) +{ + Tree *p; + if(epi==0) return comp; + for(p=epi;p->child[1];p=p->child[1]); + p->child[1]=comp; + return epi; +} + +/* + * Add a SIMPLE node at the root of t and percolate all the redirections + * up to the root. + */ +Tree * +simplemung(Tree *t) +{ + Tree *u; + Io *s; + t=tree1(SIMPLE, t); + s=openstr(); + pfmt(s, "%t", t); + t->str=strdup(s->strp); + closeio(s); + for(u=t->child[0];u->type==ARGLIST;u=u->child[0]){ + if(u->child[1]->type==DUP + || u->child[1]->type==REDIR){ + u->child[1]->child[1]=t; + t=u->child[1]; + u->child[1]=0; + } + } + return t; +} + +Tree * +token(char *str, int type) +{ + Tree *t=newtree(); + t->type=type; + t->str=strdup(str); + return t; +} + +void +freetree(Tree *p) +{ + if(p==0) return; + freetree(p->child[0]); + freetree(p->child[1]); + freetree(p->child[2]); + if(p->str) free(p->str); + free((char *)p); +} diff --git a/utils/rcsh/var.c b/utils/rcsh/var.c new file mode 100644 index 00000000..cee8adf5 --- /dev/null +++ b/utils/rcsh/var.c @@ -0,0 +1,240 @@ +#include "rc.h" +#include "y.tab.h" + +extern char **_environ; + +typedef struct Kw Kw; + +#define NKW 30 +#define NVAR 521 + +struct Kw{ + char *name; + int type; + Kw *next; +}; + +void updenvlocal(Var *v); +void addenv(Var *v); + +Kw *kw[NKW]; +Var *gvar[NVAR]; + +int +hash(char *s, int n) +{ + int h=0, i=1; + + while(*s) + h+=*s++*i++; + h%=n; + return h<0?h+n:h; +} + +void +kenter(int type, char *name) +{ + int h=hash(name, NKW); + Kw *p=new(Kw); + p->type=type; + p->name=name; + p->next=kw[h]; + kw[h]=p; +} + +void +vinit(void) +{ + char **env, *name, *val, *p; + int i; + Word *w; + Io *f; + int n; + Var *v; + + env = _environ; + for(i=0; env[i]; free(name), i++) { + name = strdup(env[i]); + p = strchr(name, '='); + if(p == 0 || p == name) + continue; + *p = 0; + val = p+1; + n = strlen(val); + if(n == 0) + continue; + + if(strncmp(name, "fn#", 3)!=0) { + /* variable */ + w = 0; + p = val+n-1; + while(*p) { + if(*p == IWS) + *p-- = 0; + for(; *p && *p != IWS; p--) + ; + w=newword(p+1, w); + } + setvar(name, w); + vlook(name)->changed=0; + } else { + /* function */ + f = opencore(val, n); + execcmds(f); + } + } + v = vlook("path"); + p = getenv("path"); + if(v->val == 0 && p) + v->val = newword(p, 0); +} + + +Tree * +klook(char *name) +{ + Kw *p; + Tree *t=token(name, WORD); + for(p=kw[hash(name, NKW)];p;p=p->next) { + if(strcmp(p->name, name)==0){ + t->type=p->type; + t->iskw=1; + break; + } + } + return t; +} + +Var * +gvlook(char *name) +{ + int h=hash(name, NVAR); + Var *v; + for(v=gvar[h]; v; v=v->next) + if(strcmp(v->name, name)==0) + return v; + + return gvar[h]=newvar(strdup(name), gvar[h]); +} + +Var * +vlook(char *name) +{ + Var *v; + if(runq) + for(v=runq->local; v; v=v->next) + if(strcmp(v->name, name)==0) + return v; + return gvlook(name); +} + +void +setvar(char *name, Word *val) +{ + Var *v=vlook(name); + freewords(v->val); + v->val=val; + v->changed=1; +} + +Var * +newvar(char *name, Var *next) +{ + Var *v=new(Var); + v->name=name; + v->val=0; + v->fn=0; + v->changed=0; + v->fnchanged=0; + v->next=next; + return v; +} + + +void +execfinit(void) +{ +} + +void +updenv(void) +{ + Var *v, **h; + + for(h=gvar;h!=&gvar[NVAR];h++) + for(v=*h;v;v=v->next) + addenv(v); + + if(runq) + updenvlocal(runq->local); +} + +static void +envput(char *var, char *val) +{ + int i, n; + char *e; + char buf[256]; + + snprint(buf, sizeof(buf), "%s=%s", var, val); + n = strlen(var); + for(i = 0;;i++){ + e = environ[i]; + if(e == 0) + break; + if(strncmp(e, var, n) == 0){ + free(e); + environ[i] = strdup(buf); + return; + } + } + environ = realloc(environ, (i+2)*sizeof(char*)); + environ[i++] = strdup(buf); + environ[i] = 0; +} + +void +addenv(Var *v) +{ + char buf[100], *p; + Io *f; + Word *w; + int i, n; + + if(v->changed){ + v->changed=0; + p = 0; + n = 0; + if(v->val) { + for(w=v->val; w; w=w->next) { + i = strlen(w->word); + p = realloc(p, n+i+1); + memmove(p+n, w->word, i); + n+=i; + p[n++] = IWS; + } + p[n-1] = 0; + envput(v->name, p); + } else + envput(v->name, ""); + free(p); + } + + if(v->fnchanged){ + v->fnchanged=0; + snprint(buf, sizeof(buf), "fn#%s", v->name); + f = openstr(); + pfmt(f, "fn %s %s\n", v->name, v->fn[v->pc-1].s); + envput(buf, f->strp); + closeio(f); + } +} + +void +updenvlocal(Var *v) +{ + if(v){ + updenvlocal(v->next); + addenv(v); + } +} diff --git a/utils/rcsh/word.c b/utils/rcsh/word.c new file mode 100644 index 00000000..7cbab88e --- /dev/null +++ b/utils/rcsh/word.c @@ -0,0 +1,173 @@ +#include "rc.h" + +Word * +newword(char *wd, Word *next) +{ + Word *p=new(Word); + p->word=strdup(wd); + p->next=next; + return p; +} + +void +pushword(char *wd) +{ + if(runq->argv==0) + panic("pushword but no argv!", 0); + runq->argv->words=newword(wd, runq->argv->words); +} + +void +popword(void) +{ + Word *p; + + if(runq->argv==0) + panic("popword but no argv!", 0); + p=runq->argv->words; + if(p==0) + panic("popword but no word!", 0); + runq->argv->words=p->next; + free(p->word); + free(p); +} + +void +freewords(Word *w) +{ + Word *nw; + while(w){ + free(w->word); + nw=w->next; + free(w); + w=nw; + } +} + +void +freelist(Word *w) +{ + Word *nw; + while(w){ + nw=w->next; + free(w->word); + free(w); + w=nw; + } +} + +void +pushlist(void) +{ + List *p=new(List); + p->next=runq->argv; + p->words=0; + runq->argv=p; +} + +void +poplist(void) +{ + List *p=runq->argv; + if(p==0) + panic("poplist but no argv", 0); + freelist(p->words); + runq->argv=p->next; + free(p); +} + +int +count(Word *w) +{ + int n; + for(n=0;w;n++) + w=w->next; + return n; +} + +/* + * copy arglist a, adding the copy to the front of tail + */ +Word * +copywords(Word *a, Word *tail) +{ + Word *v=0, **end; + for(end=&v;a;a=a->next,end=&(*end)->next) + *end=newword(a->word, 0); + *end=tail; + return v; +} + +char * +list2str(Word *words) +{ + char *value, *s, *t; + int len=0; + Word *ap; + + for(ap=words;ap;ap=ap->next) + len+=1+strlen(ap->word); + value=malloc(len+1); + s=value; + for(ap=words;ap;ap=ap->next){ + for(t=ap->word;*t;) *s++=*t++; + *s++=' '; + } + if(s==value) + *s='\0'; + else + s[-1]='\0'; + return value; +} + +Word * +subwords(Word *val, int len, Word *sub, Word *a) +{ + int n; + char *s; + + if(!sub) return a; + a=subwords(val, len, sub->next, a); + s=sub->word; + deglob(s); + n=0; + while('0'<=*s && *s<='9') n=n*10+ *s++ -'0'; + if(n<1 || len<n) return a; + for(;n!=1;--n) val=val->next; + return newword(val->word, a); +} + + +void +pushredir(int type, int from, int to) +{ + Redir *rp=new(Redir); + rp->type=type; + rp->from=from; + rp->to=to; + rp->next=runq->redir; + runq->redir=rp; +} + +void +turfredir(void) +{ + while(runq->redir!=runq->startredir) + Xpopredir(); +} + +Word* +conclist(Word *lp, Word *rp, Word *tail) +{ + char *buf; + Word *v; + if(lp->next || rp->next) + tail=conclist(lp->next==0?lp:lp->next, rp->next==0?rp:rp->next, + tail); + buf=malloc(strlen(lp->word)+strlen(rp->word)+1); + strcpy(buf, lp->word); + strcat(buf, rp->word); + v=newword(buf, tail); + free(buf); + return v; +} |
