1 /* sedexec.c -- axecute compiled form of stream editor commands
2    Copyright (C) 1995-2003 Eric S. Raymond
3    Copyright (C) 2004-2014 Rene Rebe
4 
5    The single entry point of this module is the function execute(). It
6 may take a string argument (the name of a file to be used as text)  or
7 the argument NULL which tells it to filter standard input. It executes
8 the compiled commands in cmds[] on each line in turn.
9    The function command() does most of the work.  match() and advance()
10 are used for matching text against precompiled regular expressions and
11 dosub() does right-hand-side substitution.  Getline() does text input;
12 readout() and memcmp() are output and string-comparison utilities.
13 */
14 
15 #include <stdlib.h>	/* exit */
16 #include <stdio.h>	/* {f}puts, {f}printf, getc/putc, f{re}open, fclose */
17 #include <ctype.h>	/* for isprint(), isdigit(), toascii() macros */
18 #include <string.h>	/* for memcmp(3) */
19 #include "sed.h"	/* command type structures & miscellaneous constants */
20 
21 /***** shared variables imported from the main ******/
22 
23 /* main data areas */
24 extern char	linebuf[];	/* current-line buffer */
25 extern sedcmd	cmds[];		/* hold compiled commands */
26 extern long	linenum[];	/* numeric-addresses table */
27 
28 /* miscellaneous shared variables */
29 extern int	nflag;		/* -n option flag */
30 extern int	eargc;		/* scratch copy of argument count */
31 extern sedcmd	*pending;	/* ptr to command waiting to be executed */
32 
33 extern int	last_line_used; /* last line address ($) used */
34 
35 /***** end of imported stuff *****/
36 
37 #define MAXHOLD		MAXBUF	/* size of the hold space */
38 #define GENSIZ		MAXBUF	/* maximum genbuf size */
39 
40 static const char LTLMSG[]	= "sed: line too long\n";
41 
42 static char	*spend;		/* current end-of-line-buffer pointer */
43 static long	lnum = 0L;	/* current source line number */
44 
45 /* append buffer maintenance */
46 static sedcmd	*appends[MAXAPPENDS];	/* array of ptrs to a,i,c commands */
47 static sedcmd	**aptr = appends;	/* ptr to current append */
48 
49 /* genbuf and its pointers */
50 static char	genbuf[GENSIZ];
51 static char	*loc1;
52 static char	*loc2;
53 static char	*locs;
54 
55 /* command-logic flags */
56 static int	lastline;		/* do-line flag */
57 static int	line_with_newline;	/* line had newline */
58 static int	jump;			/* jump to cmd's link address if set */
59 static int	delete;			/* delete command flag */
60 
61 /* tagged-pattern tracking */
62 static char	*bracend[MAXTAGS];	/* tagged pattern start pointers */
63 static char	*brastart[MAXTAGS];	/* tagged pattern end pointers */
64 
65 /* prototypes */
66 static char *sed_getline(char *buf, int max);
67 static char *place(char* asp, char* al1, char* al2);
68 static int advance(char* lp, char* ep, char** eob);
69 static int match(char *expbuf, int gf);
70 static int selected(sedcmd *ipc);
71 static int substitute(sedcmd *ipc);
72 static void command(sedcmd *ipc);
73 static void dosub(char *rhsbuf);
74 static void dumpto(char *p1, FILE *fp);
75 static void listto(char *p1, FILE *fp);
76 static void readout(void);
77 static void truncated(int h);
78 
79 /* execute the compiled commands in cmds[] on a file
80    file:  name of text source file to be filtered */
execute(char * file)81 void execute(char* file)
82 {
83 	register sedcmd		*ipc;		/* ptr to current command */
84 	char			*execp;		/* ptr to source */
85 
86 	if (file != NULL)	/* filter text from a named file */
87 		if (freopen(file, "r", stdin) == NULL)
88 			fprintf(stderr, "sed: can't open %s\n", file);
89 
90 	if (pending)		/* there's a command waiting */
91 	{
92 		ipc = pending;		/* it will be first executed */
93 		pending = FALSE;	/*   turn off the waiting flag */
94 		goto doit;		/*   go to execute it immediately */
95 	}
96 
97 	/* here's the main command-execution loop */
98 	for(;;)
99 	{
100 		/* get next line to filter */
101 		if ((execp = sed_getline(linebuf, MAXBUF+1)) == BAD)
102 			return;
103 		spend = execp;
104 
105 		/* loop through compiled commands, executing them */
106 		for(ipc = cmds; ipc->command; )
107 		{
108 			/* address command to select? - If not address
109 			   but allbut then invert, that is skip, the commmand */
110 			if (ipc->addr1 || ipc->flags.allbut) {
111 				if (!ipc->addr1 || !selected(ipc)) {
112 					ipc++;	/* not selected, next cmd */
113 					continue;
114 				}
115 			}
116 	doit:
117 			command(ipc);	/* execute the command pointed at */
118 
119 			if (delete)	/* if delete flag is set */
120 				break;	/* don't exec rest of compiled cmds */
121 
122 			if (jump)	/* if jump set, follow cmd's link */
123 			{
124 				jump = FALSE;
125 				if ((ipc = ipc->u.link) == 0)
126 				{
127 					ipc = cmds;
128 					break;
129 				}
130 			}
131 			else		/* normal goto next command */
132 				ipc++;
133 		}
134 		/* we've now done all modification commands on the line */
135 
136 		/* here's where the transformed line is output */
137 		if (!nflag && !delete)
138 		{
139 			fwrite(linebuf, spend - linebuf, 1, stdout);
140 			if (line_with_newline)
141 				putc('\n', stdout);
142 		}
143 
144 		/* if we've been set up for append, emit the text from it */
145 		if (aptr > appends)
146 			readout();
147 
148 		delete = FALSE;	/* clear delete flag; about to get next cmd */
149 	}
150 }
151 
152 /* is current command selected */
selected(sedcmd * ipc)153 static int selected(sedcmd *ipc)
154 {
155 	register char	*p1 = ipc->addr1;	/* point p1 at first address */
156 	register char	*p2 = ipc->addr2;	/*   and p2 at second */
157 	unsigned char	c;
158 	int selected = FALSE;
159 
160 	if (ipc->flags.inrange)
161 	{
162 		selected = TRUE;
163 		if (*p2 == CEND)
164 			;
165 		else if (*p2 == CLNUM)
166 		{
167 			c = p2[1];
168 			if (lnum >= linenum[c])
169 				ipc->flags.inrange = FALSE;
170 		}
171 		else if (match(p2, 0))
172 			ipc->flags.inrange = FALSE;
173 	}
174 	else if (*p1 == CEND)
175 	{
176 		if (lastline)
177 			selected = TRUE;
178 	}
179 	else if (*p1 == CLNUM)
180 	{
181 		c = p1[1];
182 		if (lnum == linenum[c]) {
183 			selected = TRUE;
184 			if (p2)
185 				ipc->flags.inrange = TRUE;
186 		}
187 	}
188 	else if (match(p1, 0))
189 	{
190 		selected = TRUE;
191 		if (p2)
192 			ipc->flags.inrange = TRUE;
193 	}
194 	return ipc->flags.allbut ? !selected : selected;
195 }
196 
197 /* match RE at expbuf against linebuf; if gf set, copy linebuf from genbuf */
_match(char * expbuf,int gf)198 static int _match(char *expbuf, int gf)	/* uses genbuf */
199 {
200 	char *p1, *p2, c;
201 
202 	if (!gf)
203 	{
204 		p1 = linebuf;
205 		locs = NULL;
206 	}
207 	else
208 	{
209 		if (*expbuf)
210 			return FALSE;
211 		/* if the last match was zero length, continue to next */
212 		if (loc2 - loc1 == 0) {
213 			loc2++;
214 		}
215 		locs = p1 = loc2;
216 	}
217 
218 	p2 = expbuf;
219 	if (*p2++)
220 	{
221 		loc1 = p1;
222 		if (*p2 == CCHR && p2[1] != *p1)	/* 1st char is wrong */
223 			return FALSE;		/*   so fail */
224 		return advance(p1, p2, NULL);	/* else try to match rest */
225 	}
226 
227 	/* quick check for 1st character if it's literal */
228 	if (*p2 == CCHR)
229 	{
230 		c = p2[1];		/* pull out character to search for */
231 		do {
232 			if (*p1 != c)
233 				continue;	/* scan the source string */
234 			if (advance(p1, p2, NULL)) /* found it, match the rest */
235 				return loc1 = p1, TRUE;
236 		} while (*p1++);
237 		return FALSE;		/* didn't find that first char */
238 	}
239 
240 	/* else try for unanchored match of the pattern */
241 	do {
242 		if (advance(p1, p2, NULL))
243 			return loc1 = p1, TRUE;
244 	} while (*p1++);
245 
246 	/* if got here, didn't match either way */
247 	return FALSE;
248 }
249 
match(char * expbuf,int gf)250 static int match(char *expbuf, int gf)	/* uses genbuf */
251 {
252 	const char *loc2i = loc2;
253 	const int ret = _match(expbuf, gf);
254 
255 	/* if last match was zero length, do not allow a follpwing zero match */
256 	if (loc2i && loc1 == loc2i && loc2 - loc1 == 0) {
257 		loc2++;
258 		return _match(expbuf, gf);
259 	}
260 	return ret;
261 }
262 
263 /* attempt to advance match pointer by one pattern element
264    lp:	source (linebuf) ptr
265    ep:	regular expression element ptr */
advance(char * lp,char * ep,char ** eob)266 static int advance(char* lp, char* ep, char** eob)
267 {
268 	char	*curlp;		/* save ptr for closures */
269 	char	c;		/* scratch character holder */
270 	char	*bbeg;
271 	int	ct;
272 	signed int	bcount = -1;
273 
274 	for (;;)
275 		switch (*ep++)
276 		{
277 		case CCHR:		/* literal character */
278 			if (*ep++ == *lp++)	/* if chars are equal */
279 				continue;	/* matched */
280 			return FALSE;		/* else return false */
281 
282 		case CDOT:		/* anything but newline */
283 			if (*lp++)		/* first NUL is at EOL */
284 				continue;	/* keep going if didn't find */
285 			return FALSE;		/* else return false */
286 
287 		case CNL:		/* start-of-line */
288 		case CDOL:		/* end-of-line */
289 			if (*lp == 0)		/* found that first NUL? */
290 				continue;	/* yes, keep going */
291 			return FALSE;		/* else return false */
292 
293 		case CEOF:		/* end-of-address mark */
294 			loc2 = lp;		/* set second loc */
295 			return TRUE;		/* return true */
296 
297 		case CCL:		/* a closure */
298 			c = *lp++ & 0177;
299 			if (ep[c>>3] & bits(c & 07))	/* is char in set? */
300 			{
301 				ep += 16;	/* then skip rest of bitmask */
302 				continue;	/*   and keep going */
303 			}
304 			return FALSE;		/* else return false */
305 
306 		case CBRA:		/* start of tagged pattern */
307 			brastart[(unsigned char)*ep++] = lp;	/* mark it */
308 			continue;		/* and go */
309 
310 		case CKET:		/* end of tagged pattern */
311 			bcount = *ep;
312 			if (eob) {
313 				*eob = lp;
314 				return TRUE;
315 			}
316 			else
317 				bracend[(unsigned char)*ep++] = lp;    /* mark it */
318 			continue;		/* and go */
319 
320 		case CBACK:		/* match back reference */
321 			bbeg = brastart[(unsigned char)*ep];
322 			ct = bracend[(unsigned char)*ep++] - bbeg;
323 
324 			if (memcmp(bbeg, lp, ct) == 0)
325 			{
326 				lp += ct;
327 				continue;
328 			}
329 			return FALSE;
330 
331 		case CBRA|STAR:		/* \(...\)* */
332 		{
333 			char *lastlp;
334 			curlp = lp;
335 
336 			if (*ep > bcount)
337 				brastart[(unsigned char)*ep] = bracend[(unsigned char)*ep] = lp;
338 
339 			while (advance(lastlp=lp, ep+1, &lp)) {
340 				if (*ep > bcount && lp != lastlp) {
341 					bracend[(unsigned char)*ep] = lp;    /* mark it */
342 					brastart[(unsigned char)*ep] = lastlp;
343 				}
344 				if (lp == lastlp) break;
345 			}
346 			ep++;
347 
348 			/* FIXME: scan for the brace end */
349 			while (*ep != CKET)
350 				ep++;
351 			ep+=2;
352 
353 			if (lp == curlp) /* 0 matches */
354 				continue;
355 			lp++; /* because the star handling decrements it */
356 			goto star;
357 		}
358 		case CBACK|STAR:	/* \n* */
359 			bbeg = brastart[(unsigned char)*ep];
360 			ct = bracend[(unsigned char)*ep++] - bbeg;
361 			curlp = lp;
362 			while(memcmp(bbeg, lp, ct) == 0)
363 				lp += ct;
364 
365 			while(lp >= curlp)
366 			{
367 				if (advance(lp, ep, eob))
368 					return TRUE;
369 				lp -= ct;
370 			}
371 			return FALSE;
372 
373 		case CDOT|STAR:		/* match .* */
374 			curlp = lp;		/* save closure start loc */
375 			while (*lp++);		/* match anything */
376 			goto star;		/* now look for followers */
377 
378 		case CCHR|STAR:		/* match <literal char>* */
379 			curlp = lp;		/* save closure start loc */
380 			while (*lp++ == *ep);	/* match many of that char */
381 			ep++;			/* to start of next element */
382 			goto star;		/* match it and followers */
383 
384 		case CCL|STAR:		/* match [...]* */
385 			curlp = lp;		/* save closure start loc */
386 			do {
387 				c = *lp++ & 0x7F;	/* match any in set */
388 			} while (ep[c>>3] & bits(c & 07));
389 			ep += 16;		/* skip past the set */
390 			goto star;		/* match followers */
391 
392 		star:		/* the recursion part of a * or + match */
393 			if (--lp == curlp) {	/* 0 matches */
394 				continue;
395 			}
396 #if 0
397 			if (*ep == CCHR)
398 			{
399 				c = ep[1];
400 				do {
401 					if (*lp != c)
402 						continue;
403 					if (advance(lp, ep, eob))
404 						return TRUE;
405 				} while (lp-- > curlp);
406 				return FALSE;
407 			}
408 
409 			if (*ep == CBACK)
410 			{
411 				c = *(brastart[ep[1]]);
412 				do {
413 					if (*lp != c)
414 						continue;
415 					if (advance(lp, ep, eob))
416 						return TRUE;
417 				} while (lp-- > curlp);
418 				return FALSE;
419 			}
420 #endif
421 			/* match followers, try shorter match, if needed */
422 			do {
423 				if (lp == locs)
424 					break;
425 				if (advance(lp, ep, eob))
426 					return TRUE;
427 			} while (lp-- > curlp);
428 			return FALSE;
429 
430 		default:
431 			fprintf(stderr, "sed: internal RE error, %o\n", *--ep);
432 			exit (2);
433 		}
434 }
435 
436 /* perform s command
437    ipc:	ptr to s command struct */
substitute(sedcmd * ipc)438 static int substitute(sedcmd *ipc)
439 {
440 	unsigned int n = 0;
441 	/* find a match */
442 	while (match(ipc->u.lhs, n /* use last loc2 for n>0 */)) {
443 		/* nth 0 is implied 1 */
444 		n++;
445 		if (!ipc->nth || n == ipc->nth) {
446 			dosub(ipc->rhs);		/* perform it once */
447 			break;
448 		}
449 	}
450 	if (n == 0)
451 		return FALSE;			/* command fails */
452 
453 	if (ipc->flags.global)			/* if global flag enabled */
454 		do {				/* cycle through possibles */
455 			if (match(ipc->u.lhs, 1)) {	/* found another */
456 				dosub(ipc->rhs);	/* so substitute */
457 			}
458 			else				/* otherwise, */
459 				break;			/* we're done */
460 		} while (*loc2);
461 	return TRUE;				/* we succeeded */
462 }
463 
464 /* generate substituted right-hand side (of s command)
465    rhsbuf:	where to put the result */
dosub(char * rhsbuf)466 static void dosub(char *rhsbuf)		/* uses linebuf, genbuf, spend */
467 {
468 	char	*lp, *sp, *rp;
469 	int	c;
470 
471 	/* copy linebuf to genbuf up to location 1 */
472 	lp = linebuf; sp = genbuf;
473 	while (lp < loc1) *sp++ = *lp++;
474 
475 	/* substitute */
476 	for (rp = rhsbuf; (c = *rp++); )
477 	{
478 		if (c & 0200 && (c & 0177) == '0')
479 		{
480 			sp = place(sp, loc1, loc2);
481 			continue;
482 		}
483 		else if (c & 0200 && (c &= 0177) >= '1' && c < MAXTAGS+'1')
484 		{
485 			sp = place(sp, brastart[c-'1'], bracend[c-'1']);
486 			continue;
487 		}
488 		*sp++ = c & 0177;
489 		if (sp >= genbuf + MAXBUF)
490 			fprintf(stderr, LTLMSG);
491 
492 	}
493 
494 	/* adjust location pointers and copy reminder */
495 	lp = loc2;
496 	{
497 		long len = loc2 - loc1;
498 		loc2 = sp - genbuf + linebuf;
499 		loc1 = loc2 - len;
500 	}
501 	while ((*sp++ = *lp++))
502 		if (sp >= genbuf + MAXBUF)
503 			fprintf(stderr, LTLMSG);
504 	lp = linebuf; sp = genbuf;
505 	while ((*lp++ = *sp++));
506 	spend = lp-1;
507 }
508 
509 /* place chars at *al1...*(al1 - 1) at asp... in genbuf[] */
place(char * asp,char * al1,char * al2)510 static char *place(char* asp, char* al1, char* al2)		/* uses genbuf */
511 {
512 	while (al1 < al2)
513 	{
514 		*asp++ = *al1++;
515 		if (asp >= genbuf + MAXBUF)
516 			fprintf(stderr, LTLMSG);
517 	}
518 	return asp;
519 }
520 
521 /* list the pattern space in visually unambiguous form *p1... to fp
522    p1: the source
523    fp: output stream to write to */
listto(char * p1,FILE * fp)524 static void listto(char *p1, FILE *fp)
525 {
526 	for (; p1<spend; p1++)
527 		if (isprint(*p1))
528 			putc(*p1, fp);		/* pass it through */
529 		else
530 		{
531 			putc('\\', fp);		/* emit a backslash */
532 			switch(*p1)
533 			{
534 			case '\b':	putc('b', fp); break;	/* BS */
535 			case '\t':	putc('t', fp); break;	/* TAB */
536 			case '\n':	putc('n', fp); break;	/* NL */
537 			case '\r':	putc('r', fp); break;	/* CR */
538 			case '\033':	putc('e', fp); break;	/* ESC */
539 			default:	fprintf(fp, "%02x", *p1);
540 			}
541 		}
542 	putc('\n', fp);
543 }
544 
545 /* write a hex dump expansion of *p1... to fp
546    p1: source
547    fp: output */
dumpto(char * p1,FILE * fp)548 static void dumpto(char *p1, FILE *fp)
549 {
550 	for (; p1<spend; p1++)
551 		fprintf(fp, "%02x", *p1);
552 	fprintf(fp, "%02x", '\n');
553 	putc('\n', fp);
554 }
555 
truncated(int h)556 static void truncated(int h)
557 {
558 	static long last = 0L;
559 
560 	if (lnum == last) return;
561 	last = lnum;
562 
563 	fprintf(stderr, "sed: ");
564 	fprintf(stderr, h ? "hold space" : "line %ld", lnum);
565 	fprintf(stderr, " truncated to %d characters\n", MAXBUF);
566 }
567 
568 /* execute compiled command pointed at by ipc */
command(sedcmd * ipc)569 static void command(sedcmd *ipc)
570 {
571 	static int	didsub;			/* true if last s succeeded */
572 	static char	holdsp[MAXHOLD];	/* the hold space */
573 	static char	*hspend = holdsp;	/* hold space end pointer */
574 	register char	*p1, *p2;
575 	char		*execp;
576 
577 	switch(ipc->command)
578 	{
579 	case ACMD:		/* append */
580 		*aptr++ = ipc;
581 		if (aptr >= appends + MAXAPPENDS)
582 			fprintf(stderr,
583 				"sed: too many appends after line %ld\n",
584 				lnum);
585 		*aptr = 0;
586 		break;
587 
588 	case CCMD:		/* change pattern space */
589 		delete = TRUE;
590 		if (!ipc->flags.inrange || lastline)
591 			printf("%s\n", ipc->u.lhs);
592 		break;
593 
594 	case DCMD:		/* delete pattern space */
595 		delete = TRUE;
596 		break;
597 
598 	case CDCMD:		/* delete a line in hold space */
599 		p1 = p2 = linebuf;
600 		while(*p1 != '\n')
601 			if ((delete = (*p1++ == 0)))
602 				return;
603 		p1++;
604 		while((*p2++ = *p1++)) continue;
605 		spend = p2-1;
606 		jump++;
607 		break;
608 
609 	case EQCMD:		/* show current line number */
610 		fprintf(stdout, "%ld\n", lnum);
611 		break;
612 
613 	case GCMD:		/* copy hold space to pattern space */
614 		p1 = linebuf;	p2 = holdsp;	while((*p1++ = *p2++));
615 		spend = p1-1;
616 		break;
617 
618 	case CGCMD:		/* append hold space to pattern space */
619 		*spend++ = '\n';
620 		p1 = spend;	p2 = holdsp;
621 		do {
622 			if (p1 > linebuf + MAXBUF) {
623 				truncated(FALSE);
624 				p1[-1] = 0;
625   				break;
626 			}
627 		} while((*p1++ = *p2++));
628 
629 		spend = p1-1;
630 		break;
631 
632 	case HCMD:		/* copy pattern space to hold space */
633 		p1 = holdsp;	p2 = linebuf;	while((*p1++ = *p2++));
634 		hspend = p1-1;
635 		break;
636 
637 	case CHCMD:		/* append pattern space to hold space */
638 		*hspend++ = '\n';
639 		p1 = hspend;	p2 = linebuf;
640 		do {
641 			if (p1 > holdsp + MAXBUF) {
642 				truncated(TRUE);
643 				p1[-1] = 0;
644   				break;
645 			}
646 		} while((*p1++ = *p2++));
647 
648 		hspend = p1-1;
649 		break;
650 
651 	case ICMD:		/* insert text */
652 		printf("%s\n", ipc->u.lhs);
653 		break;
654 
655 	case BCMD:		/* branch to label */
656 		jump = TRUE;
657 		break;
658 
659 	case LCMD:		/* list text */
660 		listto(linebuf, (ipc->fout != NULL)?ipc->fout:stdout); break;
661 
662 	case CLCMD:		/* dump text */
663 		dumpto(linebuf, (ipc->fout != NULL)?ipc->fout:stdout); break;
664 
665 	case NCMD:		/* read next line into pattern space */
666 		if (!nflag)
667 			puts(linebuf);	/* flush out the current line */
668 		if (aptr > appends)
669 			readout();	/* do pending a, r commands */
670 		if ((execp = sed_getline(linebuf, MAXBUF+1)) == BAD)
671 		{
672 			pending = ipc;
673 			delete = TRUE;
674 			break;
675 		}
676 		spend = execp;
677 		break;
678 
679 	case CNCMD:		/* append next line to pattern space */
680 		if (aptr > appends)
681 			readout();
682 		*spend++ = '\n';
683 		if ((execp = sed_getline(spend,
684 		                         linebuf + MAXBUF+1 - spend)) == BAD)
685 		{
686 			pending = ipc;
687 			delete = TRUE;
688 			break;
689 		}
690 		spend = execp;
691 		break;
692 
693 	case PCMD:		/* print pattern space */
694 		puts(linebuf);
695 		break;
696 
697 	case CPCMD:		/* print one line from pattern space */
698 		cpcom:		/* so s command can jump here */
699 		for(p1 = linebuf; *p1 != '\n' && *p1 != '\0'; )
700 			putc(*p1++, stdout);
701 		putc('\n', stdout);
702 		break;
703 
704 	case QCMD:		/* quit the stream editor */
705 		if (!nflag)
706 			puts(linebuf);	/* flush out the current line */
707 		if (aptr > appends)
708 			readout();	/* do any pending a and r commands */
709 		exit(0);
710 
711 	case RCMD:		/* read a file into the stream */
712 		*aptr++ = ipc;
713 		if (aptr >= appends + MAXAPPENDS)
714 			fprintf(stderr,
715 				"sed: too many reads after line %ld\n",
716 				lnum);
717 		*aptr = 0;
718 		break;
719 
720 	case SCMD:		/* substitute RE */
721 		didsub = substitute(ipc);
722 		if (ipc->flags.print && didsub)
723 		{
724 			if (ipc->flags.print == TRUE)
725 				puts(linebuf);
726 			else
727 				goto cpcom;
728 		}
729 		if (didsub && ipc->fout)
730 			fprintf(ipc->fout, "%s\n", linebuf);
731 		break;
732 
733 	case TCMD:		/* branch on last s successful */
734 	case CTCMD:		/* branch on last s failed */
735 		if (didsub == (ipc->command == CTCMD))
736 			break;		/* no branch if last s failed, else */
737 		didsub = FALSE;
738 		jump = TRUE;		/*  set up to jump to assoc'd label */
739 		break;
740 
741 	case CWCMD:		/* write one line from pattern space */
742 		for(p1 = linebuf; *p1 != '\n' && *p1 != '\0'; )
743 			putc(*p1++, ipc->fout);
744 		putc('\n', ipc->fout);
745 		break;
746 
747 	case WCMD:		/* write pattern space to file */
748 		fprintf(ipc->fout, "%s\n", linebuf);
749 		break;
750 
751 	case XCMD:		/* exchange pattern and hold spaces */
752 		p1 = linebuf;	p2 = genbuf;	while((*p2++ = *p1++)) continue;
753 		p1 = holdsp;	p2 = linebuf;	while((*p2++ = *p1++)) continue;
754 		spend = p2 - 1;
755 		p1 = genbuf;	p2 = holdsp;	while((*p2++ = *p1++)) continue;
756 		hspend = p2 - 1;
757 		break;
758 
759 	case YCMD:
760 		p1 = linebuf;	p2 = ipc->u.lhs;
761 		while((*p1 = p2[(unsigned char)*p1]))
762 			p1++;
763 		break;
764 	}
765 }
766 
767 /* get next line of text to be filtered
768    buf: where to send the input
769    max: max chars to read */
sed_getline(char * buf,int max)770 static char *sed_getline(char *buf, int max)
771 {
772 	if (fgets(buf, max, stdin) != NULL)
773 	{
774 		int c;
775 
776 		lnum++;			/* note that we got another line */
777 		/* find the end of the input and overwrite a possible '\n' */
778 		while (*buf != '\n' && *buf != 0)
779 		    buf++;
780 		line_with_newline = *buf == '\n';
781 		*buf=0;
782 
783 		/* detect last line - but only if the address was used in a command */
784 		if  (last_line_used) {
785 		  if ((c = fgetc(stdin)) != EOF)
786 			ungetc (c, stdin);
787 		  else {
788 			if (eargc == 0)		/* if no more args */
789 				lastline = TRUE;	/* set a flag */
790 		  }
791 		}
792 
793 		return buf;		/* return ptr to terminating null */
794 	}
795 	else
796 	{
797 		return BAD;
798 	}
799 }
800 
801 /* write file indicated by r command to output */
readout(void)802 static void readout(void)
803 {
804 	register int	t;	/* hold input char or EOF */
805 	FILE		*fi;	/* ptr to file to be read */
806 
807 	aptr = appends - 1;	/* arrange for pre-increment to work right */
808 	while(*++aptr)
809 		if ((*aptr)->command == ACMD)		/* process "a" cmd */
810 			printf("%s\n", (*aptr)->u.lhs);
811 		else					/* process "r" cmd */
812 		{
813 			if ((fi = fopen((*aptr)->u.lhs, "r")) == NULL)
814 				continue;
815 			while((t = getc(fi)) != EOF)
816 				putc((char) t, stdout);
817 			fclose(fi);
818 		}
819 	aptr = appends;		/* reset the append ptr */
820 	*aptr = 0;
821 }
822 
823 /* sedexec.c ends here */
824