koreth@ssyx.ucsc.edu (Steven Grimm) (02/25/89)
Submitted-by: saj@chinet.chi.il.us (Stephen Jacobs) Posting-number: Volume 2, Issue 16 Archive-name: sed/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: sedexec.c sedtest.bat # Wrapped by saj@chinet on Mon Jan 30 20:12:03 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f sedexec.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"sedexec.c\" else echo shar: Extracting \"sedexec.c\" \(28811 characters\) sed "s/^X//" >sedexec.c <<'END_OF_sedexec.c' X#if 0 /* SAJ */ X#include "compiler.h" X#ifdef LATTICE X#define void int X#endif X X#endif /* SAJ */ X#include "debug.h" X/* Xsedexec.c -- execute compiled form of stream editor commands X X The single entry point of this module is the function execute(). It Xmay take a string argument (the name of a file to be used as text) or Xthe argument NULL which tells it to filter standard input. It executes Xthe compiled commands in cmds[] on each line in turn. X The function command() does most of the work. Match() and advance() Xare used for matching text against precompiled regular expressions and Xdosub() does right-hand-side substitution. Getline() does text input; Xreadout() and memcmp() are output and string-comparison utilities. X X==== Written for the GNU operating system by Eric S. Raymond ==== X*/ X X#include <stdio.h> /* {f}puts, {f}printf, getc/putc, f{re}open, fclose */ X#include <ctype.h> /* for isprint(), isdigit(), toascii() macros */ X#include "sed.h" /* command type structures & miscellaneous constants */ X Xextern char *strcpy(); /* used in dosub */ X X/***** shared variables imported from the main ******/ X X/* main data areas */ Xextern char linebuf[]; /* current-line buffer */ Xextern sedcmd cmds[]; /* hold compiled commands */ Xextern long linenum[]; /* numeric-addresses table */ X X/* miscellaneous shared variables */ Xextern int nflag; /* -n option flag */ Xextern int eargc; /* scratch copy of argument count */ Xextern sedcmd *pending; /* ptr to command waiting to be executed */ Xextern char bits[]; /* the bits table */ X X/***** end of imported stuff *****/ X X#define MAXHOLD MAXBUF /* size of the hold space */ X#if 0 /* I had to leave this in. Is it a joke, or what? X *3 Hours single stepping a broken program isn't funny SAJ */ X#define GENSIZ 71 /* maximum genbuf size */ X#endif X#define GENSIZ 500 /* maximum genbuf size */ X#define TRUE 1 X#define FALSE 0 X Xstatic char LTLMSG[] = "sed: line too long\n"; X Xstatic char *spend; /* current end-of-line-buffer pointer */ Xstatic long lnum = 0L; /* current source line number */ X X/* append buffer maintenance */ Xstatic sedcmd *appends[MAXAPPENDS]; /* array of ptrs to a,i,c commands */ Xstatic sedcmd **aptr = appends; /* ptr to current append */ X X/* genbuf and its pointers */ Xstatic char genbuf[GENSIZ]; Xstatic char *lcomend = genbuf + GENSIZ; Xstatic char *loc1; Xstatic char *loc2; Xstatic char *locs; X X/* command-logic flags */ Xstatic int lastline; /* do-line flag */ Xstatic int jump; /* jump to cmd's link address if set */ Xstatic int delete; /* delete command flag */ X X/* tagged-pattern tracking */ Xstatic char *bracend[MAXTAGS]; /* tagged pattern start pointers */ Xstatic char *brastart[MAXTAGS]; /* tagged pattern end pointers */ X X Xvoid execute(file) X/* execute the compiled commands in cmds[] on a file */ Xchar *file; /* name of text source file to be filtered */ X{ X register char *p1, *p2; /* dummy copy ptrs */ X register sedcmd *ipc; /* ptr to current command */ X char *execp; /* ptr to source */ X char *getline(); /* input-getting functions */ X void command(), readout(); X XPASS("execute(): entry"); X X if (file != NULL) /* filter text from a named file */ X if (freopen(file, "r", stdin) == NULL) X fprintf(stderr, "sed: can't open %s\n", file); XPASS("execute(): reopen"); X X if (pending) /* there's a command waiting */ X { X ipc = pending; /* it will be first executed */ X pending = FALSE; /* turn off the waiting flag */ X goto doit; /* go to execute it immediately */ X } X X /* here's the main command-execution loop */ X for(;;) X { XPASS("execute(): main loop entry"); X X /* get next line to filter */ X if ((execp = getline(linebuf)) == BAD) X return; XPASS("execute(): getline"); X spend = execp; X X /* loop through compiled commands, executing them */ X for(ipc = cmds; ipc->command; ) X { XPASS("execute(): command loop entry"); X /* all no-address commands are selected */ X if (ipc->addr1 && !selected(ipc)) X { X ipc++; X continue; X } X doit: XPASS("execute(): doit"); X command(ipc); /* execute the command pointed at */ XPASS("execute(): command"); X X if (delete) /* if delete flag is set */ X break; /* don't exec rest of compiled cmds */ X X if (jump) /* if jump set, follow cmd's link */ X { X jump = FALSE; X if ((ipc = ipc->u.link) == 0) X { X ipc = cmds; X break; X } X } X else /* normal goto next command */ X ipc++; XPASS("execute(): end command loop"); X } X /* we've now done all modification commands on the line */ X X /* here's where the transformed line is output */ XPASS("execute(): output"); X if (!nflag && !delete) X { X for(p1 = linebuf; p1 < spend; p1++) X putc(*p1, stdout); X putc('\n', stdout); X } X X /* if we've been set up for append, emit the text from it */ X if (aptr > appends) X readout(); X X delete = FALSE; /* clear delete flag; about to get next cmd */ XPASS("execute(): end main loop"); X } XPASS("execute(): end execute"); X} X Xstatic int selected(ipc) X/* is current command selected */ Xsedcmd *ipc; X{ X register char *p1 = ipc->addr1; /* point p1 at first address */ X register char *p2 = ipc->addr2; /* and p2 at second */ X char c; X X if (ipc->flags.inrange) X { X if (*p2 == CEND) X p1 = NULL; X else if (*p2 == CLNUM) X { X c = p2[1]; X if (lnum > linenum[c]) X { X ipc->flags.inrange = FALSE; X if (ipc->flags.allbut) X return(TRUE); X ipc++; X return(FALSE); X } X if (lnum == linenum[c]) X ipc->flags.inrange = FALSE; X } X else if (match(p2, 0)) X ipc->flags.inrange = FALSE; X } X else if (*p1 == CEND) X { X if (!lastline) X { X if (ipc->flags.allbut) X return(TRUE); X ipc++; X return(FALSE); X } X } X else if (*p1 == CLNUM) X { X c = p1[1]; X if (lnum != linenum[c]) X { X if (ipc->flags.allbut) X return(TRUE); X ipc++; X return(FALSE); X } X if (p2) X ipc->flags.inrange = TRUE; X } X else if (match(p1, 0)) X { X if (p2) X ipc->flags.inrange = TRUE; X } X else X { X if (ipc->flags.allbut) X return(TRUE); X ipc++; X return(FALSE); X } X return(TRUE); /* SAJ for Atari ST, Mark Williams C */ X} X Xstatic int match(expbuf, gf) /* uses genbuf */ X/* match RE at expbuf against linebuf; if gf set, copy linebuf from genbuf */ Xchar *expbuf; X{ X register char *p1, *p2, c; X X if (gf) X { X if (*expbuf) X return(FALSE); X p1 = linebuf; p2 = genbuf; X while (*p1++ = *p2++); X locs = p1 = loc2; X } X else X { X p1 = linebuf; X locs = FALSE; X } X X p2 = expbuf; X if (*p2++) X { X loc1 = p1; X if(*p2 == CCHR && p2[1] != *p1) /* 1st char is wrong */ X return(FALSE); /* so fail */ X return(advance(p1, p2)); /* else try to match rest */ X } X X /* quick check for 1st character if it's literal */ X if (*p2 == CCHR) X { X c = p2[1]; /* pull out character to search for */ X do { X if (*p1 != c) X continue; /* scan the source string */ X if (advance(p1, p2)) /* found it, match the rest */ X return(loc1 = p1, 1); X } while X (*p1++); X return(FALSE); /* didn't find that first char */ X } X X /* else try for unanchored match of the pattern */ X do { X if (advance(p1, p2)) X return(loc1 = p1, 1); X } while X (*p1++); X X /* if got here, didn't match either way */ X return(FALSE); X} X Xstatic int advance(lp, ep) X/* attempt to advance match pointer by one pattern element */ Xregister char *lp; /* source (linebuf) ptr */ Xregister char *ep; /* regular expression element ptr */ X{ X register char *curlp; /* save ptr for closures */ X char c; /* scratch character holder */ X char *bbeg; X int ct; X X for (;;) X switch (*ep++) X { X case CCHR: /* literal character */ X if (*ep++ == *lp++) /* if chars are equal */ X continue; /* matched */ X return(FALSE); /* else return false */ X X case CDOT: /* anything but newline */ X if (*lp++) /* first NUL is at EOL */ X continue; /* keep going if didn't find */ X return(FALSE); /* else return false */ X X case CNL: /* start-of-line */ X case CDOL: /* end-of-line */ X if (*lp == 0) /* found that first NUL? */ X continue; /* yes, keep going */ X return(FALSE); /* else return false */ X X case CEOF: /* end-of-address mark */ X loc2 = lp; /* set second loc */ X return(TRUE); /* return true */ X X case CCL: /* a closure */ X c = *lp++ & 0177; X if (ep[c>>3] & bits[c & 07]) /* is char in set? */ X { X ep += 16; /* then skip rest of bitmask */ X continue; /* and keep going */ X } X return(FALSE); /* else return false */ X X case CBRA: /* start of tagged pattern */ X brastart[*ep++] = lp; /* mark it */ X continue; /* and go */ X X case CKET: /* end of tagged pattern */ X bracend[*ep++] = lp; /* mark it */ X continue; /* and go */ X X case CBACK: X bbeg = brastart[*ep]; X ct = bracend[*ep++] - bbeg; X X if (memcmp(bbeg, lp, ct)) X { X lp += ct; X continue; X } X return(FALSE); X X case CBACK|STAR: X bbeg = brastart[*ep]; X ct = bracend[*ep++] - bbeg; X curlp = lp; X while(memcmp(bbeg, lp, ct)) X lp += ct; X X while(lp >= curlp) X { X if (advance(lp, ep)) X return(TRUE); X lp -= ct; X } X return(FALSE); X X X case CDOT|STAR: /* match .* */ X curlp = lp; /* save closure start loc */ X while (*lp++); /* match anything */ X goto star; /* now look for followers */ X X case CCHR|STAR: /* match <literal char>* */ X curlp = lp; /* save closure start loc */ X while (*lp++ == *ep); /* match many of that char */ X ep++; /* to start of next element */ X goto star; /* match it and followers */ X X case CCL|STAR: /* match [...]* */ X curlp = lp; /* save closure start loc */ X do { X c = *lp++ & 0x7F; /* match any in set */ X } while X (ep[c>>3] & bits[c & 07]); X ep += 16; /* skip past the set */ X goto star; /* match followers */ X X star: /* the recursion part of a * or + match */ X if (--lp == curlp) /* 0 matches */ X continue; X X if (*ep == CCHR) X { X c = ep[1]; X do { X if (*lp != c) X continue; X if (advance(lp, ep)) X return(TRUE); X } while X (lp-- > curlp); X return(FALSE); X } X X if (*ep == CBACK) X { X c = *(brastart[ep[1]]); X do { X if (*lp != c) X continue; X if (advance(lp, ep)) X return(TRUE); X } while X (lp-- > curlp); X return(FALSE); X } X X do { X if (lp == locs) X break; X if (advance(lp, ep)) X return(TRUE); X } while X (lp-- > curlp); X return(FALSE); X X default: X fprintf(stderr, "sed: RE error, %o\n", *--ep); X } X} X Xstatic int substitute(ipc) X/* perform s command */ Xsedcmd *ipc; /* ptr to s command struct */ X{ X void dosub(); /* for if we find a match */ X X if (match(ipc->u.lhs, 0)) /* if no match */ X dosub(ipc->rhs); /* perform it once */ X else X return(FALSE); /* command fails */ X X if (ipc->flags.global) /* if global flag enabled */ X while(*loc2) /* cycle through possibles */ X if (match(ipc->u.lhs, 1)) /* found another */ X dosub(ipc->rhs); /* so substitute */ X else /* otherwise, */ X break; /* we're done */ X return(TRUE); /* we succeeded */ X} X Xstatic void dosub(rhsbuf) /* uses linebuf, genbuf, spend */ X/* generate substituted right-hand side (of s command) */ Xchar *rhsbuf; /* where to put the result */ X{ X register char *lp, *sp, *rp; X int c; X char *place(); X X /* copy linebuf to genbuf up to location 1 */ X lp = linebuf; sp = genbuf; X while (lp < loc1) *sp++ = *lp++; X X for (rp = rhsbuf; c = *rp++; ) X { X if (c == '&') X { X sp = place(sp, loc1, loc2); X continue; X } X else if (c & 0200 && (c &= 0177) >= '1' && c < MAXTAGS+'1') X { X sp = place(sp, brastart[c-'1'], bracend[c-'1']); X continue; X } X *sp++ = c & 0177; X if (sp >= genbuf + MAXBUF) X fprintf(stderr, LTLMSG); X } X lp = loc2; X/* MRY loc2 = sp - genbuf + linebuf; */ X loc2 = sp - (genbuf - linebuf); X while (*sp++ = *lp++) X if (sp >= genbuf + MAXBUF) X fprintf(stderr, LTLMSG); X lp = linebuf; sp = genbuf; X while (*lp++ = *sp++); X spend = lp-1; X} X Xstatic char *place(asp, al1, al2) /* uses genbuf */ X/* place chars at *al1...*(al1 - 1) at asp... in genbuf[] */ Xregister char *asp, *al1, *al2; X{ X while (al1 < al2) X { X *asp++ = *al1++; X if (asp >= genbuf + MAXBUF) X fprintf(stderr, LTLMSG); X } X return(asp); X} X Xstatic void listto(p1, fp) X/* write a hex dump expansion of *p1... to fp */ Xregister char *p1; /* the source */ XFILE *fp; /* output stream to write to */ X{ X p1--; X while(*p1++) X if (isprint(*p1)) X putc(*p1, fp); /* pass it through */ X else X { X putc('\134', fp); /* emit a backslash */ X switch(*p1) X { X case '\10': putc('b', fp); break; /* BS */ X case '\11': putc('t', fp); break; /* TAB */ X/* \11 was \9 --MRY */ X case '\12': putc('n', fp); break; /* NL */ X case '\15': putc('r', fp); break; /* CR */ X case '\33': putc('e', fp); break; /* ESC */ X default: fprintf(fp, "%02x", *p1 & 0xFF); X } X } X putc('\n', fp); X} X Xstatic void command(ipc) X/* execute compiled command pointed at by ipc */ Xsedcmd *ipc; X{ X static int didsub; /* true if last s succeeded */ X static char holdsp[MAXHOLD]; /* the hold space */ X static char *hspend = holdsp; /* hold space end pointer */ X register char *p1, *p2, *p3; X register int i; X char *execp; X X switch(ipc->command) X { X case ACMD: /* append */ X *aptr++ = ipc; X if (aptr >= appends + MAXAPPENDS) X fprintf(stderr, X "sed: too many appends after line %ld\n", X lnum); X *aptr = 0; X break; X X case CCMD: /* change pattern space */ X delete = TRUE; X if (!ipc->flags.inrange || lastline) X printf("%s\n", ipc->u.lhs); X break; X X case DCMD: /* delete pattern space */ X delete++; X break; X X case CDCMD: /* delete a line in hold space */ X p1 = p2 = linebuf; X while(*p1 != '\n') X if (delete = (*p1++ == 0)) X return; X p1++; X while(*p2++ = *p1++) continue; X spend = p2-1; X jump++; X break; X X case EQCMD: /* show current line number */ X fprintf(stdout, "%ld\n", lnum); X break; X X case GCMD: /* copy hold space to pattern space */ X p1 = linebuf; p2 = holdsp; while(*p1++ = *p2++); X spend = p1-1; X break; X X case CGCMD: /* append hold space to pattern space */ X *spend++ = '\n'; X p1 = spend; p2 = holdsp; X while(*p1++ = *p2++) X if (p1 >= linebuf + MAXBUF) X break; X spend = p1-1; X break; X X case HCMD: /* copy pattern space to hold space */ X p1 = holdsp; p2 = linebuf; while(*p1++ = *p2++); X hspend = p1-1; X break; X X case CHCMD: /* append pattern space to hold space */ X *hspend++ = '\n'; X p1 = hspend; p2 = linebuf; X while(*p1++ = *p2++) X if (p1 >= holdsp + MAXBUF) X break; X hspend = p1-1; X break; X X case ICMD: /* insert text */ X printf("%s\n", ipc->u.lhs); X break; X X case BCMD: /* branch to label */ X jump = TRUE; X break; X X case LCMD: /* list text */ X listto(linebuf, (ipc->fout != NULL)?ipc->fout:stdout); break; X X case NCMD: /* read next line into pattern space */ X if (!nflag) X puts(linebuf); /* flush out the current line */ X if (aptr > appends) X readout(); /* do pending a, r commands */ X if ((execp = getline(linebuf)) == BAD) X { X pending = ipc; X delete = TRUE; X break; X } X spend = execp; X break; X X case CNCMD: /* append next line to pattern space */ X if (aptr > appends) X readout(); X *spend++ = '\n'; X if ((execp = getline(spend)) == BAD) X { X pending = ipc; X delete = TRUE; X break; X } X spend = execp; X break; X X case PCMD: /* print pattern space */ X puts(linebuf); X break; X X case CPCMD: /* print one line from pattern space */ X cpcom: /* so s command can jump here */ X for(p1 = linebuf; *p1 != '\n' && *p1 != '\0'; ) X putc(*p1++, stdout); X putc('\n', stdout); X break; X X case QCMD: /* quit the stream editor */ X if (!nflag) X puts(linebuf); /* flush out the current line */ X if (aptr > appends) X readout(); /* do any pending a and r commands */ X exit(0); X X case RCMD: /* read a file into the stream */ X *aptr++ = ipc; X if (aptr >= appends + MAXAPPENDS) X fprintf(stderr, X "sed: too many reads after line %ld\n", X lnum); X *aptr = 0; X break; X X case SCMD: /* substitute RE */ X didsub = substitute(ipc); X if (ipc->flags.print && didsub) X if (ipc->flags.print == TRUE) X puts(linebuf); X else X goto cpcom; X if (didsub && ipc->fout) X fprintf(ipc->fout, "%s\n", linebuf); X break; X X case TCMD: /* branch on last s successful */ X case CTCMD: /* branch on last s failed */ X if (didsub == (ipc->command == CTCMD)) X break; /* no branch if last s failed, else */ X didsub = FALSE; X jump = TRUE; /* set up to jump to assoc'd label */ X break; X X case CWCMD: /* write one line from pattern space */ X for(p1 = linebuf; *p1 != '\n' && *p1 != '\0'; ) X putc(*p1++, ipc->fout); X putc('\n', ipc->fout); X break; X X case WCMD: /* write pattern space to file */ X fprintf(ipc->fout, "%s\n", linebuf); X break; X X case XCMD: /* exchange pattern and hold spaces */ X p1 = linebuf; p2 = genbuf; while(*p2++ = *p1++) continue; X p1 = holdsp; p2 = linebuf; while(*p2++ = *p1++) continue; X spend = p2 - 1; X p1 = genbuf; p2 = holdsp; while(*p2++ = *p1++) continue; X hspend = p2 - 1; X break; X X case YCMD: X p1 = linebuf; p2 = ipc->u.lhs; X while(*p1 = p2[*p1]) X p1++; X break; X } X} X Xstatic char *getline(buf) X/* get next line of text to be filtered */ Xregister char *buf; /* where to send the input */ X{ X if (gets(buf) != NULL) X { X lnum++; /* note that we got another line */ X while(*buf++); /* find the end of the input */ X return(--buf); /* return ptr to terminating null */ X } X else X { X if (eargc == 0) /* if no more args */ X lastline = TRUE; /* set a flag */ X return(BAD); X } X} X Xstatic int memcmp(a, b, count) X/* return TRUE if *a... == *b... for count chars, FALSE otherwise */ Xregister char *a, *b; X{ X while(count--) /* look at count characters */ X if (*a++ != *b++) /* if any are nonequal */ X return(FALSE); /* return FALSE for false */ X return(TRUE); /* compare succeeded */ X} X Xstatic void readout() X/* write file indicated by r command to output */ X{ X register char *p1; /* character-fetching dummy */ X register int t; /* hold input char or EOF */ X FILE *fi; /* ptr to file to be read */ X X aptr = appends - 1; /* arrange for pre-increment to work right */ X while(*++aptr) X if ((*aptr)->command == ACMD) /* process "a" cmd */ X printf("%s\n", (*aptr)->u.lhs); X else /* process "r" cmd */ X { X if ((fi = fopen((*aptr)->u.lhs, "r")) == NULL) X continue; X while((t = getc(fi)) != EOF) X putc((char) t, stdout); X fclose(fi); X } X aptr = appends; /* reset the append ptr */ X *aptr = 0; X} X X/* sedexec.c ends here */ END_OF_sedexec.c if test 28811 -ne `wc -c <sedexec.c`; then echo shar: \"sedexec.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f sedtest.bat -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"sedtest.bat\" else echo shar: Extracting \"sedtest.bat\" \(21 characters\) sed "s/^X//" >sedtest.bat <<'END_OF_sedtest.bat' Xsed -f ctrans pascal END_OF_sedtest.bat if test 21 -ne `wc -c <sedtest.bat`; then echo shar: \"sedtest.bat\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of shell archive. exit 0