Commit Diff


commit - a27e20579cb03a28bd1e1b88e49f80db76e51d68
commit + 70bcc7804e3cac85bd92306668bae053df86a29e
blob - /dev/null
blob + cd538faba70c07b6624d7261d5764c65e27e7018 (mode 644)
--- /dev/null
+++ include/ar.h
@@ -0,0 +1,17 @@
+#define	ARMAG	"!<arch>\n"
+#define	SARMAG	8
+
+#define	ARFMAG	"`\n"
+#define SARNAME	16
+
+struct	ar_hdr
+{
+	char	name[SARNAME];
+	char	date[12];
+	char	uid[6];
+	char	gid[6];
+	char	mode[8];
+	char	size[10];
+	char	fmag[2];
+};
+#define	SAR_HDR	(SARNAME+44)
blob - /dev/null
blob + 49e78b895ccbc29c52e321777c398692ab1eb648 (mode 644)
--- /dev/null
+++ src/cmd/9sed.c
@@ -0,0 +1,1447 @@
+/*
+ * sed -- stream  editor
+ *
+ *
+ */
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <regexp.h>
+
+enum {
+	DEPTH		= 20,		/* max nesting depth of {} */
+	MAXCMDS		= 512,		/* max sed commands */
+	ADDSIZE		= 10000,	/* size of add & read buffer */
+	MAXADDS		= 20,		/* max pending adds and reads */
+	LBSIZE		= 8192,		/* input line size */
+	LABSIZE		= 50,		/* max label name size */
+	MAXSUB		= 10,		/* max number of sub reg exp */
+	MAXFILES	= 120,		/* max output files */
+};
+	/* An address is a line #, a R.E., "$", a reference to the last
+	 * R.E., or nothing.
+	 */
+typedef struct	{
+	enum {
+		A_NONE,
+		A_DOL,
+		A_LINE,
+		A_RE,
+		A_LAST,
+	}type;
+	union {
+		long line;		/* Line # */
+		Reprog *rp;		/* Compiled R.E. */
+	};
+} Addr;
+
+typedef struct	SEDCOM {
+	Addr	ad1;			/* optional start address */
+	Addr	ad2;			/* optional end address */
+	union {
+		Reprog	*re1;		/* compiled R.E. */
+		Rune	*text;		/* added text or file name */
+		struct	SEDCOM	*lb1;	/* destination command of branch */
+	};
+	Rune	*rhs;			/* Right-hand side of substitution */
+	Biobuf*	fcode;			/* File ID for read and write */
+	char	command;		/* command code -see below */
+	char	gfl;			/* 'Global' flag for substitutions */
+	char	pfl;			/* 'print' flag for substitutions */
+	char	active;			/* 1 => data between start and end */
+	char	negfl;			/* negation flag */
+} SedCom;
+
+	/* Command Codes for field SedCom.command */
+#define ACOM	01
+#define BCOM	020
+#define CCOM	02
+#define	CDCOM	025
+#define	CNCOM	022
+#define COCOM	017
+#define	CPCOM	023
+#define DCOM	03
+#define ECOM	015
+#define EQCOM	013
+#define FCOM	016
+#define GCOM	027
+#define CGCOM	030
+#define HCOM	031
+#define CHCOM	032
+#define ICOM	04
+#define LCOM	05
+#define NCOM	012
+#define PCOM	010
+#define QCOM	011
+#define RCOM	06
+#define SCOM	07
+#define TCOM	021
+#define WCOM	014
+#define	CWCOM	024
+#define	YCOM	026
+#define XCOM	033
+
+	
+typedef struct label {			/* Label symbol table */
+	Rune	asc[9];			/* Label name */
+	SedCom	*chain;
+	SedCom	*address;		/* Command associated with label */
+} Label;
+
+typedef	struct	FILE_CACHE {		/* Data file control block */
+	struct FILE_CACHE *next;	/* Forward Link */
+	char 		  *name;	/* Name of file */
+} FileCache;
+
+SedCom pspace[MAXCMDS];			/* Command storage */
+SedCom *pend = pspace+MAXCMDS;		/* End of command storage */
+SedCom *rep = pspace;			/* Current fill point */
+
+Reprog	*lastre = 0;			/* Last regular expression */
+Resub	subexp[MAXSUB];			/* sub-patterns of pattern match*/
+
+Rune	addspace[ADDSIZE];		/* Buffer for a, c, & i commands */
+Rune	*addend = addspace+ADDSIZE;
+
+SedCom	*abuf[MAXADDS];			/* Queue of pending adds & reads */
+SedCom	**aptr = abuf;
+
+struct {				/* Sed program input control block */
+	enum PTYPE 			/* Either on command line or in file */
+		{ P_ARG,
+		  P_FILE
+		} type;
+	union PCTL {			/* Pointer to data */
+		Biobuf	*bp;
+		char	*curr;
+	} pctl;
+} prog;
+
+Rune	genbuf[LBSIZE];			/* Miscellaneous buffer */
+
+FileCache	*fhead = 0;		/* Head of File Cache Chain */
+FileCache	*ftail = 0;		/* Tail of File Cache Chain */
+
+Rune	*loc1;				/* Start of pattern match */
+Rune	*loc2;				/* End of pattern match */
+Rune	seof;				/* Pattern delimiter char */
+
+Rune	linebuf[LBSIZE+1];		/* Input data buffer */
+Rune	*lbend = linebuf+LBSIZE;	/* End of buffer */
+Rune	*spend = linebuf;			/* End of input data */
+Rune	*cp;				/* Current scan point in linebuf */
+
+Rune	holdsp[LBSIZE+1];		/* Hold buffer */
+Rune	*hend = holdsp+LBSIZE;		/* End of hold buffer */
+Rune	*hspend = holdsp;		/* End of hold data */
+
+int	nflag;				/* Command line flags */
+int	gflag;
+
+int	dolflag;			/* Set when at true EOF */
+int	sflag;				/* Set when substitution done */
+int	jflag;				/* Set when jump required */
+int	delflag;			/* Delete current line when set */
+
+long	lnum = 0;			/* Input line count */
+
+char	fname[MAXFILES][40];		/* File name cache */
+Biobuf	*fcode[MAXFILES];		/* File ID cache */
+int	nfiles = 0;			/* Cache fill point */
+
+Biobuf	fout;				/* Output stream */
+Biobuf	bstdin;				/* Default input */
+Biobuf*	f = 0;				/* Input data */
+
+Label	ltab[LABSIZE];			/* Label name symbol table */
+Label	*labend = ltab+LABSIZE;		/* End of label table */
+Label	*lab = ltab+1;			/* Current Fill point */
+
+int	depth = 0;			/* {} stack pointer */
+
+Rune	bad;				/* Dummy err ptr reference */
+Rune	*badp = &bad;
+
+
+char	CGMES[]	 = 	"Command garbled: %S";
+char	TMMES[]	 = 	"Too much text: %S";
+char	LTL[]	 = 	"Label too long: %S";
+char	AD0MES[] =	"No addresses allowed: %S";
+char	AD1MES[] =	"Only one address allowed: %S";
+
+void	address(Addr *);
+void	arout(void);
+int	cmp(char *, char *);
+int	rcmp(Rune *, Rune *);
+void	command(SedCom *);
+Reprog	*compile(void);
+Rune	*compsub(Rune *, Rune *);
+void	dechain(void);
+void	dosub(Rune *);
+int	ecmp(Rune *, Rune *, int);
+void	enroll(char *);
+void	errexit(void);
+int	executable(SedCom *);
+void	execute(void);
+void	fcomp(void);
+long	getrune(void);
+Rune	*gline(Rune *);
+int	match(Reprog *, Rune *);
+void	newfile(enum PTYPE, char *);
+int 	opendata(void);
+Biobuf	*open_file(char *);
+Rune	*place(Rune *, Rune *, Rune *);
+void	quit(char *, char *);
+int	rline(Rune *, Rune *);
+Label	*search(Label *);
+int	substitute(SedCom *);
+char	*text(char *);
+Rune	*stext(Rune *, Rune *);
+int	ycomp(SedCom *);
+char *	trans(int c);
+void	putline(Biobuf *bp, Rune *buf, int n);
+
+void
+main(int argc, char **argv)
+{
+	int	compfl;
+
+	lnum = 0;
+	Binit(&fout, 1, OWRITE);
+	fcode[nfiles++] = &fout;
+	compfl = 0;
+
+	if(argc == 1)
+		exits(0);
+	ARGBEGIN{
+		case 'n':
+			nflag++;
+			continue;
+		case 'f':
+			if(argc <= 1)
+				quit("no pattern-file", 0);
+			newfile(P_FILE, ARGF());
+			fcomp();
+			compfl = 1;
+			continue;
+		case 'e':
+			if (argc <= 1)
+				quit("missing pattern", 0);
+			newfile(P_ARG, ARGF());
+			fcomp();
+			compfl = 1;
+			continue;
+		case 'g':
+			gflag++;
+			continue;
+		default:
+			fprint(2, "sed: Unknown flag: %c\n", ARGC());
+			continue;
+	} ARGEND
+
+	if(compfl == 0) {
+		if (--argc < 0)
+			quit("missing pattern", 0);
+		newfile(P_ARG, *argv++);
+		fcomp();
+	}
+
+	if(depth)
+		quit("Too many {'s", 0);
+
+	ltab[0].address = rep;
+
+	dechain();
+
+	if(argc <= 0)
+		enroll(0);		/* Add stdin to cache */
+	else while(--argc >= 0) {
+		enroll(*argv++);
+	}
+	execute();
+	exits(0);
+}
+void
+fcomp(void)
+{
+	Rune	*tp;
+	SedCom	*pt, *pt1;
+	int	i;
+	Label	*lpt;
+
+	static Rune	*p = addspace;
+	static SedCom	**cmpend[DEPTH];	/* stack of {} operations */
+
+	while (rline(linebuf, lbend) >= 0) {
+		cp = linebuf;
+comploop:
+		while(*cp == ' ' || *cp == '\t')
+			cp++;
+		if(*cp == '\0' || *cp == '#')
+			continue;
+		if(*cp == ';') {
+			cp++;
+			goto comploop;
+		}
+
+		address(&rep->ad1);
+		if (rep->ad1.type != A_NONE) {
+			if (rep->ad1.type == A_LAST) {
+				if (!lastre)
+					quit("First RE may not be null", 0);
+				rep->ad1.type = A_RE;
+				rep->ad1.rp = lastre;
+			}
+			if(*cp == ',' || *cp == ';') {
+				cp++;
+				address(&rep->ad2);
+				if (rep->ad2.type == A_LAST) {
+					rep->ad1.type = A_RE;
+					rep->ad2.rp = lastre;
+				}
+			} else
+				rep->ad2.type = A_NONE;
+		}
+		while(*cp == ' ' || *cp == '\t')
+			cp++;
+
+swit:
+		switch(*cp++) {
+
+			default:
+				quit("Unrecognized command: %S", (char *)linebuf);
+
+			case '!':
+				rep->negfl = 1;
+				goto swit;
+
+			case '{':
+				rep->command = BCOM;
+				rep->negfl = !(rep->negfl);
+				cmpend[depth++] = &rep->lb1;
+				if(++rep >= pend)
+					quit("Too many commands: %S", (char *) linebuf);
+				if(*cp == '\0')	continue;
+				goto comploop;
+
+			case '}':
+				if(rep->ad1.type != A_NONE)
+					quit(AD0MES, (char *) linebuf);
+				if(--depth < 0)
+					quit("Too many }'s", 0);
+				*cmpend[depth] = rep;
+				if(*cp == 0)	continue;
+				goto comploop;
+
+			case '=':
+				rep->command = EQCOM;
+				if(rep->ad2.type != A_NONE)
+					quit(AD1MES, (char *) linebuf);
+				break;
+
+			case ':':
+				if(rep->ad1.type != A_NONE)
+					quit(AD0MES, (char *) linebuf);
+
+				while(*cp == ' ')
+					cp++;
+				tp = lab->asc;
+				while (*cp && *cp != ';' && *cp != ' ' && *cp != '\t' && *cp != '#') {
+					*tp++ = *cp++;
+					if(tp >= &(lab->asc[8]))
+						quit(LTL, (char *) linebuf);
+				}
+				*tp = '\0';
+
+				if(lpt = search(lab)) {
+					if(lpt->address)
+						quit("Duplicate labels: %S", (char *) linebuf);
+				} else {
+					lab->chain = 0;
+					lpt = lab;
+					if(++lab >= labend)
+						quit("Too many labels: %S", (char *) linebuf);
+				}
+				lpt->address = rep;
+				if (*cp == '#')
+					continue;
+				rep--;			/* reuse this slot */
+				break;
+
+			case 'a':
+				rep->command = ACOM;
+				if(rep->ad2.type != A_NONE)
+					quit(AD1MES, (char *) linebuf);
+				if(*cp == '\\')	cp++;
+				if(*cp++ != '\n')
+					quit(CGMES, (char *) linebuf);
+				rep->text = p;
+				p = stext(p, addend);
+				break;
+			case 'c':
+				rep->command = CCOM;
+				if(*cp == '\\')	cp++;
+				if(*cp++ != '\n')
+					quit(CGMES, (char *) linebuf);
+				rep->text = p;
+				p = stext(p, addend);
+				break;
+			case 'i':
+				rep->command = ICOM;
+				if(rep->ad2.type != A_NONE)
+					quit(AD1MES, (char *) linebuf);
+				if(*cp == '\\')	cp++;
+				if(*cp++ != '\n')
+					quit(CGMES, (char *) linebuf);
+				rep->text = p;
+				p = stext(p, addend);
+				break;
+
+			case 'g':
+				rep->command = GCOM;
+				break;
+
+			case 'G':
+				rep->command = CGCOM;
+				break;
+
+			case 'h':
+				rep->command = HCOM;
+				break;
+
+			case 'H':
+				rep->command = CHCOM;
+				break;
+
+			case 't':
+				rep->command = TCOM;
+				goto jtcommon;
+
+			case 'b':
+				rep->command = BCOM;
+jtcommon:
+				while(*cp == ' ')cp++;
+				if(*cp == '\0') {
+					if(pt = ltab[0].chain) {
+						while(pt1 = pt->lb1)
+							pt = pt1;
+						pt->lb1 = rep;
+					} else
+						ltab[0].chain = rep;
+					break;
+				}
+				tp = lab->asc;
+				while((*tp++ = *cp++))
+					if(tp >= &(lab->asc[8]))
+						quit(LTL, (char *) linebuf);
+				cp--;
+				tp[-1] = '\0';
+
+				if(lpt = search(lab)) {
+					if(lpt->address) {
+						rep->lb1 = lpt->address;
+					} else {
+						pt = lpt->chain;
+						while(pt1 = pt->lb1)
+							pt = pt1;
+						pt->lb1 = rep;
+					}
+				} else {
+					lab->chain = rep;
+					lab->address = 0;
+					if(++lab >= labend)
+						quit("Too many labels: %S",
+							(char *) linebuf);
+				}
+				break;
+
+			case 'n':
+				rep->command = NCOM;
+				break;
+
+			case 'N':
+				rep->command = CNCOM;
+				break;
+
+			case 'p':
+				rep->command = PCOM;
+				break;
+
+			case 'P':
+				rep->command = CPCOM;
+				break;
+
+			case 'r':
+				rep->command = RCOM;
+				if(rep->ad2.type != A_NONE)
+					quit(AD1MES, (char *) linebuf);
+				if(*cp++ != ' ')
+					quit(CGMES, (char *) linebuf);
+				rep->text = p;
+				p = stext(p, addend);
+				break;
+
+			case 'd':
+				rep->command = DCOM;
+				break;
+
+			case 'D':
+				rep->command = CDCOM;
+				rep->lb1 = pspace;
+				break;
+
+			case 'q':
+				rep->command = QCOM;
+				if(rep->ad2.type != A_NONE)
+					quit(AD1MES, (char *) linebuf);
+				break;
+
+			case 'l':
+				rep->command = LCOM;
+				break;
+
+			case 's':
+				rep->command = SCOM;
+				seof = *cp++;
+				if ((rep->re1 = compile()) == 0) {
+					if(!lastre)
+						quit("First RE may not be null.", 0);
+					rep->re1 = lastre;
+				}
+				rep->rhs = p;
+				if((p = compsub(p, addend)) == 0)
+					quit(CGMES, (char *) linebuf);
+				if(*cp == 'g') {
+					cp++;
+					rep->gfl++;
+				} else if(gflag)
+					rep->gfl++;
+
+				if(*cp == 'p') {
+					cp++;
+					rep->pfl = 1;
+				}
+
+				if(*cp == 'P') {
+					cp++;
+					rep->pfl = 2;
+				}
+
+				if(*cp == 'w') {
+					cp++;
+					if(*cp++ !=  ' ')
+						quit(CGMES, (char *) linebuf);
+					text(fname[nfiles]);
+					for(i = nfiles - 1; i >= 0; i--)
+						if(cmp(fname[nfiles],fname[i]) == 0) {
+							rep->fcode = fcode[i];
+							goto done;
+						}
+					if(nfiles >= MAXFILES)
+						quit("Too many files in w commands 1", 0);
+					rep->fcode = open_file(fname[nfiles]);
+				}
+				break;
+
+			case 'w':
+				rep->command = WCOM;
+				if(*cp++ != ' ')
+					quit(CGMES, (char *) linebuf);
+				text(fname[nfiles]);
+				for(i = nfiles - 1; i >= 0; i--)
+					if(cmp(fname[nfiles], fname[i]) == 0) {
+						rep->fcode = fcode[i];
+						goto done;
+					}
+				if(nfiles >= MAXFILES){
+					fprint(2, "sed: Too many files in w commands 2 \n");
+					fprint(2, "nfiles = %d; MAXF = %d\n", nfiles, MAXFILES);
+					errexit();
+				}
+				rep->fcode = open_file(fname[nfiles]);
+				break;
+
+			case 'x':
+				rep->command = XCOM;
+				break;
+
+			case 'y':
+				rep->command = YCOM;
+				seof = *cp++;
+				if (ycomp(rep) == 0)
+					quit(CGMES, (char *) linebuf);
+				break;
+
+		}
+done:
+		if(++rep >= pend)
+			quit("Too many commands, last: %S", (char *) linebuf);
+
+		if(*cp++ != '\0') {
+			if(cp[-1] == ';')
+				goto comploop;
+			quit(CGMES, (char *) linebuf);
+		}
+
+	}
+}
+
+Biobuf *
+open_file(char *name)
+{
+	Biobuf *bp;
+	int fd;
+
+	if ((bp = malloc(sizeof(Biobuf))) == 0)
+		quit("Out of memory", 0);
+	if ((fd = open(name, OWRITE)) < 0 &&
+		(fd = create(name, OWRITE, 0666)) < 0)
+			quit("Cannot create %s", name);
+	Binit(bp, fd, OWRITE);
+	Bseek(bp, 0, 2);
+	fcode[nfiles++] = bp;
+	return bp;
+}
+
+Rune	*
+compsub(Rune *rhs, Rune *end)
+{
+	Rune	r;
+
+	while ((r = *cp++) != '\0') {
+		if(r == '\\') {
+			if (rhs < end)
+				*rhs++ = 0xFFFF;
+			else
+				return 0;
+			r = *cp++;
+			if(r == 'n')
+				r = '\n';
+		} else {
+			if(r == seof) {
+				if (rhs < end)
+					*rhs++ = '\0';
+				else
+					return 0;
+				return rhs;
+			}
+		}
+		if (rhs < end)
+			*rhs++ = r;
+		else	
+			return 0;
+
+	}
+	return 0;
+}
+
+Reprog *
+compile(void)
+{
+	Rune c;
+	char *ep;
+	char expbuf[512];
+
+	if((c = *cp++) == seof)		/* '//' */
+		return 0;
+	ep = expbuf;
+	do {
+		if (c == 0 || c == '\n')
+			quit(TMMES, (char *) linebuf);
+		if (c == '\\') {
+			if (ep >= expbuf+sizeof(expbuf))
+				quit(TMMES, (char *) linebuf);
+			ep += runetochar(ep, &c);
+			if ((c = *cp++) == 'n')
+				c = '\n';
+		}
+		if (ep >= expbuf+sizeof(expbuf))
+			quit(TMMES, (char *) linebuf);
+		ep += runetochar(ep, &c);
+	} while ((c = *cp++) != seof);
+	*ep = 0;
+	return lastre = regcomp(expbuf);
+}
+
+void
+regerror(char *s)
+{
+	USED(s);
+	quit(CGMES, (char *) linebuf);
+}
+
+void
+newfile(enum PTYPE type, char *name)
+{
+	if (type == P_ARG)
+		prog.pctl.curr = name;
+	else if ((prog.pctl.bp = Bopen(name, OREAD)) == 0)
+		quit("Cannot open pattern-file: %s\n", name);
+	prog.type = type;
+}
+
+int
+rline(Rune *buf, Rune *end)
+{
+	long c;
+	Rune r;
+
+	while ((c = getrune()) >= 0) {
+		r = c;
+		if (r == '\\') {
+			if (buf <= end)
+				*buf++ = r;
+			if ((c = getrune()) < 0)
+				break;
+			r = c;
+		} else if (r == '\n') {
+			*buf = '\0';
+			return(1);
+		}
+		if (buf <= end)
+			*buf++ = r;
+	}
+	*buf = '\0';
+	return(-1);
+}
+
+long
+getrune(void)
+{
+	char *p;
+	long c;
+	Rune r;
+
+	if (prog.type == P_ARG) {
+		if ((p = prog.pctl.curr) != 0) {
+			if (*p) {
+				prog.pctl.curr += chartorune(&r, p);
+				c = r;
+			} else {
+				c = '\n';	/* fake an end-of-line */
+				prog.pctl.curr = 0;
+			}
+		} else 
+			c = -1;
+	} else if ((c = Bgetrune(prog.pctl.bp)) < 0)
+			Bterm(prog.pctl.bp);
+	return c;
+}
+
+void
+address(Addr *ap)
+{
+	int c;
+	long	lno;
+
+	if((c = *cp++) == '$')
+		ap->type = A_DOL;
+	else if(c == '/') {
+		seof = c;
+		if (ap->rp = compile())
+			ap->type = A_RE;
+		else
+			ap->type = A_LAST;
+	}
+	else if (c >= '0' && c <= '9') {
+		lno = c-'0';
+		while ((c = *cp) >= '0' && c <= '9')
+			lno = lno*10 + *cp++-'0';
+		if(!lno)
+			quit("line number 0 is illegal",0);
+		ap->type = A_LINE;
+		ap->line = lno;
+	}
+	else {
+		cp--;
+		ap->type = A_NONE;
+	}
+}
+
+int
+cmp(char *a, char *b)		/* compare characters */
+{
+	while(*a == *b++)
+		if (*a == '\0')
+			return(0);
+		else a++;
+	return(1);
+}
+
+int
+rcmp(Rune *a, Rune *b)		/* compare runes */
+{
+	while(*a == *b++)
+		if (*a == '\0')
+			return(0);
+		else a++;
+	return(1);
+}
+
+char *
+text(char *p)		/* extract character string */
+{
+	Rune	r;
+
+	while(*cp == '\t' || *cp == ' ')
+			cp++;
+	while (*cp) {
+		if ((r = *cp++) == '\\')
+			if ((r = *cp++) == 0)
+				break;;
+		if (r == '\n')
+			while (*cp == '\t' || *cp == ' ')
+					cp++;
+		p += runetochar(p, &r);
+	}
+	*p++ = '\0';
+	return p;
+}
+
+Rune *
+stext(Rune *p, Rune *end)		/* extract rune string */
+{
+	while(*cp == '\t' || *cp == ' ')
+		cp++;
+	while (*cp) {
+		if (*cp == '\\')
+			if (*++cp == 0)
+				break;
+		if (p >= end-1)
+			quit(TMMES, (char *) linebuf);
+		if ((*p++ = *cp++) == '\n')
+			while(*cp == '\t' || *cp == ' ')
+					cp++;
+	}
+	*p++ = 0;
+	return p;
+}
+
+
+Label *
+search (Label *ptr)
+{
+	Label	*rp;
+
+	for (rp = ltab; rp < ptr; rp++)
+		if(rcmp(rp->asc, ptr->asc) == 0)
+			return(rp);
+	return(0);
+}
+
+void
+dechain(void)
+{
+	Label	*lptr;
+	SedCom	*rptr, *trptr;
+
+	for(lptr = ltab; lptr < lab; lptr++) {
+
+		if(lptr->address == 0)
+			quit("Undefined label: %S", (char *) lptr->asc);
+
+		if(lptr->chain) {
+			rptr = lptr->chain;
+			while(trptr = rptr->lb1) {
+				rptr->lb1 = lptr->address;
+				rptr = trptr;
+			}
+			rptr->lb1 = lptr->address;
+		}
+	}
+}
+
+int
+ycomp(SedCom *r)
+{
+	int 	i;
+	Rune	*rp;
+	Rune	c, *tsp, highc;
+	Rune	*sp;
+
+	highc = 0;
+	for(tsp = cp; *tsp != seof; tsp++) {
+		if(*tsp == '\\')
+			tsp++;
+		if(*tsp == '\n' || *tsp == '\0')
+			return(0);
+		if (*tsp > highc) highc = *tsp;
+	}
+	tsp++;
+	if ((rp = r->text = (Rune *) malloc(sizeof(Rune)*(highc+2))) == 0)
+		quit("Out of memory", 0);
+	*rp++ = highc;				/* save upper bound */
+	for (i = 0; i <= highc; i++)
+		rp[i] = i;
+	sp = cp;
+	while((c = *sp++) != seof) {
+		if(c == '\\' && *sp == 'n') {
+			sp++;
+			c = '\n';
+		}
+		if((rp[c] = *tsp++) == '\\' && *tsp == 'n') {
+			rp[c] = '\n';
+			tsp++;
+		}
+		if(rp[c] == seof || rp[c] == '\0') {
+			free(r->re1);
+			r->re1 = 0;
+			return(0);
+		}
+	}
+	if(*tsp != seof) {
+		free(r->re1);
+		r->re1 = 0;
+		return(0);
+	}
+	cp = tsp+1;
+	return(1);
+}
+
+void
+execute(void)
+{
+	SedCom	*ipc;
+
+	while (spend = gline(linebuf)){
+		for(ipc = pspace; ipc->command; ) {
+			if (!executable(ipc)) {
+				ipc++;
+				continue;
+			}
+			command(ipc);
+
+			if(delflag)
+				break;
+			if(jflag) {
+				jflag = 0;
+				if((ipc = ipc->lb1) == 0)
+					break;
+			} else
+				ipc++;
+
+		}
+		if(!nflag && !delflag)
+			putline(&fout, linebuf, spend-linebuf);
+		if(aptr > abuf) {
+			arout();
+		}
+		delflag = 0;
+	}
+}
+	/* determine if a statement should be applied to an input line */
+int
+executable(SedCom *ipc)
+{
+	if (ipc->active) {	/* Addr1 satisfied - accept until Addr2 */
+		if (ipc->active == 1)		/* Second line */
+			ipc->active = 2;
+		switch(ipc->ad2.type) {
+			case A_NONE:	/* No second addr; use first */
+				ipc->active = 0;
+				break;
+			case A_DOL:	/* Accept everything */
+				return !ipc->negfl;
+			case A_LINE:	/* Line at end of range? */
+				if (lnum <= ipc->ad2.line) {
+					if (ipc->ad2.line == lnum)
+						ipc->active = 0;
+					return !ipc->negfl;
+				}
+				ipc->active = 0;	/* out of range */
+				return ipc->negfl;
+			case A_RE:	/* Check for matching R.E. */
+				if (match(ipc->ad2.rp, linebuf))
+					ipc->active = 0;
+				return !ipc->negfl;
+			default:		/* internal error */
+				quit("Internal error", 0);
+		}
+	}
+	switch (ipc->ad1.type) {	/* Check first address */
+		case A_NONE:			/* Everything matches */
+			return !ipc->negfl;
+		case A_DOL:			/* Only last line */
+			if (dolflag)
+				return !ipc->negfl;
+			break;
+		case A_LINE:			/* Check line number */
+			if (ipc->ad1.line == lnum) {
+				ipc->active = 1;	/* In range */
+				return !ipc->negfl;
+			}
+			break;
+		case A_RE:			/* Check R.E. */
+			if (match(ipc->ad1.rp, linebuf)) {
+				ipc->active = 1;	/* In range */
+				return !ipc->negfl;
+			}
+			break;
+		default:
+			quit("Internal error", 0);
+	}
+	return ipc->negfl;
+}
+
+int
+match(Reprog *pattern, Rune *buf)
+{
+	if (!pattern)
+		return 0;
+	subexp[0].s.rsp = buf; 
+	subexp[0].e.rep = 0;
+	if (rregexec(pattern, linebuf, subexp, MAXSUB)) {
+		loc1 = subexp[0].s.rsp;
+		loc2 = subexp[0].e.rep;
+		return 1;
+	}
+	loc1 = loc2 = 0;
+	return 0;
+}
+
+int
+substitute(SedCom *ipc)
+{
+	int len;
+
+	if(!match(ipc->re1, linebuf))
+		return 0;
+
+	/*
+	 * we have at least one match.  some patterns, e.g. '$' or '^', can
+	 * produce zero-length matches, so during a global substitute we
+	 * must bump to the character after a zero-length match to keep from looping.
+	 */
+	sflag = 1;
+	if(ipc->gfl == 0)		/* single substitution */
+		dosub(ipc->rhs);
+	else
+	do{				/* global substitution */
+		len = loc2-loc1;	/* length of match */
+		dosub(ipc->rhs);	/* dosub moves loc2 */
+		if(*loc2 == 0)		/* end of string */
+			break;
+		if(len == 0)		/* zero-length R.E. match */
+			loc2++;		/* bump over zero-length match */
+		if(*loc2 == 0)		/* end of string */
+			break;
+	} while(match(ipc->re1, loc2));
+	return 1;
+}
+
+void
+dosub(Rune *rhsbuf)
+{
+	Rune *lp, *sp;
+	Rune *rp;
+	int c, n;
+
+	lp = linebuf;
+	sp = genbuf;
+	rp = rhsbuf;
+	while (lp < loc1)
+		*sp++ = *lp++;
+	while(c = *rp++) {
+		if (c == '&') {
+			sp = place(sp, loc1, loc2);
+			continue;
+		}
+		if (c == 0xFFFF && (c = *rp++) >= '1' && c < MAXSUB+'0') {
+			n = c-'0';
+			if (subexp[n].s.rsp && subexp[n].e.rep) {
+				sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep);
+				continue;
+			}
+			else {
+				fprint(2, "sed: Invalid back reference \\%d\n",n);
+				errexit();
+			}
+		}
+		*sp++ = c;
+		if (sp >= &genbuf[LBSIZE])
+			fprint(2, "sed: Output line too long.\n");
+	}
+	lp = loc2;
+	loc2 = sp - genbuf + linebuf;
+	while (*sp++ = *lp++)
+		if (sp >= &genbuf[LBSIZE])
+			fprint(2, "sed: Output line too long.\n");
+	lp = linebuf;
+	sp = genbuf;
+	while (*lp++ = *sp++)
+		;
+	spend = lp-1;
+}
+
+Rune *
+place(Rune *sp, Rune *l1, Rune *l2)
+{
+	while (l1 < l2) {
+		*sp++ = *l1++;
+		if (sp >= &genbuf[LBSIZE])
+			fprint(2, "sed: Output line too long.\n");
+	}
+	return(sp);
+}
+
+char *
+trans(int c)
+{
+	static char buf[] = "\\x0000";
+	static char hex[] = "0123456789abcdef";
+
+	switch(c) {
+		case '\b':
+			return "\\b";
+		case '\n':
+			return "\\n";
+		case '\r':
+			return "\\r";
+		case '\t':
+			return "\\t";
+		case '\\':
+			return "\\\\";
+	}
+	buf[2] = hex[(c>>12)&0xF];
+	buf[3] = hex[(c>>8)&0xF];
+	buf[4] = hex[(c>>4)&0xF];
+	buf[5] = hex[c&0xF];
+	return buf;
+}
+
+void
+command(SedCom *ipc)
+{
+	int	i, c;
+	Rune	*p1, *p2;
+	char	*ucp;
+	Rune	*rp;
+	Rune	*execp;
+
+	switch(ipc->command) {
+
+		case ACOM:
+			*aptr++ = ipc;
+			if(aptr >= abuf+MAXADDS) {
+				quit("sed: Too many appends after line %ld\n",
+					(char *) lnum);
+			}
+			*aptr = 0;
+			break;
+		case CCOM:
+			delflag = 1;
+			if(ipc->active == 1) {
+				for(rp = ipc->text; *rp; rp++)
+					Bputrune(&fout, *rp);
+				Bputc(&fout, '\n');
+			}
+			break;
+		case DCOM:
+			delflag++;
+			break;
+		case CDCOM:
+			p1 = p2 = linebuf;
+			while(*p1 != '\n') {
+				if(*p1++ == 0) {
+					delflag++;
+					return;
+				}
+			}
+			p1++;
+			while(*p2++ = *p1++)
+				;
+			spend = p2-1;
+			jflag++;
+			break;
+		case EQCOM:
+			Bprint(&fout, "%ld\n", lnum);
+			break;
+		case GCOM:
+			p1 = linebuf;
+			p2 = holdsp;
+			while(*p1++ = *p2++)
+				;
+			spend = p1-1;
+			break;
+		case CGCOM:
+			*spend++ = '\n';
+			p1 = spend;
+			p2 = holdsp;
+			while(*p1++ = *p2++)
+				if(p1 >= lbend)
+					break;
+			spend = p1-1;
+			break;
+		case HCOM:
+			p1 = holdsp;
+			p2 = linebuf;
+			while(*p1++ = *p2++);
+			hspend = p1-1;
+			break;
+		case CHCOM:
+			*hspend++ = '\n';
+			p1 = hspend;
+			p2 = linebuf;
+			while(*p1++ = *p2++)
+				if(p1 >= hend)
+					break;
+			hspend = p1-1;
+			break;
+		case ICOM:
+			for(rp = ipc->text; *rp; rp++)
+				Bputrune(&fout, *rp);
+			Bputc(&fout, '\n');
+			break;
+		case BCOM:
+			jflag = 1;
+			break;
+		case LCOM:
+			c = 0;
+			for (i = 0, rp = linebuf; *rp; rp++) {
+				c = *rp;
+				if(c >= 0x20 && c < 0x7F && c != '\\') {
+					Bputc(&fout, c);
+					if(i++ > 71) {
+						Bprint(&fout, "\\\n");
+						i = 0;
+					}
+				} else {
+					for (ucp = trans(*rp); *ucp; ucp++){
+						c = *ucp;
+						Bputc(&fout, c);
+						if(i++ > 71) {
+							Bprint(&fout, "\\\n");
+							i = 0;
+						}
+					}
+				}
+			}
+			if(c == ' ')
+				Bprint(&fout, "\\n");
+			Bputc(&fout, '\n');
+			break;
+		case NCOM:
+			if(!nflag)
+				putline(&fout, linebuf, spend-linebuf);
+
+			if(aptr > abuf)
+				arout();
+			if((execp = gline(linebuf)) == 0) {
+				delflag = 1;
+				break;
+			}
+			spend = execp;
+			break;
+		case CNCOM:
+			if(aptr > abuf)
+				arout();
+			*spend++ = '\n';
+			if((execp = gline(spend)) == 0) {
+				delflag = 1;
+				break;
+			}
+			spend = execp;
+			break;
+		case PCOM:
+			putline(&fout, linebuf, spend-linebuf);
+			break;
+		case CPCOM:
+	cpcom:
+			for(rp = linebuf; *rp && *rp != '\n'; rp++)
+				Bputc(&fout, *rp);
+			Bputc(&fout, '\n');
+			break;
+		case QCOM:
+			if(!nflag)
+				putline(&fout, linebuf, spend-linebuf);
+			if(aptr > abuf)
+				arout();
+			exits(0);
+		case RCOM:
+			*aptr++ = ipc;
+			if(aptr >= &abuf[MAXADDS])
+				quit("sed: Too many reads after line %ld\n",
+					(char *) lnum);
+			*aptr = 0;
+			break;
+		case SCOM:
+			i = substitute(ipc);
+			if(i && ipc->pfl)
+				if(ipc->pfl == 1)
+					putline(&fout, linebuf, spend-linebuf);
+				else
+					goto cpcom;
+			if(i && ipc->fcode)
+				goto wcom;
+			break;
+
+		case TCOM:
+			if(sflag == 0)	break;
+			sflag = 0;
+			jflag = 1;
+			break;
+
+		wcom:
+		case WCOM:
+			putline(ipc->fcode,linebuf, spend-linebuf);
+			break;
+		case XCOM:
+			p1 = linebuf;
+			p2 = genbuf;
+			while(*p2++ = *p1++);
+			p1 = holdsp;
+			p2 = linebuf;
+			while(*p2++ = *p1++);
+			spend = p2 - 1;
+			p1 = genbuf;
+			p2 = holdsp;
+			while(*p2++ = *p1++);
+			hspend = p2 - 1;
+			break;
+		case YCOM:
+			p1 = linebuf;
+			p2 = ipc->text;
+			for (i = *p2++;	*p1; p1++){
+				if (*p1 <= i) *p1 = p2[*p1];
+			}
+			break;
+	}
+
+}
+
+void
+putline(Biobuf *bp, Rune *buf, int n)
+{
+	while (n--)
+		Bputrune(bp, *buf++);
+	Bputc(bp, '\n');
+}
+
+int
+ecmp(Rune *a, Rune *b, int count)
+{
+	while(count--)
+		if(*a++ != *b++)	return(0);
+	return(1);
+}
+
+void
+arout(void)
+{
+	Rune	*p1;
+	Biobuf	*fi;
+	int	c;
+	char	*s;
+	char	buf[128];
+
+	for (aptr = abuf; *aptr; aptr++) {
+		if((*aptr)->command == ACOM) {
+			for(p1 = (*aptr)->text; *p1; p1++ )
+				Bputrune(&fout, *p1);
+			Bputc(&fout, '\n');
+		} else {
+			for(s = buf, p1= (*aptr)->text; *p1; p1++)
+					s += runetochar(s, p1);
+			*s = '\0';
+			if((fi = Bopen(buf, OREAD)) == 0)
+				continue;
+			while((c = Bgetc(fi)) >= 0)
+				Bputc(&fout, c);
+			Bterm(fi);
+		}
+	}
+	aptr = abuf;
+	*aptr = 0;
+}
+
+void
+errexit(void)
+{
+	exits("error");
+}
+
+void
+quit (char *msg, char *arg)
+{
+	fprint(2, "sed: ");
+	fprint(2, msg, arg);
+	fprint(2, "\n");
+	errexit();
+}
+
+Rune *
+gline(Rune *addr)
+{
+	long	c;
+	Rune *p;
+
+	static long peekc = 0;
+
+	if (f == 0 && opendata() < 0)
+		return 0;
+	sflag = 0;
+	lnum++;
+/*	Bflush(&fout);********* dumped 4/30/92 - bobf****/
+	do {
+		p = addr;
+		for (c = (peekc ? peekc : Bgetrune(f)); c >= 0; c = Bgetrune(f)) {
+			if (c == '\n') {
+				if ((peekc = Bgetrune(f)) < 0) {
+					if (fhead == 0)
+						dolflag = 1;
+				}
+				*p = '\0';
+				return p;
+			}
+			if (c && p < lbend)
+				*p++ = c;
+		}
+		/* return partial final line, adding implicit newline */
+		if(p != addr) {
+			*p = '\0';
+			peekc = -1;
+			if (fhead == 0)
+				dolflag = 1;
+			return p;
+		}
+		peekc = 0;
+		Bterm(f);
+	} while (opendata() > 0);	/* Switch to next stream */
+	f = 0;
+	return 0;
+}
+
+	/* Data file input section - the intent is to transparently
+	 *	catenate all data input streams.
+	 */
+void
+enroll(char *filename)		/* Add a file to the input file cache */
+{
+	FileCache *fp;
+
+	if ((fp = (FileCache *) malloc(sizeof (FileCache))) == 0)
+		quit("Out of memory", 0);
+	if (ftail == 0)
+		fhead = fp;
+	else
+		ftail->next = fp;
+	ftail = fp;
+	fp->next = 0;
+	fp->name = filename;	/* 0 => stdin */
+}
+
+int
+opendata(void)
+{
+	if (fhead == 0)
+		return -1;
+	if (fhead->name) {
+		if ((f = Bopen(fhead->name, OREAD)) == 0)
+			quit("Can't open %s", fhead->name);
+	} else {
+		Binit(&bstdin, 0, OREAD);
+		f = &bstdin;
+	}
+	fhead = fhead->next;
+	return 1;
+}
blob - /dev/null
blob + 858b4e74e89899103b08cd8e269ad56e2a86ac8f (mode 644)
--- /dev/null
+++ src/lib9/get9root.c
@@ -0,0 +1,13 @@
+#include <u.h>
+#include <libc.h>
+
+char*
+get9root(void)
+{
+	char *s;
+
+	if((s = getenv("PLAN9")) != 0)
+		return s;
+	return "/usr/local/plan9";
+}
+
blob - /dev/null
blob + b7db700c0e85744e4768bbb06a29b973a4c92fdd (mode 644)
--- /dev/null
+++ src/lib9/unsharp.c
@@ -0,0 +1,44 @@
+#include <u.h>
+#include <libc.h>
+
+/*
+ * I don't want too many of these,
+ * but the ones we have are just too useful. 
+ */
+static struct {
+	char *old;
+	char *new;
+} replace[] = {
+	"#9", nil,	/* must be first */
+	"#d", "/dev/fd",
+};
+
+char*
+unsharp(char *old)
+{
+	char *new;
+	int i, olen, nlen, len;
+
+	if(replace[0].new == nil)
+		replace[0].new = get9root();
+
+	for(i=0; i<nelem(replace); i++){
+		if(!replace[i].new)
+			continue;
+		olen = strlen(replace[i].old);
+		if(strncmp(old, replace[i].old, olen) != 0
+		|| (old[olen] != '\0' && old[olen] != '/'))
+			continue;
+		nlen = strlen(replace[i].new);
+		len = strlen(old)+nlen-olen;
+		new = malloc(len+1);
+		if(new == nil)
+			/* Most callers won't check the return value... */
+			sysfatal("out of memory translating %s to %s%s", old, replace[i].new, old+olen);
+		strcpy(new, replace[i].new);
+		strcpy(new+nlen, old+olen);
+		assert(strlen(new) == len);
+		return new;
+	}
+	return old;
+}
blob - /dev/null
blob + 2c38808bfca445c4978ecee434e47d706d4e6f82 (mode 644)
--- /dev/null
+++ src/libventi/parsescore.c
@@ -0,0 +1,40 @@
+#include <u.h>
+#include <libc.h>
+#include <venti.h>
+
+int
+vtparsescore(char *s, char **prefix, uchar score[VtScoreSize])
+{
+	int i, c;
+	char *buf, *colon;
+
+	if((colon = strchr(s, ':')) != nil)
+		buf = colon+1;
+	else
+		buf = s;
+
+	if(strlen(buf) != 2*VtScoreSize)
+		return -1;
+
+	memset(score, 0, VtScoreSize);
+	for(i=0; i<2*VtScoreSize; i++){
+		if(buf[i] >= '0' && buf[i] <= '9')
+			c = buf[i] - '0';
+		else if(buf[i] >= 'a' && buf[i] <= 'z')
+			c = buf[i] - 'a' + 10;
+		else if(buf[i] >= 'A' && buf[i] <= 'Z')
+			c = buf[i] - 'A' + 10;
+		else
+			return -1;
+
+		if((i & 1) == 0)
+			c <<= 4;
+		score[i>>1] |= c;
+	}
+	if(colon){
+		*colon = 0;
+		*prefix = s;
+	}else
+		*prefix = nil;
+	return 0;
+}