820 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			820 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* sedexec.c -- axecute compiled form of stream editor commands
 | |
|    Copyright (C) 1995-2003 Eric S. Raymond
 | |
|    Copyright (C) 2004-2006 Rene Rebe
 | |
| 
 | |
|    The single entry point of this module is the function execute(). It
 | |
| may take a string argument (the name of a file to be used as text)  or
 | |
| the argument NULL which tells it to filter standard input. It executes
 | |
| the compiled commands in cmds[] on each line in turn.
 | |
|    The function command() does most of the work.  match() and advance()
 | |
| are used for matching text against precompiled regular expressions and
 | |
| dosub() does right-hand-side substitution.  Getline() does text input;
 | |
| readout() and memcmp() are output and string-comparison utilities.  
 | |
| */
 | |
| 
 | |
| #include <stdlib.h>	/* exit */
 | |
| #include <stdio.h>	/* {f}puts, {f}printf, getc/putc, f{re}open, fclose */
 | |
| #include <ctype.h>	/* for isprint(), isdigit(), toascii() macros */
 | |
| #include <string.h>	/* for memcmp(3) */
 | |
| #include "sed.h"	/* command type structures & miscellaneous constants */
 | |
| 
 | |
| /***** shared variables imported from the main ******/
 | |
| 
 | |
| /* main data areas */
 | |
| extern char	linebuf[];	/* current-line buffer */
 | |
| extern sedcmd	cmds[];		/* hold compiled commands */
 | |
| extern long	linenum[];	/* numeric-addresses table */
 | |
| 
 | |
| /* miscellaneous shared variables */
 | |
| extern int	nflag;		/* -n option flag */
 | |
| extern int	eargc;		/* scratch copy of argument count */
 | |
| extern sedcmd	*pending;	/* ptr to command waiting to be executed */
 | |
| 
 | |
| extern int	last_line_used; /* last line address ($) used */
 | |
| 
 | |
| /***** end of imported stuff *****/
 | |
| 
 | |
| #define MAXHOLD		MAXBUF	/* size of the hold space */
 | |
| #define GENSIZ		MAXBUF	/* maximum genbuf size */
 | |
| 
 | |
| static char LTLMSG[]	= "sed: line too long\n";
 | |
| 
 | |
| static char	*spend;		/* current end-of-line-buffer pointer */
 | |
| static long	lnum = 0L;	/* current source line number */
 | |
| 
 | |
| /* append buffer maintenance */
 | |
| static sedcmd	*appends[MAXAPPENDS];	/* array of ptrs to a,i,c commands */
 | |
| static sedcmd	**aptr = appends;	/* ptr to current append */
 | |
| 
 | |
| /* genbuf and its pointers */
 | |
| static char	genbuf[GENSIZ];
 | |
| static char	*loc1;
 | |
| static char	*loc2;
 | |
| static char	*locs;
 | |
| 
 | |
| /* command-logic flags */
 | |
| static int	lastline;		/* do-line flag */
 | |
| static int	line_with_newline;	/* line had newline */
 | |
| static int	jump;			/* jump to cmd's link address if set */
 | |
| static int	delete;			/* delete command flag */
 | |
| static int	needs_advance;		/* needs inc after substitution */
 | |
| 					/* ugly HACK - neds REWORK */
 | |
| 
 | |
| /* tagged-pattern tracking */
 | |
| static char	*bracend[MAXTAGS];	/* tagged pattern start pointers */
 | |
| static char	*brastart[MAXTAGS];	/* tagged pattern end pointers */
 | |
| 
 | |
| /* prototypes */
 | |
| static char *getline(char *buf, int max);
 | |
| static char *place(char* asp, char* al1, char* al2);
 | |
| static int advance(char* lp, char* ep, char** eob);
 | |
| static int match(char *expbuf, int gf);
 | |
| static int selected(sedcmd *ipc);
 | |
| static int substitute(sedcmd *ipc);
 | |
| static void command(sedcmd *ipc);
 | |
| static void dosub(char *rhsbuf);
 | |
| static void dumpto(char *p1, FILE *fp);
 | |
| static void listto(char *p1, FILE *fp);
 | |
| static void readout(void);
 | |
| static void truncated(int h);
 | |
| 
 | |
| /* execute the compiled commands in cmds[] on a file
 | |
|    file:  name of text source file to be filtered */
 | |
| void execute(char* file)
 | |
