sedexec.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688
  1. /* sedexec.c -- execute compiled form of stream editor commands
  2. The single entry point of this module is the function execute(). It
  3. may take a string argument (the name of a file to be used as text) or
  4. the argument NULL which tells it to filter standard input. It executes
  5. the compiled commands in cmds[] on each line in turn.
  6. The function command() does most of the work. Match() and advance()
  7. are used for matching text against precompiled regular expressions and
  8. dosub() does right-hand-side substitution. Getline() does text input;
  9. readout() and memcmp() are output and string-comparison utilities.
  10. If your environment includes a memcmp() in the standard libraries,
  11. define the symbol MEMCMP to suppress the version given here, as yours
  12. is probably hacked to use whatever special string-compare instructions
  13. are available on your hardware and accordingly faster.
  14. ==== Written for the GNU programming environment by Eric S. Raymond ==== */
  15. #include <stdio.h> /* {f}puts, {f}printf, getc/putc, f{re}open, fclose */
  16. #include <ctype.h> /* for isprint(), isdigit(), toascii() macros */
  17. #include "sed.h" /* compiled-command structure and various constants */
  18. /* shared variables imported from sedcomp.c */
  19. extern char linebuf[]; /* current-line buffer */
  20. extern sedcmd cmds[]; /* hold compiled commands */
  21. extern long linenum[]; /* numeric-addresses table */
  22. extern int dflag; /* -d option flag */
  23. extern int nflag; /* -n option flag */
  24. extern int eargc; /* scratch copy of argument count */
  25. extern char bits[]; /* the bits table */
  26. /***** end of imported stuff *****/
  27. #define MAXHOLD MAXBUF /* size of the hold space */
  28. #define GENSIZ 71 /* maximum genbuf size */
  29. #define TRUE 1
  30. #define FALSE 0
  31. /* error messages */
  32. static char TLITL[] = "sed: line too long\n";
  33. static char COSTF[] = "sed: can't open text file %s\n";
  34. static char REBAD[] = "sed: regular expression (internal) error, %o\n";
  35. static char TMAPP[] = "sed: too many appends after line %ld\n";
  36. static char TMRDS[] = "sed: too many reads after line %ld\n";
  37. /* pointers and data areas used in RE interpretation */
  38. static char *tagend[MAXTAGS]; /* tagged pattern start pointers */
  39. static char *tagstart[MAXTAGS]; /* tagged pattern end pointers */
  40. static char genbuf[GENSIZ]; /* right-hand-sides expanded here */
  41. static char *loc1, *loc2, *locs; /* pointers to pattern-match locs */
  42. /* miscellaneous internals */
  43. static sedcmd *appends[MAXAPPENDS]; /* array of ptrs to a,i,c commands */
  44. static sedcmd **aptr = appends; /* ptr to current append */
  45. static long lnum = 0L; /* current source line number */
  46. static char *spend = linebuf; /* current end-of-line pointer */
  47. /* command-logic flags */
  48. static int lastline; /* do-line flag */
  49. static int jump; /* jump to cmd's link address if set */
  50. static int delete; /* delete command flag */
  51. void execute(file)
  52. /* execute the compiled commands in cmds[] on a file */
  53. char *file; /* name of text source file to be filtered */
  54. {
  55. register sedcmd *ipc; /* ptr to current command */
  56. static sedcmd *pending = NULL; /* next cmd to be executed */
  57. sedcmd *command(); /* main command executive */
  58. char *newspend; /* ptr to source, linebuf */
  59. char *getline(); /* input-getting functions */
  60. if (file != NULL) /* filter text from a named file */
  61. if (freopen(file, "r", stdin) == NULL)
  62. fprintf(stderr, COSTF, file);
  63. if (pending) /* there's a command waiting */
  64. {
  65. ipc = pending; /* it will be first executed */
  66. pending = NULL; /* clear the waiting ptr */
  67. goto doit; /* and go to execute it immediately */
  68. }
  69. /* here's the main command-execution loop */
  70. while ((newspend = getline(linebuf)) != BAD)
  71. {
  72. spend = newspend; /* update buffer-end ptr */
  73. /* loop through compiled commands, executing them */
  74. for(ipc = cmds; ipc->command; ipc++)
  75. {
  76. if (!selected(ipc))
  77. continue;
  78. /* execute the command pointed at */
  79. doit: pending = command(ipc);
  80. if (delete) /* if delete flag is set */
  81. break; /* don't exec rest of compiled cmds */
  82. if (jump) /* if jump set, follow cmd's link */
  83. {
  84. jump = FALSE;
  85. if ((ipc = ipc->u.link) == NULL)
  86. {
  87. ipc = cmds; /* restart commands */
  88. break;
  89. }
  90. else
  91. ipc--; /* so ipc++ won't screw us */
  92. }
  93. }
  94. /* we've now done all modification commands on the line */
  95. /* here's where the transformed line is output */
  96. if (!nflag && !delete)
  97. {
  98. for(newspend = linebuf; newspend < spend; newspend++)
  99. putc(*newspend, stdout);
  100. putc('\n', stdout);
  101. }
  102. /* if we've been set up for append, emit the text from it */
  103. if (aptr > appends)
  104. readout();
  105. delete = FALSE; /* clear delete flag; about to get next cmd */
  106. }
  107. }
  108. static int selected(ipc) /* uses lnum, linenum */
  109. /* return TRUE if the command is currently selected, FALSE otherwise */
  110. sedcmd *ipc;
  111. {
  112. register char *start_at = ipc->addr1; /* point p1 at first address */
  113. register char *end_at = ipc->addr2; /* and p2 at second */
  114. int c; /* scratch character holder */
  115. if (start_at)
  116. {
  117. if (ipc->flags.active) /* command is selected */
  118. {
  119. if (*end_at == CEND)
  120. start_at = NULL;
  121. else if (*end_at == CLNUM)
  122. {
  123. if (lnum > linenum[end_at[1]])
  124. {
  125. ipc->flags.active = FALSE;
  126. return(ipc->flags.allbut);
  127. }
  128. if (lnum == linenum[c])
  129. ipc->flags.active = FALSE;
  130. }
  131. else if (match(linebuf, end_at, FALSE))
  132. ipc->flags.active = FALSE;
  133. }
  134. else if (*start_at == CEND)
  135. {
  136. if (!lastline)
  137. return(ipc->flags.allbut);
  138. }
  139. else if (*start_at == CLNUM)
  140. {
  141. if (lnum != linenum[start_at[1]])
  142. return(ipc->flags.allbut);
  143. ipc->flags.active = (end_at != NULL);
  144. }
  145. else if (match(linebuf, start_at, FALSE))
  146. ipc->flags.active = (end_at != NULL);
  147. else
  148. return(ipc->flags.allbut);
  149. }
  150. /* skip selected command if flags.allbut is on */
  151. return (!ipc->flags.allbut);
  152. }
  153. static int match(lp, ep, gf) /* uses genbuf */
  154. /* match RE at ep... against lp...; if gf set, copy lp... from genbuf first */
  155. register char *lp, *ep;
  156. int gf;
  157. {
  158. char *scp;
  159. char c;
  160. if (gf)
  161. {
  162. if (*ep)
  163. return(FALSE);
  164. strcpy(lp, genbuf);
  165. locs = lp = loc2;
  166. }
  167. else
  168. locs = NULL;
  169. if (*ep++)
  170. {
  171. loc1 = lp;
  172. if(*ep == CCHR && ep[1] != *lp) /* 1st char is wrong */
  173. return(FALSE); /* so fail */
  174. return(advance(lp, ep)); /* else try to match rest */
  175. }
  176. /* quick check for 1st character if it's literal */
  177. if (*ep == CCHR)
  178. {
  179. c = ep[1]; /* pull out character to search for */
  180. do {
  181. if (*lp != c)
  182. continue; /* scan the source string */
  183. if (advance(lp, ep)) /* found it, match the rest */
  184. return(loc1 = lp, 1);
  185. } while
  186. (*lp++);
  187. return(FALSE); /* didn't find that first char */
  188. }
  189. /* else try for unanchored match of the pattern */
  190. do {
  191. if (advance(lp, ep))
  192. return(loc1 = lp, TRUE);
  193. } while
  194. (*lp++);
  195. /* if got here, didn't match either way */
  196. return(FALSE);
  197. }
  198. static int advance(lp, ep)
  199. /* attempt to advance match pointer by one pattern element */
  200. register char *lp; /* source (linebuf) ptr */
  201. register char *ep; /* regular expression element ptr */
  202. {
  203. register char *curlp; /* save ptr for closures */
  204. char c; /* scratch character holder */
  205. char *bbeg;
  206. int ct;
  207. for (;;)
  208. switch (*ep++)
  209. {
  210. case CCHR: /* literal character */
  211. if (*ep++ == *lp++) /* if chars are equal */
  212. continue; /* matched */
  213. return(FALSE); /* else return false */
  214. case CDOT: /* anything but newline */
  215. if (*lp++) /* first NUL is at EOL */
  216. continue; /* keep going if didn't find */
  217. return(FALSE); /* else return false */
  218. case CNL: /* start-of-line */
  219. case CDOL: /* end-of-line */
  220. if (*lp == 0) /* found that first NUL? */
  221. continue; /* yes, keep going */
  222. return(FALSE); /* else return false */
  223. case CEOF: /* end-of-address mark */
  224. loc2 = lp; /* set second loc */
  225. return(TRUE); /* return true */
  226. case CCL: /* a character class */
  227. c = *lp++ & 0177;
  228. if (ep[c>>3] & bits[c & 07]) /* is char in set? */
  229. {
  230. ep += 16; /* then skip rest of bitmask */
  231. continue; /* and keep going */
  232. }
  233. return(FALSE); /* else return false */
  234. case CBRA: /* start of tagged pattern */
  235. tagstart[*ep++] = lp; /* mark it */
  236. continue; /* and go */
  237. case CKET: /* end of tagged pattern */
  238. tagend[*ep++] = lp; /* mark it */
  239. continue; /* and go */
  240. case CBACK: /* a pattern tag reference */
  241. bbeg = tagstart[*ep]; /* find the start */
  242. ct = tagend[*ep++] - bbeg; /* and the length */
  243. if (memcmp(bbeg, lp, ct)) /* matching text */
  244. {
  245. lp += ct; /* yes, look past end */
  246. continue; /* and keep going */
  247. }
  248. return(FALSE); /* else match failed */
  249. case CBACK|STAR: /* pattern tag with Kleene star */
  250. bbeg = tagstart[*ep]; /* get start */
  251. ct = tagend[*ep++] - bbeg; /* and length */
  252. curlp = lp; /* save the location */
  253. while(memcmp(bbeg, lp, ct)) /* while we match */
  254. lp += ct; /* go to next one */
  255. while(lp >= curlp) /* while 1 match left */
  256. {
  257. if (advance(lp, ep)) /* try rest of RE */
  258. return(TRUE); /* if matched, O.K. */
  259. lp -= ct; /* else backtrack */
  260. }
  261. return(FALSE); /* no matches left, fail */
  262. case CDOT|STAR: /* match .* */
  263. curlp = lp; /* save closure start loc */
  264. while (*lp++); /* match anything */
  265. goto star; /* now look for followers */
  266. case CCHR|STAR: /* match <literal char>* */
  267. curlp = lp; /* save closure start loc */
  268. while (*lp++ == *ep); /* match many of that char */
  269. ep++; /* to start of next element */
  270. goto star; /* match it and followers */
  271. case CCL|STAR: /* match [...]* */
  272. curlp = lp; /* save closure start loc */
  273. do {
  274. c = *lp++ & 0x7F; /* match any in set */
  275. } while
  276. (ep[c>>3] & bits[c & 07]);
  277. ep += 16; /* skip past the set */
  278. goto star; /* match followers */
  279. star: /* the recursion part of a * or + match */
  280. if (--lp == curlp) /* 0 matches */
  281. continue;
  282. if (*ep == CCHR)
  283. {
  284. c = ep[1];
  285. do {
  286. if (*lp != c)
  287. continue;
  288. if (advance(lp, ep))
  289. return(TRUE);
  290. } while
  291. (lp-- > curlp);
  292. return(FALSE);
  293. }
  294. if (*ep == CBACK)
  295. {
  296. c = *(tagstart[ep[1]]);
  297. do {
  298. if (*lp != c)
  299. continue;
  300. if (advance(lp, ep))
  301. return(TRUE);
  302. } while
  303. (lp-- > curlp);
  304. return(FALSE);
  305. }
  306. do {
  307. if (lp == locs)
  308. break;
  309. if (advance(lp, ep))
  310. return(TRUE);
  311. } while
  312. (lp-- > curlp);
  313. return(FALSE);
  314. default:
  315. fprintf(stderr, REBAD, *--ep);
  316. }
  317. }
  318. static int substitute(lhs, rhs, gf) /* uses linebuf, spend */
  319. /* perform s command */
  320. char *lhs, *rhs; /* left and right sides of command */
  321. int gf; /* the global-substitute flag */
  322. {
  323. char *dosub(); /* for if we find a match */
  324. if (match(linebuf, lhs, FALSE)) /* if 1 match */
  325. spend = dosub(linebuf, rhs); /* perform substitution once */
  326. else
  327. return(FALSE); /* command fails */
  328. while(gf && match(linebuf, lhs, TRUE)) /* cycle through possibles */
  329. spend = dosub(linebuf, rhs); /* substitute each one */
  330. return(TRUE); /* we succeeded */
  331. }
  332. static char *dosub(linep, rhsbuf) /* uses genbuf, loc1, loc2 */
  333. /* generate substituted right-hand side (of s command) */
  334. char *linep; /* line buffer to substitute in */
  335. char *rhsbuf; /* where to put the result */
  336. {
  337. register char *lp, *sp, *rp;
  338. int c;
  339. char *place();
  340. /* copy linebuf to genbuf up to location 1 */
  341. sp = genbuf; lp = linep; while (lp < loc1) *sp++ = *lp++;
  342. /* insert substitute right-hand-side with tags expanded in genbuf */
  343. for (rp = rhsbuf; c = *rp++; )
  344. {
  345. if (c == '&')
  346. sp = place(sp, loc1, loc2);
  347. else if (c & TAGMARK && (c &= 0x7F) >= '1' && c < '1'+MAXTAGS)
  348. sp = place(sp, tagstart[c - '1'], tagend[c - '1']);
  349. else if (c & TAGMARK)
  350. fprintf(stderr, "sed: bad tag value %x\n", c);
  351. else
  352. *sp++ = toascii(c);
  353. if (sp >= genbuf + GENSIZ)
  354. fprintf(stderr, TLITL);
  355. }
  356. /* copy the part of the line after the substituted area */
  357. lp = loc2;
  358. loc2 = sp - genbuf + linep;
  359. while (*sp++ = *lp++)
  360. if (sp >= genbuf + GENSIZ)
  361. fprintf(stderr, TLITL);
  362. /* copy the substituted pattern from genbuf to linebuf */
  363. lp = linep; sp = genbuf; while (*lp++ = *sp++) continue;
  364. return(lp - 1);
  365. }
  366. static char *place(asp, al1, al2) /* uses genbuf */
  367. /* place chars at *al1...*(al1 - 1) at asp... in genbuf[] */
  368. register char *asp, *al1, *al2;
  369. {
  370. while (al1 < al2 && asp < genbuf + GENSIZ)
  371. *asp++ = *al1++;
  372. return(asp);
  373. }
  374. static void listto(p1, fp)
  375. /* write a hex dump expansion of *p1... to fp */
  376. register char *p1; /* the source */
  377. FILE *fp; /* output stream to write to */
  378. {
  379. p1--;
  380. while(*p1++)
  381. if (isprint(*p1))
  382. putc(*p1, fp); /* pass it through */
  383. else
  384. {
  385. putc('\134', fp); /* emit a backslash */
  386. switch(*p1)
  387. {
  388. case '\10': putc('b', fp); break; /* BS */
  389. case '\09': putc('t', fp); break; /* TAB */
  390. case '\12': putc('n', fp); break; /* NL */
  391. case '\15': putc('r', fp); break; /* CR */
  392. case '\33': putc('e', fp); break; /* ESC */
  393. default: fprintf(fp, "%02x", *p1 & 0xFF);
  394. }
  395. }
  396. putc('\n', fp);
  397. }
  398. static void dumpline(ipc)
  399. /* execute p, P, w, W commands and options */
  400. sedcmd *ipc;
  401. {
  402. register char *p;
  403. if (ipc->flags.firstl)
  404. for(p = linebuf; *p != '\n' && *p != '\0'; )
  405. putc(*p++, ipc->fout);
  406. else
  407. fputs(linebuf, ipc->fout);
  408. putc('\n', ipc->fout);
  409. }
  410. static sedcmd *command(ipc)
  411. /* execute compiled command pointed at by ipc */
  412. sedcmd *ipc;
  413. {
  414. register char *p1, *p2; /* scratch pointers */
  415. static char holdsp[MAXHOLD]; /* the hold space */
  416. static char *hspend = holdsp; /* hold space end pointer */
  417. static int sflag; /* true if last s succeeded */
  418. char c, *newspend;
  419. if (dflag)
  420. fprintf(stderr,
  421. "sed: executing %x on arguments \"%s\" and \"%s\"\n",
  422. ipc->command, ipc->addr1, ipc->addr2);
  423. switch(ipc->command)
  424. {
  425. case ACMD: /* append */
  426. *aptr++ = ipc;
  427. if (aptr >= appends + MAXAPPENDS)
  428. fprintf(stderr, TMAPP, lnum);
  429. *aptr = 0;
  430. break;
  431. case CCMD: /* change pattern space */
  432. if (!ipc->flags.active || lastline)
  433. puts(ipc->u.lhs);
  434. case DCMD: /* delete pattern space */
  435. delete = TRUE;
  436. break;
  437. case CDCMD: /* delete a line in pattern space */
  438. p1 = p2 = linebuf;
  439. while(*p1 != '\n')
  440. if (delete = (*p1++ == 0))
  441. return;
  442. p1++;
  443. while(*p2++ = *p1++);
  444. spend = p2-1;
  445. break;
  446. case EQCMD: /* show current line number */
  447. fprintf(stdout, "%ld\n", lnum);
  448. break;
  449. case GCMD: /* copy hold space to pattern space */
  450. p1 = linebuf; p2 = holdsp; while (*p1++ = *p2++);
  451. spend = p1-1;
  452. break;
  453. case CGCMD: /* append hold space to pattern space */
  454. *spend++ = '\n';
  455. p1 = spend; p2 = holdsp;
  456. while(*p1++ = *p2++)
  457. if (p1 >= linebuf + MAXBUF)
  458. break;
  459. spend = p1-1;
  460. break;
  461. case HCMD: /* copy pattern space to hold space */
  462. p1 = holdsp; p2 = linebuf; while(*p1++ = *p2++);
  463. hspend = p1-1;
  464. break;
  465. case CHCMD: /* append pattern space to hold space */
  466. *hspend++ = '\n';
  467. p1 = hspend; p2 = linebuf;
  468. while(*p1++ = *p2++)
  469. if (p1 >= holdsp + MAXHOLD)
  470. break;
  471. hspend = p1-1;
  472. break;
  473. case ICMD: /* insert text */
  474. printf("%s\n", ipc->u.lhs);
  475. break;
  476. case LCMD: /* list text */
  477. listto(linebuf, (ipc->fout != NULL)?ipc->fout:stdout);
  478. break;
  479. case NCMD: /* read next line into pattern space */
  480. if (!nflag)
  481. puts(linebuf); /* flush out the current line */
  482. if (aptr > appends)
  483. readout(); /* do pending a, r commands */
  484. if ((newspend = getline(linebuf)) == BAD)
  485. return(delete = 1, ipc);
  486. spend = newspend;
  487. break;
  488. case CNCMD: /* append next line to pattern space */
  489. if (aptr > appends)
  490. readout();
  491. *spend++ = '\n';
  492. if ((newspend = getline(spend)) == BAD)
  493. return(delete = 1, ipc);
  494. spend = newspend;
  495. break;
  496. case PCMD: /* print pattern space */
  497. case CPCMD: /* print one line from pattern space */
  498. case WCMD: /* write pattern space to file */
  499. case CWCMD: /* write one line from pattern space */
  500. dumpline(ipc);
  501. break;
  502. case QCMD: /* quit the stream editor */
  503. if (!nflag)
  504. puts(linebuf); /* flush out the current line */
  505. if (aptr > appends)
  506. readout(); /* do any pending a and r commands */
  507. exit(0);
  508. case RCMD: /* read a file into the stream */
  509. *aptr++ = ipc;
  510. if (aptr >= appends + MAXAPPENDS)
  511. fprintf(stderr, TMRDS, lnum);
  512. *aptr = 0;
  513. break;
  514. case SCMD: /* substitute RE */
  515. sflag = substitute(ipc->u.lhs, ipc->rhs, ipc->flags.global);
  516. if (sflag)
  517. dumpline(ipc);
  518. break;
  519. case TCMD: /* branch on last s successful */
  520. case CTCMD: /* branch on last s failed */
  521. if (sflag == (ipc->command == CTCMD))
  522. break; /* no branch if last s failed, else */
  523. sflag = FALSE; /* clear the s condition flag */
  524. case BCMD: /* branch to label */
  525. jump = TRUE; /* set up to jump to assoc'd label */
  526. break;
  527. case XCMD: /* exchange pattern and hold spaces */
  528. p1 = linebuf, p2 = holdsp;
  529. while (p1 <= spend || p2 <= hspend)
  530. {
  531. c = *p1; *p1++ = *p2; *p2++ = c;
  532. }
  533. newspend = hspend; hspend = spend; spend = newspend;
  534. break;
  535. case YCMD: /* translate a line */
  536. p1 = linebuf; p2 = ipc->u.lhs;
  537. while(*p1 = p2[*p1])
  538. p1++;
  539. break;
  540. }
  541. return(NULL);
  542. }
  543. static char *getline(buf) /* uses lastline, eargc */
  544. /* get next line of text to be filtered */
  545. register char *buf; /* where to send the input */
  546. {
  547. if (gets(buf) != NULL)
  548. {
  549. lnum++; /* note that we got another line */
  550. while(*buf++); /* find the end of the input */
  551. return(--buf); /* return ptr to terminating null */
  552. }
  553. else
  554. {
  555. if (eargc == 0) /* if there are no more args */
  556. lastline = TRUE; /* set a flag */
  557. return(BAD);
  558. }
  559. }
  560. #ifndef MEMCMP
  561. static int memcmp(a, b, count)
  562. /* return 1 if *a... == *b... for count chars, 0 otherwise */
  563. register char *a, *b;
  564. {
  565. while(count--) /* look at count characters */
  566. if (*a++ != *b++) /* if any are nonequal */
  567. return(0); /* return 0 for false */
  568. return(TRUE); /* compare succeeded */
  569. }
  570. #endif MEMCMP
  571. static void readout() /* uses appends, aptr */
  572. /* write file indicated by r command to output */
  573. {
  574. register char *p1; /* character-fetching dummy */
  575. register int t; /* hold input char or EOF */
  576. FILE *fi; /* ptr to file to be read */
  577. aptr = appends - 1; /* arrange for pre-increment to work right */
  578. while(*++aptr)
  579. if ((*aptr)->command == ACMD) /* process "a" cmd */
  580. printf("%s\n", (*aptr)->u.lhs);
  581. else /* process "r" cmd */
  582. {
  583. if ((fi = fopen((*aptr)->u.lhs, "r")) == NULL)
  584. continue;
  585. while((t = getc(fi)) != EOF)
  586. putc((char) t, stdout);
  587. fclose(fi);
  588. }
  589. aptr = appends; /* reset the append ptr */
  590. *aptr = 0;
  591. }
  592. /* sedexec.c ends here */