diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/cmd/rc/compiling.on.unix |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/cmd/rc/compiling.on.unix')
-rwxr-xr-x | sys/src/cmd/rc/compiling.on.unix | 1866 |
1 files changed, 1866 insertions, 0 deletions
diff --git a/sys/src/cmd/rc/compiling.on.unix b/sys/src/cmd/rc/compiling.on.unix new file mode 100755 index 000000000..cd20c56c7 --- /dev/null +++ b/sys/src/cmd/rc/compiling.on.unix @@ -0,0 +1,1866 @@ +From geoff@collyer.net Fri Dec 19 01:21:40 EST 2003 +Received: from plan9.cs.bell-labs.com ([135.104.9.2]) by plan9; Fri Dec 19 01:21:39 EST 2003 +Received: from collyer.net ([63.192.14.226]) by plan9; Fri Dec 19 01:21:35 EST 2003 +Message-ID: <c790d8b1e06b3918ad2c7848a3ae0ec7@collyer.net> +subject: rc on unix, part 1 +From: Geoff Collyer <geoff@collyer.net> +Date: Thu, 18 Dec 2003 22:21:33 -0800 +To: presotto@plan9.bell-labs.com, rsc@plan9.bell-labs.com, geoff@collyer.net +MIME-Version: 1.0 +Content-Type: text/plain; charset="US-ASCII" +Content-Transfer-Encoding: 7bit + +I got /sys/src/cmd/rc to compile under APE (in preparation for moving it +to Unixes) with the following changed files. I cadged some include files +from rsc but had to edit lib9.h slightly. I'll send the include files +separately. I can't tell if it works yet, but it does complain about +/usr/lib/rcmain being absent when I start it. Oh, and I haven't yet +simulated the effect of the OCEXEC bit. + + +# To unbundle, run this file +echo mkfile +sed 's/^X//' >mkfile <<'!' +X</$objtype/mkfile + +TARG=rc +COMMONOFILES=\ +X code.$O\ +X exec.$O\ +X getflags.$O\ +X glob.$O\ +X here.$O\ +X io.$O\ +X lex.$O\ +X pcmd.$O\ +X pfnc.$O\ +X simple.$O\ +X subr.$O\ +X trap.$O\ +X tree.$O\ +X var.$O\ +X havefork.$O\ + +PLAN9OFILES=plan9.$O\ + +UNIXOFILES=unix.$O\ + +OFILES=$COMMONOFILES $UNIXOFILES y.tab.$O + +HFILES=rc.h\ +X x.tab.h\ +X io.h\ +X exec.h\ +X fns.h\ + +YFILES=syn.y + +BIN=/$objtype/bin + +UPDATE=\ +X mkfile\ +X $HFILES\ +X ${COMMONOFILES:%.$O=%.c}\ +X ${UNIXOFILES:%.$O=%.c}\ +X ${PLAN9OFILES:%.$O=%.c}\ +X $YFILES\ +X ${TARG:%=/386/bin/%}\ + +CC=pcc -c -B -I../include +LD=pcc + +X</sys/src/cmd/mkone + +x.tab.h: y.tab.h +X cmp -s x.tab.h y.tab.h || cp y.tab.h x.tab.h + +clean:V: +X rm -f [$OS].out *.[$OS] [xy].tab.? y.debug $TARG + +regress: $O.out +X cd test +X mk + +unregress:V: +X for(test in test/*.test) rc $test >$test.out + +listing: +X pr mkfile $HFILES $FILES $FILES9 $FILESUNIX $YFILES|lp -du +! +echo simple.c +sed 's/^X//' >simple.c <<'!' +X/* +X * Maybe `simple' is a misnomer. +X */ +X#include "rc.h" +X#include "getflags.h" +X#include "exec.h" +X#include "io.h" +X#include "fns.h" +X/* +X * Search through the following code to see if we're just going to exit. +X */ +exitnext(void){ +X union code *c=&runq->code[runq->pc]; +X while(c->f==Xpopredir) c++; +X return c->f==Xexit; +X} + +void +XXsimple(void) +X{ +X word *a; +X thread *p = runq; +X var *v; +X struct builtin *bp; +X int pid; +X globlist(); +X a = runq->argv->words; +X if(a==0){ +X Xerror1("empty argument list"); +X return; +X } +X if(flag['x']) +X pfmt(err, "%v\n", p->argv->words); /* wrong, should do redirs */ +X v = gvlook(a->word); +X if(v->fn) +X execfunc(v); +X else{ +X if(strcmp(a->word, "builtin")==0){ +X if(count(a)==1){ +X pfmt(err, "builtin: empty argument list\n"); +X setstatus("empty arg list"); +X poplist(); +X return; +X } +X a = a->next; +X popword(); +X } +X for(bp = Builtin;bp->name;bp++) +X if(strcmp(a->word, bp->name)==0){ +X (*bp->fnc)(); +X return; +X } +X if(exitnext()){ +X /* fork and wait is redundant */ +X pushword("exec"); +X execexec(); +X Xexit(); +X } +X else{ +X flush(err); +X Updenv(); /* necessary so changes don't go out again */ +X if((pid = execforkexec()) < 0){ +X Xerror("try again"); +X return; +X } + +X /* interrupts don't get us out */ +X poplist(); +X while(Waitfor(pid, 1) < 0) +X ; +X } +X } +X} +struct word nullpath = { "", 0}; + +void +doredir(redir *rp) +X{ +X if(rp){ +X doredir(rp->next); +X switch(rp->type){ +X case ROPEN: +X if(rp->from!=rp->to){ +X Dup(rp->from, rp->to); +X close(rp->from); +X } +X break; +X case RDUP: +X Dup(rp->from, rp->to); +X break; +X case RCLOSE: +X close(rp->from); +X break; +X } +X } +X} + +word* +searchpath(char *w) +X{ +X word *path; +X if(strncmp(w, "/", 1)==0 +X || strncmp(w, "#", 1)==0 +X || strncmp(w, "./", 2)==0 +X || strncmp(w, "../", 3)==0 +X || (path = vlook("path")->val)==0) +X path=&nullpath; +X return path; +X} + +void +execexec(void) +X{ +X popword(); /* "exec" */ +X if(runq->argv->words==0){ +X Xerror1("empty argument list"); +X return; +X } +X doredir(runq->redir); +X Execute(runq->argv->words, searchpath(runq->argv->words->word)); +X poplist(); +X} + +void +execfunc(var *func) +X{ +X word *starval; +X popword(); +X starval = runq->argv->words; +X runq->argv->words = 0; +X poplist(); +X start(func->fn, func->pc, (struct var *)0); +X runq->local = newvar(strdup("*"), runq->local); +X runq->local->val = starval; +X runq->local->changed = 1; +X} + +int +dochdir(char *word) +X{ +X /* report to /dev/wdir if it exists and we're interactive */ +X static int wdirfd = -2; +X if(chdir(word)<0) return -1; +X if(flag['i']!=0){ +X if(wdirfd==-2) /* try only once */ +X /* TODO: arrange close-on-exec on Unix */ +X wdirfd = open("/dev/wdir", OWRITE|OCEXEC); +X if(wdirfd>=0) +X write(wdirfd, word, strlen(word)); +X } +X return 1; +X} + +void +execcd(void) +X{ +X word *a = runq->argv->words; +X word *cdpath; +X char dir[512]; +X setstatus("can't cd"); +X cdpath = vlook("cdpath")->val; +X switch(count(a)){ +X default: +X pfmt(err, "Usage: cd [directory]\n"); +X break; +X case 2: +X if(a->next->word[0]=='/' || cdpath==0) +X cdpath=&nullpath; +X for(;cdpath;cdpath = cdpath->next){ +X strcpy(dir, cdpath->word); +X if(dir[0]) +X strcat(dir, "/"); +X strcat(dir, a->next->word); +X if(dochdir(dir)>=0){ +X if(strlen(cdpath->word) +X && strcmp(cdpath->word, ".")!=0) +X pfmt(err, "%s\n", dir); +X setstatus(""); +X break; +X } +X } +X if(cdpath==0) +X pfmt(err, "Can't cd %s: %r\n", a->next->word); +X break; +X case 1: +X a = vlook("home")->val; +X if(count(a)>=1){ +X if(dochdir(a->word)>=0) +X setstatus(""); +X else +X pfmt(err, "Can't cd %s: %r\n", a->word); +X } +X else +X pfmt(err, "Can't cd -- $home empty\n"); +X break; +X } +X poplist(); +X} + +void +execexit(void) +X{ +X switch(count(runq->argv->words)){ +X default: +X pfmt(err, "Usage: exit [status]\nExiting anyway\n"); +X case 2: +X setstatus(runq->argv->words->next->word); +X case 1: Xexit(); +X } +X} + +void +execshift(void) +X{ +X int n; +X word *a; +X var *star; +X switch(count(runq->argv->words)){ +X default: +X pfmt(err, "Usage: shift [n]\n"); +X setstatus("shift usage"); +X poplist(); +X return; +X case 2: +X n = atoi(runq->argv->words->next->word); +X break; +X case 1: +X n = 1; +X break; +X } +X star = vlook("*"); +X for(;n && star->val;--n){ +X a = star->val->next; +X efree(star->val->word); +X efree((char *)star->val); +X star->val = a; +X star->changed = 1; +X } +X setstatus(""); +X poplist(); +X} + +int +octal(char *s) +X{ +X int n = 0; +X while(*s==' ' || *s=='\t' || *s=='\n') s++; +X while('0'<=*s && *s<='7') n = n*8+*s++-'0'; +X return n; +X} + +int +mapfd(int fd) +X{ +X redir *rp; +X for(rp = runq->redir;rp;rp = rp->next){ +X switch(rp->type){ +X case RCLOSE: +X if(rp->from==fd) +X fd=-1; +X break; +X case RDUP: +X case ROPEN: +X if(rp->to==fd) +X fd = rp->from; +X break; +X } +X } +X return fd; +X} +union code rdcmds[4]; + +void +execcmds(io *f) +X{ +X static int first = 1; +X if(first){ +X rdcmds[0].i = 1; +X rdcmds[1].f = Xrdcmds; +X rdcmds[2].f = Xreturn; +X first = 0; +X } +X start(rdcmds, 1, runq->local); +X runq->cmdfd = f; +X runq->iflast = 0; +X} + +void +execeval(void) +X{ +X char *cmdline, *s, *t; +X int len = 0; +X word *ap; +X if(count(runq->argv->words)<=1){ +X Xerror1("Usage: eval cmd ..."); +X return; +X } +X eflagok = 1; +X for(ap = runq->argv->words->next;ap;ap = ap->next) +X len+=1+strlen(ap->word); +X cmdline = emalloc(len); +X s = cmdline; +X for(ap = runq->argv->words->next;ap;ap = ap->next){ +X for(t = ap->word;*t;) *s++=*t++; +X *s++=' '; +X } +X s[-1]='\n'; +X poplist(); +X execcmds(opencore(cmdline, len)); +X efree(cmdline); +X} +union code dotcmds[14]; + +void +execdot(void) +X{ +X int iflag = 0; +X int fd; +X list *av; +X thread *p = runq; +X char *zero; +X static int first = 1; +X char file[512]; +X word *path; +X if(first){ +X dotcmds[0].i = 1; +X dotcmds[1].f = Xmark; +X dotcmds[2].f = Xword; +X dotcmds[3].s="0"; +X dotcmds[4].f = Xlocal; +X dotcmds[5].f = Xmark; +X dotcmds[6].f = Xword; +X dotcmds[7].s="*"; +X dotcmds[8].f = Xlocal; +X dotcmds[9].f = Xrdcmds; +X dotcmds[10].f = Xunlocal; +X dotcmds[11].f = Xunlocal; +X dotcmds[12].f = Xreturn; +X first = 0; +X } +X else +X eflagok = 1; +X popword(); +X if(p->argv->words && strcmp(p->argv->words->word, "-i")==0){ +X iflag = 1; +X popword(); +X } +X /* get input file */ +X if(p->argv->words==0){ +X Xerror1("Usage: . [-i] file [arg ...]"); +X return; +X } +X zero = strdup(p->argv->words->word); +X popword(); +X fd=-1; +X for(path = searchpath(zero);path;path = path->next){ +X strcpy(file, path->word); +X if(file[0]) +X strcat(file, "/"); +X strcat(file, zero); +X if((fd = open(file, 0))>=0) break; +X if(strcmp(file, "/dev/stdin")==0){ /* for sun & ucb */ +X fd = Dup1(0); +X if(fd>=0) +X break; +X } +X } +X if(fd<0){ +X pfmt(err, "%s: ", zero); +X setstatus("can't open"); +X Xerror(".: can't open"); +X return; +X } +X /* set up for a new command loop */ +X start(dotcmds, 1, (struct var *)0); +X pushredir(RCLOSE, fd, 0); +X runq->cmdfile = zero; +X runq->cmdfd = openfd(fd); +X runq->iflag = iflag; +X runq->iflast = 0; +X /* push $* value */ +X pushlist(); +X runq->argv->words = p->argv->words; +X /* free caller's copy of $* */ +X av = p->argv; +X p->argv = av->next; +X efree((char *)av); +X /* push $0 value */ +X pushlist(); +X pushword(zero); +X ndot++; +X} + +void +execflag(void) +X{ +X char *letter, *val; +X switch(count(runq->argv->words)){ +X case 2: +X setstatus(flag[runq->argv->words->next->word[0]]?"":"flag not set"); +X break; +X case 3: +X letter = runq->argv->words->next->word; +X val = runq->argv->words->next->next->word; +X if(strlen(letter)==1){ +X if(strcmp(val, "+")==0){ +X flag[letter[0]] = flagset; +X break; +X } +X if(strcmp(val, "-")==0){ +X flag[letter[0]] = 0; +X break; +X } +X } +X default: +X Xerror1("Usage: flag [letter] [+-]"); +X return; +X } +X poplist(); +X} + +void +execwhatis(void){ /* mildly wrong -- should fork before writing */ +X word *a, *b, *path; +X var *v; +X struct builtin *bp; +X char file[512]; +X struct io out[1]; +X int found, sep; +X a = runq->argv->words->next; +X if(a==0){ +X Xerror1("Usage: whatis name ..."); +X return; +X } +X setstatus(""); +X out->fd = mapfd(1); +X out->bufp = out->buf; +X out->ebuf = &out->buf[NBUF]; +X out->strp = 0; +X for(;a;a = a->next){ +X v = vlook(a->word); +X if(v->val){ +X pfmt(out, "%s=", a->word); +X if(v->val->next==0) +X pfmt(out, "%q\n", v->val->word); +X else{ +X sep='('; +X for(b = v->val;b && b->word;b = b->next){ +X pfmt(out, "%c%q", sep, b->word); +X sep=' '; +X } +X pfmt(out, ")\n"); +X } +X found = 1; +X } +X else +X found = 0; +X v = gvlook(a->word); +X if(v->fn) +X pfmt(out, "fn %s %s\n", v->name, v->fn[v->pc-1].s); +X else{ +X for(bp = Builtin;bp->name;bp++) +X if(strcmp(a->word, bp->name)==0){ +X pfmt(out, "builtin %s\n", a->word); +X break; +X } +X if(!bp->name){ +X for(path = searchpath(a->word);path;path = path->next){ +X strcpy(file, path->word); +X if(file[0]) +X strcat(file, "/"); +X strcat(file, a->word); +X if(Executable(file)){ +X pfmt(out, "%s\n", file); +X break; +X } +X } +X if(!path && !found){ +X pfmt(err, "%s: not found\n", a->word); +X setstatus("not found"); +X } +X } +X } +X } +X poplist(); +X flush(err); +X} + +void +execwait(void) +X{ +X switch(count(runq->argv->words)){ +X default: +X Xerror1("Usage: wait [pid]"); +X return; +X case 2: +X Waitfor(atoi(runq->argv->words->next->word), 0); +X break; +X case 1: +X Waitfor(-1, 0); +X break; +X } +X poplist(); +X} +! +echo havefork.c +sed 's/^X//' >havefork.c <<'!' +X#include "rc.h" +X#include "getflags.h" +X#include "exec.h" +X#include "io.h" +X#include "fns.h" + +int havefork = 1; + +void +XXasync(void) +X{ +X int null = open("/dev/null", 0); +X int pid; +X char npid[10]; +X if(null<0){ +X Xerror("Can't open /dev/null\n"); +X return; +X } +X#ifdef Unix +X pid = fork(); +X#else +X pid = rfork(RFFDG|RFPROC|RFNOTEG); +X#endif +X switch(pid){ +X case -1: +X close(null); +X Xerror("try again"); +X break; +X case 0: +X pushredir(ROPEN, null, 0); +X start(runq->code, runq->pc+1, runq->local); +X runq->ret = 0; +X break; +X default: +X close(null); +X runq->pc = runq->code[runq->pc].i; +X inttoascii(npid, pid); +X setvar("apid", newword(npid, (word *)0)); +X break; +X } +X} + +void +XXpipe(void) +X{ +X struct thread *p = runq; +X int pc = p->pc, forkid; +X int lfd = p->code[pc++].i; +X int rfd = p->code[pc++].i; +X int pfd[2]; +X if(pipe(pfd)<0){ +X Xerror("can't get pipe"); +X return; +X } +X switch(forkid = fork()){ +X case -1: +X Xerror("try again"); +X break; +X case 0: +X start(p->code, pc+2, runq->local); +X runq->ret = 0; +X close(pfd[PRD]); +X pushredir(ROPEN, pfd[PWR], lfd); +X break; +X default: +X start(p->code, p->code[pc].i, runq->local); +X close(pfd[PWR]); +X pushredir(ROPEN, pfd[PRD], rfd); +X p->pc = p->code[pc+1].i; +X p->pid = forkid; +X break; +X } +X} + +X/* +X * Who should wait for the exit from the fork? +X */ +void +XXbackq(void) +X{ +X char wd[8193]; +X int c; +X char *s, *ewd=&wd[8192], *stop; +X struct io *f; +X var *ifs = vlook("ifs"); +X word *v, *nextv; +X int pfd[2]; +X int pid; +X stop = ifs->val?ifs->val->word:""; +X if(pipe(pfd)<0){ +X Xerror("can't make pipe"); +X return; +X } +X switch(pid = fork()){ +X case -1: +X Xerror("try again"); +X close(pfd[PRD]); +X close(pfd[PWR]); +X return; +X case 0: +X close(pfd[PRD]); +X start(runq->code, runq->pc+1, runq->local); +X pushredir(ROPEN, pfd[PWR], 1); +X return; +X default: +X close(pfd[PWR]); +X f = openfd(pfd[PRD]); +X s = wd; +X v = 0; +X while((c = rchr(f))!=EOF){ +X if(strchr(stop, c) || s==ewd){ +X if(s!=wd){ +X *s='\0'; +X v = newword(wd, v); +X s = wd; +X } +X } +X else *s++=c; +X } +X if(s!=wd){ +X *s='\0'; +X v = newword(wd, v); +X } +X closeio(f); +X Waitfor(pid, 0); +X /* v points to reversed arglist -- reverse it onto argv */ +X while(v){ +X nextv = v->next; +X v->next = runq->argv->words; +X runq->argv->words = v; +X v = nextv; +X } +X runq->pc = runq->code[runq->pc].i; +X return; +X } +X} + +void +XXpipefd(void) +X{ +X struct thread *p = runq; +X int pc = p->pc; +X char name[40]; +X int pfd[2]; +X int sidefd, mainfd; +X if(pipe(pfd)<0){ +X Xerror("can't get pipe"); +X return; +X } +X if(p->code[pc].i==READ){ +X sidefd = pfd[PWR]; +X mainfd = pfd[PRD]; +X } +X else{ +X sidefd = pfd[PRD]; +X mainfd = pfd[PWR]; +X } +X switch(fork()){ +X case -1: +X Xerror("try again"); +X break; +X case 0: +X start(p->code, pc+2, runq->local); +X close(mainfd); +X pushredir(ROPEN, sidefd, p->code[pc].i==READ?1:0); +X runq->ret = 0; +X break; +X default: +X close(sidefd); +X pushredir(ROPEN, mainfd, mainfd); /* isn't this a noop? */ +X strcpy(name, Fdprefix); +X inttoascii(name+strlen(name), mainfd); +X pushword(name); +X p->pc = p->code[pc+1].i; +X break; +X } +X} + +void +XXsubshell(void) +X{ +X int pid; +X switch(pid = fork()){ +X case -1: +X Xerror("try again"); +X break; +X case 0: +X start(runq->code, runq->pc+1, runq->local); +X runq->ret = 0; +X break; +X default: +X Waitfor(pid, 1); +X runq->pc = runq->code[runq->pc].i; +X break; +X } +X} + +int +execforkexec(void) +X{ +X int pid; +X int n; +X char buf[ERRMAX]; + +X switch(pid = fork()){ +X case -1: +X return -1; +X case 0: +X pushword("exec"); +X execexec(); +X strcpy(buf, "can't exec: "); +X n = strlen(buf); +X errstr(buf+n, ERRMAX-n); +X Exit(buf); +X } +X return pid; +X} +! +echo rc.h +sed 's/^X//' >rc.h <<'!' +X/* +X * Plan9 is defined for plan 9 +X * V9 is defined for 9th edition +X * Sun is defined for sun-os +X * Please don't litter the code with ifdefs. The three below should be enough. +X */ +X#define Unix + +X#ifdef Plan9 +X#include <u.h> +X#include <libc.h> +X#define NSIG 32 +X#define SIGINT 2 +X#define SIGQUIT 3 +X#endif + +X#ifdef Unix +X#define _POSIX_SOURCE +X#define _BSD_EXTENSION + +X#include <stdlib.h> +X#include <stdarg.h> +X#include <string.h> +X#include <unistd.h> +X#include <fcntl.h> +X#include <lib9.h> +X#include <signal.h> +X#endif + +X#ifndef ERRMAX +X#define ERRMAX 128 +X#endif + +X#define YYMAXDEPTH 500 +X#ifndef PAREN +X#include "x.tab.h" +X#endif +typedef struct tree tree; +typedef struct word word; +typedef struct io io; +typedef union code code; +typedef struct var var; +typedef struct list list; +typedef struct redir redir; +typedef struct thread thread; +typedef struct builtin builtin; + +struct tree{ +X int type; +X int rtype, fd0, fd1; /* details of REDIR PIPE DUP tokens */ +X char *str; +X int quoted; +X int iskw; +X tree *child[3]; +X tree *next; +X}; +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*); +tree *cmdtree; +X/* +X * The first word of any code vector is a reference count. +X * Always create a new reference to a code vector by calling codecopy(.). +X * Always call codefree(.) when deleting a reference. +X */ +union code{ +X void (*f)(void); +X int i; +X char *s; +X}; +char *promptstr; +int doprompt; +X#define NTOK 8192 +char tok[NTOK]; +X#define APPEND 1 +X#define WRITE 2 +X#define READ 3 +X#define HERE 4 +X#define DUPFD 5 +X#define CLOSE 6 +struct var{ +X char *name; /* ascii name */ +X word *val; /* value */ +X int changed; +X code *fn; /* pointer to function's code vector */ +X int fnchanged; +X int pc; /* pc of start of function */ +X var *next; /* next on hash or local list */ +X}; +var *vlook(char*), *gvlook(char*), *newvar(char*, var*); +X#define NVAR 521 +var *gvar[NVAR]; /* hash for globals */ +X#define new(type) ((type *)emalloc(sizeof(type))) +char *emalloc(long); +void *Malloc(ulong); +void efree(char*); +X#define NOFILE 128 /* should come from <param.h> */ +struct here{ +X tree *tag; +X char *name; +X struct here *next; +X}; +int mypid; +X/* +X * Glob character escape in strings: +X * In a string, GLOB must be followed by *?[ or GLOB. +X * GLOB* matches any string +X * GLOB? matches any single character +X * GLOB[...] matches anything in the brackets +X * GLOBGLOB matches GLOB +X */ +X#define GLOB ((char)0x01) +X/* +X * onebyte(c), twobyte(c), threebyte(c) +X * Is c the first character of a one- two- or three-byte utf sequence? +X */ +X#define onebyte(c) ((c&0x80)==0x00) +X#define twobyte(c) ((c&0xe0)==0xc0) +X#define threebyte(c) ((c&0xf0)==0xe0) +char **argp; +char **args; +int nerror; /* number of errors encountered during compilation */ +int doprompt; /* is it time for a prompt? */ +X/* +X * Which fds are the reading/writing end of a pipe? +X * Unfortunately, this can vary from system to system. +X * 9th edition Unix doesn't care, the following defines +X * work on plan 9. +X */ +X#define PRD 0 +X#define PWR 1 +char Rcmain[], Fdprefix[]; +X#define register +X/* +X * How many dot commands have we executed? +X * Used to ensure that -v flag doesn't print rcmain. +X */ +int ndot; +char *getstatus(void); +int lastc; +int lastword; +! +echo unix.c +sed 's/^X//' >unix.c <<'!' +X/* +X * Unix versions of system-specific functions +X * By convention, exported routines herein have names beginning with an +X * upper case letter. +X */ +X#include "rc.h" +X#include "io.h" +X#include "exec.h" +X#include "getflags.h" +X#include <errno.h> + +char Rcmain[]="/usr/lib/rcmain"; +char Fdprefix[]="/dev/fd/"; + +void execfinit(void); + +struct builtin Builtin[] = { +X "cd", execcd, +X "whatis", execwhatis, +X "eval", execeval, +X "exec", execexec, /* but with popword first */ +X "exit", execexit, +X "shift", execshift, +X "wait", execwait, +X "umask", execumask, +X ".", execdot, +X "finit", execfinit, +X "flag", execflag, +X 0 +X}; +X#define SEP '\1' +char **environp; + +struct word* +enval(s) +register char *s; +X{ +X char *t, c; +X struct word *v; +X for(t = s;*t && *t!=SEP;t++); +X c=*t; +X *t='\0'; +X v = newword(s, c=='\0'?(struct word *)0:enval(t+1)); +X *t = c; +X return v; +X} + +void +Vinit(void) +X{ +X extern char **environ; +X char *s; +X char **env = environ; +X environp = env; +X for(;*env;env++){ +X for(s=*env;*s && *s!='(' && *s!='=';s++); +X switch(*s){ +X case '\0': +X pfmt(err, "environment %q?\n", *env); +X break; +X case '=': +X *s='\0'; +X setvar(*env, enval(s+1)); +X *s='='; +X break; +X case '(': /* ignore functions for now */ +X break; +X } +X } +X} + +char **envp; + +void +XXrdfn(void) +X{ +X char *s; +X int len; +X for(;*envp;envp++){ +X for(s=*envp;*s && *s!='(' && *s!='=';s++); +X switch(*s){ +X case '\0': +X pfmt(err, "environment %q?\n", *envp); +X break; +X case '=': /* ignore variables */ +X break; +X case '(': /* Bourne again */ +X s=*envp+3; +X envp++; +X len = strlen(s); +X s[len]='\n'; +X execcmds(opencore(s, len+1)); +X s[len]='\0'; +X return; +X } +X } +X Xreturn(); +X} + +union code rdfns[4]; + +void +execfinit(void) +X{ +X static int first = 1; +X if(first){ +X rdfns[0].i = 1; +X rdfns[1].f = Xrdfn; +X rdfns[2].f = Xjump; +X rdfns[3].i = 1; +X first = 0; +X } +X Xpopm(); +X envp = environp; +X start(rdfns, 1, runq->local); +X} + +int +cmpenv(const void *aa, const void *ab) +X{ +X char **a = aa, **b = ab; + +X return strcmp(*a, *b); +X} + +char ** +mkenv(void) +X{ +X char **env, **ep, *p, *q; +X struct var **h, *v; +X struct word *a; +X int nvar = 0, nchr = 0, sep; + +X /* +X * Slightly kludgy loops look at locals then globals. +X * locals no longer exist - geoff +X */ +X for(h = gvar-1; h != &gvar[NVAR]; h++) +X for(v = h >= gvar? *h: runq->local; v ;v = v->next){ +X if((v==vlook(v->name)) && v->val){ +X nvar++; +X nchr+=strlen(v->name)+1; +X for(a = v->val;a;a = a->next) +X nchr+=strlen(a->word)+1; +X } +X if(v->fn){ +X nvar++; +X nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8; +X } +X } +X env = (char **)emalloc((nvar+1)*sizeof(char *)+nchr); +X ep = env; +X p = (char *)&env[nvar+1]; +X for(h = gvar-1; h != &gvar[NVAR]; h++) +X for(v = h >= gvar? *h: runq->local;v;v = v->next){ +X if((v==vlook(v->name)) && v->val){ +X *ep++=p; +X q = v->name; +X while(*q) *p++=*q++; +X sep='='; +X for(a = v->val;a;a = a->next){ +X *p++=sep; +X sep = SEP; +X q = a->word; +X while(*q) *p++=*q++; +X } +X *p++='\0'; +X } +X if(v->fn){ +X *ep++=p; +X *p++='#'; *p++='('; *p++=')'; /* to fool Bourne */ +X *p++='f'; *p++='n'; *p++=' '; +X q = v->name; +X while(*q) *p++=*q++; +X *p++=' '; +X q = v->fn[v->pc-1].s; +X while(*q) *p++=*q++; +X *p++='\0'; +X } +X } +X *ep = 0; +X qsort((void *)env, nvar, sizeof ep[0], cmpenv); +X return env; +X} +char *sigmsg[] = { +X/* 0 normal */ 0, +X/* 1 SIGHUP */ "Hangup", +X/* 2 SIGINT */ 0, +X/* 3 SIGQUIT */ "Quit", +X/* 4 SIGILL */ "Illegal instruction", +X/* 5 SIGTRAP */ "Trace/BPT trap", +X/* 6 SIGIOT */ "abort", +X/* 7 SIGEMT */ "EMT trap", +X/* 8 SIGFPE */ "Floating exception", +X/* 9 SIGKILL */ "Killed", +X/* 10 SIGBUS */ "Bus error", +X/* 11 SIGSEGV */ "Memory fault", +X/* 12 SIGSYS */ "Bad system call", +X/* 13 SIGPIPE */ 0, +X/* 14 SIGALRM */ "Alarm call", +X/* 15 SIGTERM */ "Terminated", +X/* 16 unused */ "signal 16", +X/* 17 SIGSTOP */ "Process stopped", +X/* 18 unused */ "signal 18", +X/* 19 SIGCONT */ "Process continued", +X/* 20 SIGCHLD */ "Child death", +X}; + +void +Waitfor(int pid, int persist) +X{ +X int wpid, sig; +X struct thread *p; +X int wstat; +X char wstatstr[12]; + +X for(;;){ +X errno = 0; +X wpid = wait(&wstat); +X if(errno==EINTR && persist) +X continue; +X if(wpid==-1) +X break; +X sig = wstat&0177; +X if(sig==0177){ +X pfmt(err, "trace: "); +X sig = (wstat>>8)&0177; +X } +X if(sig>(sizeof sigmsg/sizeof sigmsg[0]) || sigmsg[sig]){ +X if(pid!=wpid) +X pfmt(err, "%d: ", wpid); +X if(sig<=(sizeof sigmsg/sizeof sigmsg[0])) +X pfmt(err, "%s", sigmsg[sig]); +X else if(sig==0177) pfmt(err, "stopped by ptrace"); +X else pfmt(err, "signal %d", sig); +X if(wstat&0200)pfmt(err, " -- core dumped"); +X pfmt(err, "\n"); +X } +X wstat = sig?sig+1000:(wstat>>8)&0xFF; +X if(wpid==pid){ +X inttoascii(wstatstr, wstat); +X setstatus(wstatstr); +X break; +X } +X else{ +X for(p = runq->ret;p;p = p->ret) +X if(p->pid==wpid){ +X p->pid=-1; +X inttoascii(p->status, wstat); +X break; +X } +X } +X } +X} + +char ** +mkargv(a) +register struct word *a; +X{ +X char **argv = (char **)emalloc((count(a)+2)*sizeof(char *)); +X char **argp = argv+1; /* leave one at front for runcoms */ + +X for(;a;a = a->next) +X *argp++=a->word; +X *argp = 0; +X return argv; +X} + +void +Updenv(void) +X{ +X} + +void +Execute(struct word *args, struct word *path) +X{ +X char *msg="not found"; +X#ifdef ETXTBSY +X int txtbusy = 0; +X#endif +X char **env = mkenv(); +X char **argv = mkargv(args); +X char file[512]; + +X for(;path;path = path->next){ +X strcpy(file, path->word); +X if(file[0]) +X strcat(file, "/"); +X strcat(file, argv[1]); +X#ifdef ETXTBSY +ReExec: +X#endif +X execve(file, argv+1, env); +X switch(errno){ +X case ENOEXEC: +X pfmt(err, "%s: Bourne again\n", argv[1]); +X argv[0]="sh"; +X argv[1] = strdup(file); +X execve("/bin/sh", argv, env); +X goto Bad; +X#ifdef ETXTBSY +X case ETXTBSY: +X if(++txtbusy!=5){ +X sleep(txtbusy); +X goto ReExec; +X } +X msg="text busy"; goto Bad; +X#endif +X case EACCES: +X msg="no access"; +X break; +X case ENOMEM: +X msg="not enough memory"; goto Bad; +X case E2BIG: +X msg="too big"; goto Bad; +X } +X } +Bad: +X pfmt(err, "%s: %s\n", argv[1], msg); +X efree((char *)env); +X efree((char *)argv); +X} + +X#define NDIR 14 /* should get this from param.h */ + +Globsize(p) +register char *p; +X{ +X int isglob = 0, globlen = NDIR+1; +X for(;*p;p++){ +X if(*p==GLOB){ +X p++; +X if(*p!=GLOB) +X isglob++; +X globlen+=*p=='*'?NDIR:1; +X } +X else +X globlen++; +X } +X return isglob?globlen:0; +X} + +X#include <sys/types.h> +X#include <dirent.h> + +X#define NDIRLIST 50 + +DIR *dirlist[NDIRLIST]; + +Opendir(name) +char *name; +X{ +X DIR **dp; +X for(dp = dirlist;dp!=&dirlist[NDIRLIST];dp++) +X if(*dp==0){ +X *dp = opendir(name); +X return *dp?dp-dirlist:-1; +X } +X return -1; +X} + +int +Readdir(int f, char *p, int /* onlydirs */ ) +X{ +X struct dirent *dp = readdir(dirlist[f]); + +X if(dp==0) +X return 0; +X strcpy(p, dp->d_name); +X return 1; +X} + +void +Closedir(int f) +X{ +X closedir(dirlist[f]); +X dirlist[f] = 0; +X} + +char *Signame[] = { +X "sigexit", "sighup", "sigint", "sigquit", +X "sigill", "sigtrap", "sigiot", "sigemt", +X "sigfpe", "sigkill", "sigbus", "sigsegv", +X "sigsys", "sigpipe", "sigalrm", "sigterm", +X "sig16", "sigstop", "sigtstp", "sigcont", +X "sigchld", "sigttin", "sigttou", "sigtint", +X "sigxcpu", "sigxfsz", "sig26", "sig27", +X "sig28", "sig29", "sig30", "sig31", +X 0, +X}; + +void +gettrap(int sig) +X{ +X signal(sig, gettrap); +X trap[sig]++; +X ntrap++; +X if(ntrap>=NSIG){ +X pfmt(err, "rc: Too many traps (trap %d), dumping core\n", sig); +X signal(SIGABRT, (void (*)())0); +X kill(getpid(), SIGABRT); +X } +X} + +void +Trapinit(void) +X{ +X int i; +X void (*sig)(); + +X if(1 || flag['d']){ /* wrong!!! */ +X sig = signal(SIGINT, gettrap); +X if(sig==SIG_IGN) +X signal(SIGINT, SIG_IGN); +X } +X else{ +X for(i = 1;i<=NSIG;i++) if(i!=SIGCHLD){ +X sig = signal(i, gettrap); +X if(sig==SIG_IGN) +X signal(i, SIG_IGN); +X } +X } +X} + +Unlink(name) +char *name; +X{ +X return unlink(name); +X} +Write(fd, buf, cnt) +char *buf; +X{ +X return write(fd, buf, cnt); +X} +Read(fd, buf, cnt) +char *buf; +X{ +X return read(fd, buf, cnt); +X} +Seek(fd, cnt, whence) +long cnt; +X{ +X return lseek(fd, cnt, whence); +X} +Executable(file) +char *file; +X{ +X return(access(file, 01)==0); +X} +Creat(file) +char *file; +X{ +X return creat(file, 0666); +X} +Dup(a, b){ +X return dup2(a, b); +X} +Dup1(a){ +X return dup(a); +X} +X/* +X * Wrong: should go through components of a|b|c and return the maximum. +X */ +void +Exit(char *stat) +X{ +X int n = 0; + +X while(*stat){ +X if(*stat!='|'){ +X if(*stat<'0' || '9'<*stat) +X exit(1); +X else n = n*10+*stat-'0'; +X } +X stat++; +X } +X exit(n); +X} +Eintr(){ +X return errno==EINTR; +X} + +void +Noerror() +X{ +X errno = 0; +X} +Isatty(fd){ +X return isatty(fd); +X} + +void +Abort() +X{ +X abort(); +X} + +void +execumask(void) /* wrong -- should fork before writing */ +X{ +X int m; +X struct io out[1]; +X switch(count(runq->argv->words)){ +X default: +X pfmt(err, "Usage: umask [umask]\n"); +X setstatus("umask usage"); +X poplist(); +X return; +X case 2: +X umask(octal(runq->argv->words->next->word)); +X break; +X case 1: +X umask(m = umask(0)); +X out->fd = mapfd(1); +X out->bufp = out->buf; +X out->ebuf=&out->buf[NBUF]; +X out->strp = 0; +X pfmt(out, "%o\n", m); +X break; +X } +X setstatus(""); +X poplist(); +X} + +void +Memcpy(a, b, n) +char *a, *b; +X{ +X memmove(a, b, n); +X} + +void* +Malloc(unsigned long n) +X{ +X return (void *)malloc(n); +X} + +void +errstr(char *buf, int len) +X{ +X strncpy(buf, strerror(errno), len); +X} +! + +From geoff@collyer.net Fri Dec 19 01:23:26 EST 2003 +Received: from plan9.cs.bell-labs.com ([135.104.9.2]) by plan9; Fri Dec 19 01:23:25 EST 2003 +Received: from collyer.net ([63.192.14.226]) by plan9; Fri Dec 19 01:23:22 EST 2003 +Message-ID: <0b5ea130198a21a49139759d00d69939@collyer.net> +subject: rc on unix, part 2 +From: Geoff Collyer <geoff@collyer.net> +Date: Thu, 18 Dec 2003 22:23:21 -0800 +To: presotto@plan9.bell-labs.com, rsc@plan9.bell-labs.com, geoff@collyer.net +MIME-Version: 1.0 +Content-Type: text/plain; charset="US-ASCII" +Content-Transfer-Encoding: 7bit + +These are the include files I used to emulate plan 9's include +files on Unix (APE). + + +# To unbundle, run this file +mkdir include +echo include/bio.h +sed 's/^X//' >include/bio.h <<'!' +X#ifndef _BIOH_ +X#define _BIOH_ 1 + +X#include <sys/types.h> /* for off_t */ +X#include <fcntl.h> /* for O_RDONLY, O_WRONLY */ + +typedef struct Biobuf Biobuf; + +enum +X{ +X Bsize = 8*1024, +X Bungetsize = 4, /* space for ungetc */ +X Bmagic = 0x314159, +X Beof = -1, +X Bbad = -2, + +X Binactive = 0, /* states */ +X Bractive, +X Bwactive, +X Bracteof, + +X Bend +X}; + +struct Biobuf +X{ +X int icount; /* neg num of bytes at eob */ +X int ocount; /* num of bytes at bob */ +X int rdline; /* num of bytes after rdline */ +X int runesize; /* num of bytes of last getrune */ +X int state; /* r/w/inactive */ +X int fid; /* open file */ +X int flag; /* magic if malloc'ed */ +X off_t offset; /* offset of buffer in file */ +X int bsize; /* size of buffer */ +X unsigned char* bbuf; /* pointer to beginning of buffer */ +X unsigned char* ebuf; /* pointer to end of buffer */ +X unsigned char* gbuf; /* pointer to good data in buf */ +X unsigned char b[Bungetsize+Bsize]; +X}; + +X#define BGETC(bp)\ +X ((bp)->icount?(bp)->bbuf[(bp)->bsize+(bp)->icount++]:Bgetc((bp))) +X#define BPUTC(bp,c)\ +X ((bp)->ocount?(bp)->bbuf[(bp)->bsize+(bp)->ocount++]=(c),0:Bputc((bp),(c))) +X#define BOFFSET(bp)\ +X (((bp)->state==Bractive)?\ +X (bp)->offset + (bp)->icount:\ +X (((bp)->state==Bwactive)?\ +X (bp)->offset + ((bp)->bsize + (bp)->ocount):\ +X -1)) +X#define BLINELEN(bp)\ +X (bp)->rdline +X#define BFILDES(bp)\ +X (bp)->fid + +int Bbuffered(Biobuf*); +int Bfildes(Biobuf*); +int Bflush(Biobuf*); +int Bgetc(Biobuf*); +int Bgetd(Biobuf*, double*); +int Binit(Biobuf*, int, int); +int Binits(Biobuf*, int, int, unsigned char*, int); +int Blinelen(Biobuf*); +off_t Boffset(Biobuf*); +Biobuf* Bopen(char*, int); +int Bprint(Biobuf*, char*, ...); +int Bputc(Biobuf*, int); +void* Brdline(Biobuf*, int); +long Bread(Biobuf*, void*, long); +off_t Bseek(Biobuf*, off_t, int); +int Bterm(Biobuf*); +int Bungetc(Biobuf*); +long Bwrite(Biobuf*, void*, long); + +long Bgetrune(Biobuf*); +int Bputrune(Biobuf*, long); +int Bungetrune(Biobuf*); + +X#endif +! +echo include/fmt.h +sed 's/^X//' >include/fmt.h <<'!' + +X/* +X * The authors of this software are Rob Pike and Ken Thompson. +X * Copyright (c) 2002 by Lucent Technologies. +X * Permission to use, copy, modify, and distribute this software for any +X * purpose without fee is hereby granted, provided that this entire notice +X * is included in all copies of any software which is or includes a copy +X * or modification of this software and in all copies of the supporting +X * documentation for such software. +X * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +X * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY +X * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY +X * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +X */ + +X#ifndef _FMTH_ +X#define _FMTH_ 1 + +X#include <stdarg.h> + +X#ifndef _UTFH_ +X#include <utf.h> +X#endif + +typedef struct Fmt Fmt; +struct Fmt{ +X unsigned char runes; /* output buffer is runes or chars? */ +X void *start; /* of buffer */ +X void *to; /* current place in the buffer */ +X void *stop; /* end of the buffer; overwritten if flush fails */ +X int (*flush)(Fmt *); /* called when to == stop */ +X void *farg; /* to make flush a closure */ +X int nfmt; /* num chars formatted so far */ +X va_list args; /* args passed to dofmt */ +X int r; /* % format Rune */ +X int width; +X int prec; +X unsigned long flags; +X}; + +enum{ +X FmtWidth = 1, +X FmtLeft = FmtWidth << 1, +X FmtPrec = FmtLeft << 1, +X FmtSharp = FmtPrec << 1, +X FmtSpace = FmtSharp << 1, +X FmtSign = FmtSpace << 1, +X FmtZero = FmtSign << 1, +X FmtUnsigned = FmtZero << 1, +X FmtShort = FmtUnsigned << 1, +X FmtLong = FmtShort << 1, +X FmtVLong = FmtLong << 1, +X FmtComma = FmtVLong << 1, +X FmtByte = FmtComma << 1, +X FmtLDouble = FmtByte << 1, + +X FmtFlag = FmtLDouble << 1 +X}; + +extern int print(char*, ...); +extern char* seprint(char*, char*, char*, ...); +extern char* vseprint(char*, char*, char*, va_list); +extern int snprint(char*, int, char*, ...); +extern int vsnprint(char*, int, char*, va_list); +extern char* smprint(char*, ...); +extern char* vsmprint(char*, va_list); +extern int sprint(char*, char*, ...); +extern int fprint(int, char*, ...); +extern int vfprint(int, char*, va_list); + +extern int runesprint(Rune*, char*, ...); +extern int runesnprint(Rune*, int, char*, ...); +extern int runevsnprint(Rune*, int, char*, va_list); +extern Rune* runeseprint(Rune*, Rune*, char*, ...); +extern Rune* runevseprint(Rune*, Rune*, char*, va_list); +extern Rune* runesmprint(char*, ...); +extern Rune* runevsmprint(char*, va_list); + +extern int fmtfdinit(Fmt*, int, char*, int); +extern int fmtfdflush(Fmt*); +extern int fmtstrinit(Fmt*); +extern char* fmtstrflush(Fmt*); + +extern int quotestrfmt(Fmt *f); +extern void quotefmtinstall(void); +extern int (*fmtdoquote)(int); + + +extern int fmtinstall(int, int (*)(Fmt*)); +extern int dofmt(Fmt*, char*); +extern int fmtprint(Fmt*, char*, ...); +extern int fmtvprint(Fmt*, char*, va_list); +extern int fmtrune(Fmt*, int); +extern int fmtstrcpy(Fmt*, char*); + +extern double fmtstrtod(const char *, char **); +extern double fmtcharstod(int(*)(void*), void*); + +X#endif +! +echo include/lib9.h +sed 's/^X//' >include/lib9.h <<'!' +X#include <string.h> +X#include "utf.h" + +X#define nil ((void*)0) + +X#define uchar _fmtuchar +X#define ushort _fmtushort +X#define uint _fmtuint +X#define ulong _fmtulong +X#define vlong _fmtvlong +X#define uvlong _fmtuvlong + +typedef unsigned char uchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +X#define OREAD O_RDONLY +X#define OWRITE O_WRONLY +X#define ORDWR O_RDWR +X#define OCEXEC 0 +! +echo include/regexp9.h +sed 's/^X//' >include/regexp9.h <<'!' +X#ifndef _REGEXP9H_ + +X#define _REGEXP9H_ 1 +X#include <utf.h> + +typedef struct Resub Resub; +typedef struct Reclass Reclass; +typedef struct Reinst Reinst; +typedef struct Reprog Reprog; + +X/* +X * Sub expression matches +X */ +struct Resub{ +X union +X { +X char *sp; +X Rune *rsp; +X }s; +X union +X { +X char *ep; +X Rune *rep; +X }e; +X}; + +X/* +X * character class, each pair of rune's defines a range +X */ +struct Reclass{ +X Rune *end; +X Rune spans[64]; +X}; + +X/* +X * Machine instructions +X */ +struct Reinst{ +X int type; +X union { +X Reclass *cp; /* class pointer */ +X Rune r; /* character */ +X int subid; /* sub-expression id for RBRA and LBRA */ +X Reinst *right; /* right child of OR */ +X }u1; +X union { /* regexp relies on these two being in the same union */ +X Reinst *left; /* left child of OR */ +X Reinst *next; /* next instruction for CAT & LBRA */ +X }u2; +X}; + +X/* +X * Reprogram definition +X */ +struct Reprog{ +X Reinst *startinst; /* start pc */ +X Reclass class[16]; /* .data */ +X Reinst firstinst[5]; /* .text */ +X}; + +extern Reprog *regcomp(char*); +extern Reprog *regcomplit(char*); +extern Reprog *regcompnl(char*); +extern void regerror(char*); +extern int regexec(Reprog*, char*, Resub*, int); +extern void regsub(char*, char*, int, Resub*, int); + +extern int rregexec(Reprog*, Rune*, Resub*, int); +extern void rregsub(Rune*, Rune*, Resub*, int); + +X#endif +! +echo include/utf.h +sed 's/^X//' >include/utf.h <<'!' +X#ifndef _UTFH_ +X#define _UTFH_ 1 + +typedef unsigned short Rune; /* 16 bits */ + +enum +X{ +X UTFmax = 3, /* maximum bytes per rune */ +X Runesync = 0x80, /* cannot represent part of a UTF sequence (<) */ +X Runeself = 0x80, /* rune and UTF sequences are the same (<) */ +X Runeerror = 0x80, /* decoding error in UTF */ +X}; + +X/* +X * rune routines +X */ +extern int runetochar(char*, Rune*); +extern int chartorune(Rune*, char*); +extern int runelen(long); +extern int runenlen(Rune*, int); +extern int fullrune(char*, int); +extern int utflen(char*); +extern int utfnlen(char*, long); +extern char* utfrune(char*, long); +extern char* utfrrune(char*, long); +extern char* utfutf(char*, char*); +extern char* utfecpy(char*, char*, char*); + +extern Rune* runestrcat(Rune*, Rune*); +extern Rune* runestrchr(Rune*, Rune); +extern int runestrcmp(Rune*, Rune*); +extern Rune* runestrcpy(Rune*, Rune*); +extern Rune* runestrncpy(Rune*, Rune*, long); +extern Rune* runestrecpy(Rune*, Rune*, Rune*); +extern Rune* runestrdup(Rune*); +extern Rune* runestrncat(Rune*, Rune*, long); +extern int runestrncmp(Rune*, Rune*, long); +extern Rune* runestrrchr(Rune*, Rune); +extern long runestrlen(Rune*); +extern Rune* runestrstr(Rune*, Rune*); + +extern Rune tolowerrune(Rune); +extern Rune totitlerune(Rune); +extern Rune toupperrune(Rune); +extern int isalpharune(Rune); +extern int islowerrune(Rune); +extern int isspacerune(Rune); +extern int istitlerune(Rune); +extern int isupperrune(Rune); + +X#endif +! + |