| /* | 
 |  * 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. */ | 
 | 	} u; | 
 | } 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 */ | 
 | 	} u; | 
 | 	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	lflag; | 
 |  | 
 | 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; | 
 | 		case 'l': | 
 | 			lflag++; | 
 | 			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.u.rp = lastre; | 
 | 			} | 
 | 			if(*cp == ',' || *cp == ';') { | 
 | 				cp++; | 
 | 				address(&rep->ad2); | 
 | 				if (rep->ad2.type == A_LAST) { | 
 | 					rep->ad1.type = A_RE; | 
 | 					rep->ad2.u.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->u.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->u.text = p; | 
 | 				p = stext(p, addend); | 
 | 				break; | 
 | 			case 'c': | 
 | 				rep->command = CCOM; | 
 | 				if(*cp == '\\')	cp++; | 
 | 				if(*cp++ != '\n') | 
 | 					quit(CGMES, (char *) linebuf); | 
 | 				rep->u.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->u.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->u.lb1) | 
 | 							pt = pt1; | 
 | 						pt->u.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->u.lb1 = lpt->address; | 
 | 					} else { | 
 | 						pt = lpt->chain; | 
 | 						while(pt1 = pt->u.lb1) | 
 | 							pt = pt1; | 
 | 						pt->u.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->u.text = p; | 
 | 				p = stext(p, addend); | 
 | 				break; | 
 |  | 
 | 			case 'd': | 
 | 				rep->command = DCOM; | 
 | 				break; | 
 |  | 
 | 			case 'D': | 
 | 				rep->command = CDCOM; | 
 | 				rep->u.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->u.re1 = compile()) == 0) { | 
 | 					if(!lastre) | 
 | 						quit("First RE may not be null.", 0); | 
 | 					rep->u.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++ = Runemax; | 
 | 			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->u.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->u.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->u.lb1) { | 
 | 				rptr->u.lb1 = lptr->address; | 
 | 				rptr = trptr; | 
 | 			} | 
 | 			rptr->u.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->u.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->u.re1); | 
 | 			r->u.re1 = 0; | 
 | 			return(0); | 
 | 		} | 
 | 	} | 
 | 	if(*tsp != seof) { | 
 | 		free(r->u.re1); | 
 | 		r->u.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->u.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.u.line) { | 
 | 					if (ipc->ad2.u.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.u.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.u.line == lnum) { | 
 | 				ipc->active = 1;	/* In range */ | 
 | 				return !ipc->negfl; | 
 | 			} | 
 | 			break; | 
 | 		case A_RE:			/* Check R.E. */ | 
 | 			if (match(ipc->ad1.u.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) > 0) { | 
 | 		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->u.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->u.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 == Runemax && (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->u.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->u.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->u.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'); | 
 | 	if(lflag) | 
 | 		Bflush(bp); | 
 | } | 
 |  | 
 | 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)->u.text; *p1; p1++ ) | 
 | 				Bputrune(&fout, *p1); | 
 | 			Bputc(&fout, '\n'); | 
 | 		} else { | 
 | 			for(s = buf, p1= (*aptr)->u.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; | 
 | } |