| {
 | |
| 	register sedcmd		*ipc;		/* ptr to current command */
 | |
| 	char			*execp;		/* ptr to source */
 | |
| 
 | |
| 	if (file != NULL)	/* filter text from a named file */ 
 | |
| 		if (freopen(file, "r", stdin) == NULL)
 | |
| 			fprintf(stderr, "sed: can't open %s\n", file);
 | |
| 
 | |
| 	if (pending)		/* there's a command waiting */
 | |
| 	{
 | |
| 		ipc = pending;		/* it will be first executed */
 | |
| 		pending = FALSE;	/*   turn off the waiting flag */
 | |
| 		goto doit;		/*   go to execute it immediately */
 | |
| 	}
 | |
| 
 | |
| 	/* here's the main command-execution loop */
 | |
| 	for(;;)
 | |
| 	{
 | |
| 		/* get next line to filter */
 | |
| 		if ((execp = getline(linebuf, MAXBUF+1)) == BAD)
 | |
| 			return;
 | |
| 		spend = execp;
 | |
| 
 | |
| 		/* loop through compiled commands, executing them */
 | |
| 		for(ipc = cmds; ipc->command; )
 | |
| 		{
 | |
| 			/* address command to select? - If not address
 | |
| 			   but allbut then invert, that is skip, the commmand */
 | |
| 			if (ipc->addr1 || ipc->flags.allbut) {
 | |
| 				if (!ipc->addr1 || !selected(ipc)) {
 | |
| 					ipc++;	/* not selected, next cmd */
 | |
| 					continue;
 | |
| 				}
 | |
| 			}
 | |
| 	doit:
 | |
| 			command(ipc);	/* execute the command pointed at */
 | |
| 
 | |
| 			if (delete)	/* if delete flag is set */
 | |
| 				break;	/* don't exec rest of compiled cmds */
 | |
| 
 | |
| 			if (jump)	/* if jump set, follow cmd's link */
 | |
| 			{
 | |
| 				jump = FALSE;
 | |
| 				if ((ipc = ipc->u.link) == 0)
 | |
| 				{
 | |
| 					ipc = cmds;
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 			else		/* normal goto next command */
 | |
| 				ipc++;
 | |
| 		}
 | |
| 		/* we've now done all modification commands on the line */
 | |
| 
 | |
| 		/* here's where the transformed line is output */
 | |
| 		if (!nflag && !delete)
 | |
| 		{
 | |
| 			fwrite(linebuf, spend - linebuf, 1, stdout);
 | |
| 			if (line_with_newline)
 | |
| 				putc('\n', stdout);
 | |
| 		}
 | |
| 
 | |
| 		/* if we've been set up for append, emit the text from it */
 | |
| 		if (aptr > appends)
 | |
| 			readout();
 | |
| 
 | |
| 		delete = FALSE;	/* clear delete flag; about to get next cmd */
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* is current command selected */
 | |
| static int selected(sedcmd *ipc)
 | |
| {
 | |
| 	register char	*p1 = ipc->addr1;	/* point p1 at first address */
 | |
| 	register char	*p2 = ipc->addr2;	/*   and p2 at second */
 | |
| 	unsigned char	c;
 | |
| 	int selected = FALSE;
 | |
| 
 | |
| 	if (ipc->flags.inrange)
 | |
| 	{
 | |
| 		selected = TRUE;
 | |
| 		if (*p2 == CEND)
 | |
| 			;
 | |
| 		else if (*p2 == CLNUM)
 | |
| 		{
 | |
| 			c = p2[1];
 | |
| 			if (lnum >= linenum[c])
 | |
| 				ipc->flags.inrange = FALSE;
 | |
| 		}
 | |
| 		else if (match(p2, 0))
 | |
| 			ipc->flags.inrange = FALSE;
 | |
| 	}
 | |
| 	else if (*p1 == CEND)
 | |
| 	{
 | |
| 		if (lastline)
 | |
| 			selected = TRUE;
 | |
| 	}
 | |
| 	else if (*p1 == CLNUM)
 | |
| 	{
 | |
| 		c = p1[1];
 | |
| 		if (lnum == linenum[c]) {
 | |
| 			selected = TRUE;
 | |
| 			if (p2)
 | |
| 				ipc->flags.inrange = TRUE;
 | |
| 		}
 | |
| 	}
 | |
| 	else if (match(p1, 0))
 | |
| 	{
 | |
| 		selected = TRUE;
 | |
| 		if (p2)
 | |
| 			ipc->flags.inrange = TRUE;
 | |
| 	}
 | |
| 	return ipc->flags.allbut ? !selected : selected;
 | |
| }
 | |
| 
 | |
| /* match RE at expbuf against linebuf; if gf set, copy linebuf from genbuf */
 | |
| static int match(char *expbuf, int gf)	/* uses genbuf */
 | |
| {
 | |
| 	char *p1, *p2, c;
 | |
| 
 | |
| 	if (gf)
 | |
| 	{
 | |
| 		if (*expbuf)
 | |
| 			return(FALSE);
 | |
| 		p1 = linebuf; p2 = genbuf;
 | |
| 		while ((*p1++ = *p2++));
 | |
| 		if (needs_advance) {
 | |
| 			loc2++;
 | |
| 		}
 | |
| 		locs = p1 = loc2;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		p1 = linebuf + needs_advance;
 | |
| 		locs = FALSE;
 | |
| 	}
 | |
| 	needs_advance = 0;
 | |
| 
 | |
| 	p2 = expbuf;
 | |
| 	if (*p2++)
 | |
| 	{
 | |
| 		loc1 = p1;
 | |
| 		if(*p2 == CCHR && p2[1] != *p1)	/* 1st char is wrong */
 | |
| 			return(FALSE);		/*   so fail */
 | |
| 		return(advance(p1, p2, NULL));	/* else try to match rest */
 | |
| 	}
 | |
| 
 | |
| 	/* quick check for 1st character if it's literal */
 | |
| 	if (*p2 == CCHR)
 | |
| 	{
 | |
| 		c = p2[1];		/* pull out character to search for */
 | |
| 		do {
 | |
| 			if (*p1 != c)
 | |
| 				continue;	/* scan the source string */
 | |
| 			if (advance(p1, p2,NULL)) /* found it, match the rest */
 | |
| 				return(loc1 = p1, 1);
 | |
| 		} while
 | |
| 			(*p1++);
 | |
| 		return(FALSE);		/* didn't find that first char */
 | |
| 	}
 | |
| 
 | |
| 	/* else try for unanchored match of the pattern */
 | |
| 	do {
 | |
| 		if (advance(p1, p2, NULL))
 | |
| 			return(loc1 = p1, 1);
 | |
| 	} while
 | |
| 		(*p1++);
 | |
| 
 | |
| 	/* if got here, didn't match either way */
 | |
| 	return(FALSE);
 | |
| }
 | |
| 
 | |
| /* attempt to advance match pointer by one pattern element
 | |
|    lp:	source (linebuf) ptr
 | |
|    ep:	regular expression element ptr */
 | |
| static int advance(char* lp, char* ep, char** eob)
 | |
| {
 | |
| 	char	*curlp;		/* save ptr for closures */ 
 | |
| 	char	c;		/* scratch character holder */
 | |
| 	char	*bbeg;
 | |
| 	int	ct;
 | |
| 	signed int	bcount = -1;
 | |
| 
 | |
| 	for (;;)
 | |
| 		switch (*ep++)
 | |
| 		{
 | |
| 		case CCHR:		/* literal character */
 | |
| 			if (*ep++ == *lp++)	/* if chars are equal */
 | |
| 				continue;	/* matched */
 | |
| 			return(FALSE);		/* else return false */
 | |
| 
 | |
| 		case CDOT:		/* anything but newline */
 | |
| 			if (*lp++)		/* first NUL is at EOL */
 | |
| 				continue;	/* keep going if didn't find */
 | |
| 			return(FALSE);		/* else return false */
 | |
| 
 | |
| 		case CNL:		/* start-of-line */
 | |
| 		case CDOL:		/* end-of-line */
 | |
| 			if (*lp == 0)		/* found that first NUL? */
 | |
| 				continue;	/* yes, keep going */
 | |
| 			return(FALSE);		/* else return false */
 | |
| 
 | |
| 		case CEOF:		/* end-of-address mark */
 | |
| 			loc2 = lp;		/* set second loc */
 | |
| 			return(TRUE);		/* return true */
 | |
| 
 | |
| 		case CCL:		/* a closure */
 | |
| 			c = *lp++ & 0177;
 | |
| 			if (ep[c>>3] & bits(c & 07))	/* is char in set? */
 | |
| 			{
 | |
| 				ep += 16;	/* then skip rest of bitmask */
 | |
| 				continue;	/*   and keep going */
 | |
| 			}
 | |
| 			return(FALSE);		/* else return false */
 | |
| 
 | |
| 		case CBRA:		/* start of tagged pattern */
 | |
| 			brastart[(unsigned char)*ep++] = lp;	/* mark it */
 | |
| 			continue;		/* and go */
 | |
| 
 | |
| 		case CKET:		/* end of tagged pattern */
 | |
| 			bcount = *ep;
 | |
| 			if (eob) {
 | |
| 				*eob = lp;
 | |
| 				return (TRUE);
 | |
| 			}
 | |
| 			else
 | |
| 				bracend[(unsigned char)*ep++] = lp;    /* mark it */
 | |
| 			continue;		/* and go */
 | |
| 
 | |
| 		case CBACK:		/* match back reference */
 | |
| 			bbeg = brastart[(unsigned char)*ep];
 | |
| 			ct = bracend[(unsigned char)*ep++] - bbeg;
 | |
| 
 | |
| 			if (memcmp(bbeg, lp, ct) == 0)
 | |
| 			{
 | |
| 				lp += ct;
 | |
| 				continue;
 | |
| 			}
 | |
| 			return(FALSE);
 | |
| 
 | |
| 		case CBRA|STAR:		/* \(...\)* */
 | |
| 		{
 | |
| 			char *lastlp;
 | |
| 			curlp = lp;
 | |
| 
 | |
| 			if (*ep > bcount)
 | |
| 				brastart[(unsigned char)*ep] = bracend[(unsigned char)*ep] = lp;
 | |
| 
 | |
| 			while (advance(lastlp=lp, ep+1, &lp)) {
 | |
| 				if (*ep > bcount && lp != lastlp) {
 | |
| 					bracend[(unsigned char)*ep] = lp;    /* mark it */
 | |
| 					brastart[(unsigned char)*ep] = lastlp;
 | |
| 				}
 | |
| 				if (lp == lastlp) break;
 | |
| 			}
 | |
| 			ep++;
 | |
| 
 | |
| 			/* FIXME: scan for the brace end */
 | |
| 			while (*ep != CKET)
 | |
| 				ep++;
 | |
| 			ep+=2;
 | |
| 
 | |
| 			needs_advance = 1;
 | |
| 			if (lp == curlp) /* 0 matches */
 | |
| 				continue;
 | |
| 			lp++; 
 | |
| 			goto star;
 | |
| 		}
 | |
| 		case CBACK|STAR:	/* \n* */
 | |
| 			bbeg = brastart[(unsigned char)*ep];
 | |
| 			ct = bracend[(unsigned char)*ep++] - bbeg;
 | |
| 			curlp = lp;
 | |
| 			while(memcmp(bbeg, lp, ct) == 0)
 | |
| 				lp += ct;
 | |
| 
 | |
| 			while(lp >= curlp)
 | |
| 			{
 | |
| 				if (advance(lp, ep, eob))
 | |
| 					return(TRUE);
 | |
| 				lp -= ct;
 | |
| 			}
 | |
| 			return(FALSE);
 | |
| 
 | |
| 		case CDOT|STAR:		/* match .* */
 | |
| 			curlp = lp;		/* save closure start loc */
 | |
| 			while (*lp++);		/* match anything */ 
 | |
| 			goto star;		/* now look for followers */
 | |
| 
 | |
| 		case CCHR|STAR:		/* match <literal char>* */
 | |
| 			curlp = lp;		/* save closure start loc */
 | |
| 			while (*lp++ == *ep);	/* match many of that char */
 | |
| 			ep++;			/* to start of next element */
 | |
| 			goto star;		/* match it and followers */
 | |
| 
 | |
| 		case CCL|STAR:		/* match [...]* */
 | |
| 			curlp = lp;		/* save closure start loc */
 | |
| 			do {
 | |
| 				c = *lp++ & 0x7F;	/* match any in set */
 | |
| 			} while
 | |
| 				(ep[c>>3] & bits(c & 07));
 | |
| 			ep += 16;		/* skip past the set */
 | |
| 			goto star;		/* match followers */
 | |
| 
 | |
| 		star:		/* the recursion part of a * or + match */
 | |
| 			needs_advance = 1;
 | |
| 			if (--lp == curlp) {	/* 0 matches */
 | |
| 				continue;
 | |
| 			}
 | |
| #if 0
 | |
| 			if (*ep == CCHR)
 | |
| 			{
 | |
| 				c = ep[1];
 | |
| 				do {
 | |
| 					if (*lp != c)
 | |
| 						continue;
 | |
| 					if (advance(lp, ep, eob))
 | |
| 						return(TRUE);
 | |
| 				} while
 | |
| 				(lp-- > curlp);
 | |
| 				return(FALSE);
 | |
| 			}
 | |
| 
 | |
| 			if (*ep == CBACK)
 | |
| 			{
 | |
| 				c = *(brastart[ep[1]]);
 | |
| 				do {
 | |
| 					if (*lp != c)
 | |
| 						continue;
 | |
| 					if (advance(lp, ep, eob))
 | |
| 						return(TRUE);
 | |
| 				} while
 | |
| 					(lp-- > curlp);
 | |
| 				return(FALSE);
 | |
| 			}
 | |
| #endif
 | |
| 			/* match followers, try shorter match, if needed */
 | |
| 			do {
 | |
| 				if (lp == locs)
 | |
| 					break;
 | |
| 				if (advance(lp, ep, eob))
 | |
| 					return(TRUE);
 | |
| 			} while
 | |
| 				(lp-- > curlp);
 | |
| 			return(FALSE);
 | |
| 
 | |
| 		default:
 | |
| 			fprintf(stderr, "sed: internal RE error, %o\n", *--ep);
 | |
| 			exit (2);
 | |
| 		}
 | |
| }
 | |
| 
 | |
| /* perform s command
 | |
|    ipc:	ptr to s command struct */
 | |
| static int substitute(sedcmd *ipc)
 | |
| {
 | |
| 	unsigned int n = 1;
 | |
| 	/* find a match */
 | |
| 	/* the needs_advance code got a bit tricky - might needs a clean
 | |
| 	   refactoring */
 | |
| 	while (match(ipc->u.lhs, 0)) {
 | |
| 		/* nth 0 is implied 1 */
 | |
| 		if (!ipc->nth || n == ipc->nth) {
 | |
| 			dosub(ipc->rhs);		/* perform it once */
 | |
| 			n++;				/* mark for return */
 | |
| 			break;
 | |
| 		}
 | |
| 		needs_advance = n++;
 | |
| 	}
 | |
| 	if (n == 1)
 | |
| 		return(FALSE);			/* command fails */
 | |
| 
 | |
| 	if (ipc->flags.global)			/* if global flag enabled */
 | |
| 		do {				/* cycle through possibles */
 | |
| 			if (match(ipc->u.lhs, 1)) {	/* found another */
 | |
| 				dosub(ipc->rhs);	/* so substitute */
 | |
| 			}
 | |
| 			else				/* otherwise, */
 | |
| 				break;			/* we're done */
 | |
| 		} while (*loc2);
 | |
| 	return(TRUE);				/* we succeeded */
 | |
| }
 | |
| 
 | |
| /* generate substituted right-hand side (of s command)
 | |
|    rhsbuf:	where to put the result */
 | |
| static void dosub(char *rhsbuf)		/* uses linebuf, genbuf, spend */
 | |
| {
 | |
| 	char	*lp, *sp, *rp;
 | |
| 	int	c;
 | |
| 
 | |
| 	/* copy linebuf to genbuf up to location 1 */
 | |
| 	lp = linebuf; sp = genbuf;
 | |
| 	while (lp < loc1) *sp++ = *lp++;
 | |
| 
 | |
| 	for (rp = rhsbuf; (c = *rp++); )
 | |
| 	{
 | |
| 		if (c & 0200 && (c & 0177) == '0')
 | |
| 		{
 | |
| 			sp = place(sp, loc1, loc2);
 | |
| 			continue;
 | |
| 		}
 | |
| 		else if (c & 0200 && (c &= 0177) >= '1' && c < MAXTAGS+'1')
 | |
| 		{
 | |
| 			sp = place(sp, brastart[c-'1'], bracend[c-'1']);
 | |
| 			continue;
 | |
| 		}
 | |
| 		*sp++ = c & 0177;
 | |
| 		if (sp >= genbuf + MAXBUF)
 | |
| 			fprintf(stderr, LTLMSG);
 | |
| 
 | |
| 	}
 | |
| 	lp = loc2;
 | |
| 	loc2 = sp - genbuf + linebuf;
 | |
| 	while ((*sp++ = *lp++))
 | |
| 		if (sp >= genbuf + MAXBUF)
 | |
| 			fprintf(stderr, LTLMSG);
 | |
| 	lp = linebuf; sp = genbuf;
 | |
| 	while ((*lp++ = *sp++));
 | |
| 	spend = lp-1;
 | |
| }
 | |
| 
 | |
| /* place chars at *al1...*(al1 - 1) at asp... in genbuf[] */
 | |
| static char *place(char* asp, char* al1, char* al2)		/* uses genbuf */
 | |
| {
 | |
| 	while (al1 < al2)
 | |
| 	{
 | |
| 		*asp++ = *al1++;
 | |
| 		if (asp >= genbuf + MAXBUF)
 | |
| 			fprintf(stderr, LTLMSG);
 | |
| 	}
 | |
| 	return(asp);
 | |
| }
 | |
| 
 | |
| /* list the pattern space in  visually unambiguous form *p1... to fp
 | |
|    p1: the source
 | |
|    fp: output stream to write to */
 | |
| static void listto(char *p1, FILE *fp)
 | |
| {
 | |
| 	for (; p1<spend; p1++)
 | |
| 		if (isprint(*p1))
 | |
| 			putc(*p1, fp);		/* pass it through */
 | |
| 		else
 | |
| 		{
 | |
| 			putc('\\', fp);		/* emit a backslash */
 | |
| 			switch(*p1)
 | |
| 			{
 | |
| 			case '\b':	putc('b', fp); break;	/* BS */
 | |
| 			case '\t':	putc('t', fp); break;	/* TAB */
 | |
| 			case '\n':	putc('n', fp); break;	/* NL */
 | |
| 			case '\r':	putc('r', fp); break;	/* CR */
 | |
| 			case '\033':	putc('e', fp); break;	/* ESC */
 | |
| 			default:	fprintf(fp, "%02x", *p1);
 | |
| 			}
 | |
| 		}
 | |
| 	putc('\n', fp);
 | |
| }
 | |
| 
 | |
| /* write a hex dump expansion of *p1... to fp
 | |
|    p1: source
 | |
|    fp: output */
 | |
| static void dumpto(char *p1, FILE *fp)
 | |
| {
 | |
| 	for (; p1<spend; p1++)
 | |
| 		fprintf(fp, "%02x", *p1);
 | |
| 	fprintf(fp, "%02x", '\n');
 | |
| 	putc('\n', fp);
 | |
| }
 | |
| 
 | |
| static void truncated(int h)
 | |
| {
 | |
| 	static long last = 0L;
 | |
| 
 | |
| 	if (lnum == last) return;
 | |
| 	last = lnum;
 | |
| 
 | |
| 	fprintf(stderr, "sed: ");
 | |
| 	fprintf(stderr, h ? "hold space" : "line %ld", lnum);
 | |
| 	fprintf(stderr, " truncated to %d characters\n", MAXBUF);
 | |
| }
 | |
| 
 | |
| /* execute compiled command pointed at by ipc */
 | |
| static void command(sedcmd *ipc)
 | |
| {
 | |
| 	static int	didsub;			/* true if last s succeeded */
 | |
| 	static char	holdsp[MAXHOLD];	/* the hold space */
 | |
| 	static char	*hspend = holdsp;	/* hold space end pointer */
 | |
| 	register char	*p1, *p2;
 | |
| 	char		*execp;
 | |
| 
 | |
| 	needs_advance = 0;
 | |
| 	switch(ipc->command)
 | |
| 	{
 | |
| 	case ACMD:		/* append */
 | |
| 		*aptr++ = ipc;
 | |
| 		if (aptr >= appends + MAXAPPENDS)
 | |
| 			fprintf(stderr,
 | |
| 				"sed: too many appends after line %ld\n",
 | |
| 				lnum);
 | |
| 		*aptr = 0;
 | |
| 		break;
 | |
| 
 | |
| 	case CCMD:		/* change pattern space */
 | |
| 		delete = TRUE;
 | |
| 		if (!ipc->flags.inrange || lastline)
 | |
| 			printf("%s\n", ipc->u.lhs);		
 | |
| 		break;
 | |
| 
 | |
| 	case DCMD:		/* delete pattern space */
 | |
| 		delete++;
 | |
| 		break;
 | |
| 
 | |
| 	case CDCMD:		/* delete a line in hold space */
 | |
| 		p1 = p2 = linebuf;
 | |
| 		while(*p1 != '\n')
 | |
| 			if ((delete = (*p1++ == 0)))
 | |
| 				return;
 | |
| 		p1++;
 | |
| 		while((*p2++ = *p1++)) continue;
 | |
| 		spend = p2-1;
 | |
| 		jump++;
 | |
| 		break;
 | |
| 
 | |
| 	case EQCMD:		/* show current line number */
 | |
| 		fprintf(stdout, "%ld\n", lnum);
 | |
| 		break;
 | |
| 
 | |
| 	case GCMD:		/* copy hold space to pattern space */
 | |
| 		p1 = linebuf;	p2 = holdsp;	while((*p1++ = *p2++));
 | |
| 		spend = p1-1;
 | |
| 		break;
 | |
| 
 | |
| 	case CGCMD:		/* append hold space to pattern space */
 | |
| 		*spend++ = '\n';
 | |
| 		p1 = spend;	p2 = holdsp;
 | |
| 		do {
 | |
| 			if (p1 > linebuf + MAXBUF) {
 | |
| 				truncated(FALSE);
 | |
| 				p1[-1] = 0;
 | |
|   				break;
 | |
| 			}
 | |
| 		} while((*p1++ = *p2++));
 | |
| 
 | |
| 		spend = p1-1;
 | |
| 		break;
 | |
| 
 | |
| 	case HCMD:		/* copy pattern space to hold space */
 | |
| 		p1 = holdsp;	p2 = linebuf;	while((*p1++ = *p2++));
 | |
| 		hspend = p1-1;
 | |
| 		break;
 | |
| 
 | |
| 	case CHCMD:		/* append pattern space to hold space */
 | |
| 		*hspend++ = '\n';
 | |
| 		p1 = hspend;	p2 = linebuf;
 | |
| 		do {
 | |
| 			if (p1 > holdsp + MAXBUF) {
 | |
| 				truncated(TRUE);
 | |
| 				p1[-1] = 0;
 | |
|   				break;
 | |
| 			}
 | |
| 		} while((*p1++ = *p2++));
 | |
| 
 | |
| 		hspend = p1-1;
 | |
| 		break;
 | |
| 
 | |
| 	case ICMD:		/* insert text */
 | |
| 		printf("%s\n", ipc->u.lhs);
 | |
| 		break;
 | |
| 
 | |
| 	case BCMD:		/* branch to label */
 | |
| 		jump = TRUE;
 | |
| 		break;
 | |
| 
 | |
| 	case LCMD:		/* list text */
 | |
| 		listto(linebuf, (ipc->fout != NULL)?ipc->fout:stdout); break;
 | |
| 
 | |
| 	case CLCMD:		/* dump text */
 | |
| 		dumpto(linebuf, (ipc->fout != NULL)?ipc->fout:stdout); break;
 | |
| 
 | |
| 	case NCMD:		/* read next line into pattern space */
 | |
| 		if (!nflag)
 | |
| 			puts(linebuf);	/* flush out the current line */
 | |
| 		if (aptr > appends)
 | |
| 			readout();	/* do pending a, r commands */
 | |
| 		if ((execp = getline(linebuf, MAXBUF+1)) == BAD)
 | |
| 		{
 | |
| 			pending = ipc;
 | |
| 			delete = TRUE;
 | |
| 			break;
 | |
| 		}
 | |
| 		spend = execp;
 | |
| 		break;
 | |
| 
 | |
| 	case CNCMD:		/* append next line to pattern space */
 | |
| 		if (aptr > appends)
 | |
| 			readout();
 | |
| 		*spend++ = '\n';
 | |
| 		if ((execp = getline(spend,
 | |
| 		                     linebuf + MAXBUF+1 - spend)) == BAD)
 | |
| 		{
 | |
| 			pending = ipc;
 | |
| 			delete = TRUE;
 | |
| 			break;
 | |
| 		}
 | |
| 		spend = execp;
 | |
| 		break;
 | |
| 
 | |
| 	case PCMD:		/* print pattern space */
 | |
| 		puts(linebuf);
 | |
| 		break;
 | |
| 
 | |
| 	case CPCMD:		/* print one line from pattern space */
 | |
| 		cpcom:		/* so s command can jump here */
 | |
| 		for(p1 = linebuf; *p1 != '\n' && *p1 != '\0'; )
 | |
| 			putc(*p1++, stdout);
 | |
| 		putc('\n', stdout);
 | |
| 		break;
 | |
| 
 | |
| 	case QCMD:		/* quit the stream editor */
 | |
| 		if (!nflag)
 | |
| 			puts(linebuf);	/* flush out the current line */
 | |
| 		if (aptr > appends)
 | |
| 			readout();	/* do any pending a and r commands */
 | |
| 		exit(0);
 | |
| 
 | |
| 	case RCMD:		/* read a file into the stream */
 | |
| 		*aptr++ = ipc;
 | |
| 		if (aptr >= appends + MAXAPPENDS)
 | |
| 			fprintf(stderr,
 | |
| 				"sed: too many reads after line %ld\n",
 | |
| 				lnum);
 | |
| 		*aptr = 0;
 | |
| 		break;
 | |
| 
 | |
| 	case SCMD:		/* substitute RE */
 | |
| 		didsub = substitute(ipc);
 | |
| 		if (ipc->flags.print && didsub)
 | |
| 		{
 | |
| 			if (ipc->flags.print == TRUE)
 | |
| 				puts(linebuf);
 | |
| 			else
 | |
| 				goto cpcom;
 | |
| 		}
 | |
| 		if (didsub && ipc->fout)
 | |
| 			fprintf(ipc->fout, "%s\n", linebuf);
 | |
| 		break;
 | |
| 
 | |
| 	case TCMD:		/* branch on last s successful */
 | |
| 	case CTCMD:		/* branch on last s failed */
 | |
| 		if (didsub == (ipc->command == CTCMD))
 | |
| 			break;		/* no branch if last s failed, else */
 | |
| 		didsub = FALSE;
 | |
| 		jump = TRUE;		/*  set up to jump to assoc'd label */
 | |
| 		break;
 | |
| 
 | |
| 	case CWCMD:		/* write one line from pattern space */
 | |
| 		for(p1 = linebuf; *p1 != '\n' && *p1 != '\0'; )
 | |
| 			putc(*p1++, ipc->fout);
 | |
| 		putc('\n', ipc->fout);
 | |
| 		break;
 | |
| 
 | |
| 	case WCMD:		/* write pattern space to file */
 | |
| 		fprintf(ipc->fout, "%s\n", linebuf);
 | |
| 		break;
 | |
| 
 | |
| 	case XCMD:		/* exchange pattern and hold spaces */
 | |
| 		p1 = linebuf;	p2 = genbuf;	while((*p2++ = *p1++)) continue;
 | |
| 		p1 = holdsp;	p2 = linebuf;	while((*p2++ = *p1++)) continue;
 | |
| 		spend = p2 - 1;
 | |
| 		p1 = genbuf;	p2 = holdsp;	while((*p2++ = *p1++)) continue;
 | |
| 		hspend = p2 - 1;
 | |
| 		break;
 | |
| 
 | |
| 	case YCMD:
 | |
| 		p1 = linebuf;	p2 = ipc->u.lhs;
 | |
| 		while((*p1 = p2[(unsigned char)*p1]))
 | |
| 			p1++;
 | |
| 		break;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* get next line of text to be filtered
 | |
|    buf: where to send the input
 | |
|    max: max chars to read */
 | |
| static char *getline(char *buf, int max)
 | |
| {
 | |
| 	if (fgets(buf, max, stdin) != NULL)
 | |
| 	{
 | |
| 		int c;
 | |
| 
 | |
| 		lnum++;			/* note that we got another line */
 | |
| 		/* find the end of the input and overwrite a possible '\n' */
 | |
| 		while (*buf != '\n' && *buf != 0)
 | |
| 		    buf++;
 | |
| 		line_with_newline = *buf == '\n';
 | |
| 		*buf=0;
 | |
| 
 | |
| 		/* detect last line - but only if the address was used in a command */
 | |
| 		if  (last_line_used) {
 | |
| 		  if ((c = fgetc(stdin)) != EOF)
 | |
| 			ungetc (c, stdin);
 | |
| 		  else {
 | |
| 			if (eargc == 0)		/* if no more args */
 | |
| 				lastline = TRUE;	/* set a flag */
 | |
| 		  }
 | |
| 		}
 | |
| 
 | |
| 		return(buf);		/* return ptr to terminating null */ 
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		return(BAD);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* write file indicated by r command to output */
 | |
| static void readout(void)
 | |
| {
 | |
| 	register int	t;	/* hold input char or EOF */
 | |
| 	FILE		*fi;	/* ptr to file to be read */
 | |
| 
 | |
| 	aptr = appends - 1;	/* arrange for pre-increment to work right */
 | |
| 	while(*++aptr)
 | |
| 		if ((*aptr)->command == ACMD)		/* process "a" cmd */
 | |
| 			printf("%s\n", (*aptr)->u.lhs);
 | |
| 		else					/* process "r" cmd */
 | |
| 		{
 | |
| 			if ((fi = fopen((*aptr)->u.lhs, "r")) == NULL)
 | |
| 				continue;
 | |
| 			while((t = getc(fi)) != EOF)
 | |
| 				putc((char) t, stdout);
 | |
| 			fclose(fi);
 | |
| 		}
 | |
| 	aptr = appends;		/* reset the append ptr */
 | |
| 	*aptr = 0;
 | |
| }
 | |
| 
 | |
| /* sedexec.c ends here */
 | 
