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