Path: edison!uvacs!virginia!uunet!seismo!mcvax!botter!ast From: ast@cs.vu.nl (Andy Tanenbaum) Newsgroups: comp.os.minix Subject: sed part 1 of 2 (shar file) Message-ID: <1607@botter.cs.vu.nl> Date: 14 Sep 87 12:15:36 GMT Reply-To: ast@cs.vu.nl (Andy Tanenbaum) Organization: VU Informatica, Amsterdam Lines: 959 : This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin echo Extracting \d\e\b\u\g\.\h sed 's/^X//' > \d\e\b\u\g\.\h << '+ END-OF-FILE '\d\e\b\u\g\.\h X#define PASS(x) X/*printf("%s\n", x)*/ + END-OF-FILE debug.h chmod 'u=rw,g=r,o=r' \d\e\b\u\g\.\h set `sum \d\e\b\u\g\.\h` sum=$1 case $sum in 22322) :;; *) echo 'Bad sum in '\d\e\b\u\g\.\h >&2 esac echo Extracting \m\a\k\e\f\i\l\e sed 's/^X//' > \m\a\k\e\f\i\l\e << '+ END-OF-FILE '\m\a\k\e\f\i\l\e X#makefile for the don kneller's shareware ndmake X XOBJS= sedcomp.s sedexec.s X Xsed: $(OBJS) sed.h debug.h X cc -o sed -i $(OBJS) X Xsedcomp.s: debug.h sed.h sedcomp.c Xsedexec.s: debug.h sed.h sedexec.c + END-OF-FILE makefile chmod 'u=rw,g=r,o=r' \m\a\k\e\f\i\l\e set `sum \m\a\k\e\f\i\l\e` sum=$1 case $sum in 08217) :;; *) echo 'Bad sum in '\m\a\k\e\f\i\l\e >&2 esac echo Extracting \s\e\d\.\h sed 's/^X//' > \s\e\d\.\h << '+ END-OF-FILE '\s\e\d\.\h X/* sed.h -- types and constants for the stream editor */ X X/* data area sizes used by both modules */ X#define MAXBUF 4000 /* current line buffer size */ X#define MAXAPPENDS 20 /* maximum number of appends */ X#define MAXTAGS 9 /* tagged patterns are \1 to \9 */ X X/* constants for compiled-command representation */ X#define EQCMD 0x01 /* = -- print current line number */ X#define ACMD 0x02 /* a -- append text after current line */ X#define BCMD 0x03 /* b -- branch to label */ X#define CCMD 0x04 /* c -- change current line */ X#define DCMD 0x05 /* d -- delete all of pattern space */ X#define CDCMD 0x06 /* D -- delete first line of pattern space */ X#define GCMD 0x07 /* g -- copy hold space to pattern space */ X#define CGCMD 0x08 /* G -- append hold space to pattern space */ X#define HCMD 0x09 /* h -- copy pattern space to hold space */ X#define CHCMD 0x0A /* H -- append hold space to pattern space */ X#define ICMD 0x0B /* i -- insert text before current line */ X#define LCMD 0x0C /* l -- print pattern space in escaped form */ X#define NCMD 0x0D /* n -- get next line into pattern space */ X#define CNCMD 0x0E /* N -- append next line to pattern space */ X#define PCMD 0x0F /* p -- print pattern space to output */ X#define CPCMD 0x10 /* P -- print first line of pattern space */ X#define QCMD 0x11 /* q -- exit the stream editor */ X#define RCMD 0x12 /* r -- read in a file after current line */ X#define SCMD 0x13 /* s -- regular-expression substitute */ X#define TCMD 0x14 /* t -- branch on last substitute successful */ X#define CTCMD 0x15 /* T -- branch on last substitute failed */ X#define WCMD 0x16 /* w -- write pattern space to file */ X#define CWCMD 0x17 /* W -- write first line of pattern space */ X#define XCMD 0x18 /* x -- exhange pattern and hold spaces */ X#define YCMD 0x19 /* y -- transliterate text */ X Xstruct cmd_t /* compiled-command representation */ X{ X char *addr1; /* first address for command */ X char *addr2; /* second address for command */ X union X { X char *lhs; /* s command lhs */ X struct cmd_t *link; /* label link */ X } u; X char command; /* command code */ X char *rhs; /* s command replacement string */ X FILE *fout; /* associated output file descriptor */ X struct X { X char allbut ; /* was negation specified? */ X char global ; /* was p postfix specified? */ X char print ; /* was g postfix specified? */ X char inrange ; /* in an address range? */ X } flags; X}; Xtypedef struct cmd_t sedcmd; /* use this name for declarations */ X X#define BAD ((char *) -1) /* guaranteed not a string ptr */ X X X/* address and regular expression compiled-form markers */ X#define STAR 1 /* marker for Kleene star */ X#define CCHR 2 /* non-newline character to be matched follows */ X#define CDOT 4 /* dot wild-card marker */ X#define CCL 6 /* character class follows */ X#define CNL 8 /* match line start */ X#define CDOL 10 /* match line end */ X#define CBRA 12 /* tagged pattern start marker */ X#define CKET 14 /* tagged pattern end marker */ X#define CBACK 16 /* backslash-digit pair marker */ X#define CLNUM 18 /* numeric-address index follows */ X#define CEND 20 /* symbol for end-of-source */ X#define CEOF 22 /* end-of-field mark */ X X/* sed.h ends here */ + END-OF-FILE sed.h chmod 'u=rw,g=r,o=r' \s\e\d\.\h set `sum \s\e\d\.\h` sum=$1 case $sum in 11978) :;; *) echo 'Bad sum in '\s\e\d\.\h >&2 esac echo Extracting \s\e\d\c\o\m\p\.\c sed 's/^X//' > \s\e\d\c\o\m\p\.\c << '+ END-OF-FILE '\s\e\d\c\o\m\p\.\c X#ifdef foo X #include "compiler.h" X #ifdef LATTICE X #define void int X #endif X#endif X X#include "debug.h" X/* sedcomp.c -- stream editor main and compilation phase X X The stream editor compiles its command input (from files or -e options) Xinto an internal form using compile() then executes the compiled form using Xexecute(). Main() just initializes data structures, interprets command line Xoptions, and calls compile() and execute() in appropriate sequence. X The data structure produced by compile() is an array of compiled-command Xstructures (type sedcmd). These contain several pointers into pool[], the Xregular-expression and text-data pool, plus a command code and g & p flags. XIn the special case that the command is a label the struct will hold a ptr Xinto the labels array labels[] during most of the compile, until resolve() Xresolves references at the end. X The operation of execute() is described in its source module. X X==== Written for the GNU operating system by Eric S. Raymond ==== */ X X#include /* uses getc, fprintf, fopen, fclose */ X#include "sed.h" /* command type struct and name defines */ X X/* imported functions */ Xextern int strcmp(); /* test strings for equality */ Xextern void execute(); /* execute compiled command */ X X/***** public stuff ******/ X X#define MAXCMDS 200 /* maximum number of compiled commands */ X#define MAXLINES 256 /* max # numeric addresses to compile */ X X/* main data areas */ Xchar linebuf[MAXBUF+1]; /* current-line buffer */ Xsedcmd cmds[MAXCMDS+1]; /* hold compiled commands */ Xlong linenum[MAXLINES]; /* numeric-addresses table */ X X/* miscellaneous shared variables */ Xint nflag; /* -n option flag */ Xint eargc; /* scratch copy of argument count */ Xsedcmd *pending = NULL; /* next command to be executed */ Xchar bits[] = {1,2,4,8,16,32,64,128}; X X/***** module common stuff *****/ X X#define POOLSIZE 10000 /* size of string-pool space */ X#define WFILES 10 /* max # w output files that can be compiled */ X#define RELIMIT 256 /* max chars in compiled RE */ X#define MAXDEPTH 20 /* maximum {}-nesting level */ X#define MAXLABS 50 /* max # of labels that can be handled */ X X#define SKIPWS(pc) while ((*pc==' ') || (*pc=='\t')) pc++ X#define ABORT(msg) (fprintf(stderr, msg, linebuf), exit(2)) X#define IFEQ(x, v) if (*x == v) x++ , /* do expression */ X X/* error messages */ Xstatic char AGMSG[] = "sed: garbled address %s\n"; Xstatic char CGMSG[] = "sed: garbled command %s\n"; Xstatic char TMTXT[] = "sed: too much text: %s\n"; Xstatic char AD1NG[] = "sed: no addresses allowed for %s\n"; Xstatic char AD2NG[] = "sed: only one address allowed for %s\n"; Xstatic char TMCDS[] = "sed: too many commands, last was %s\n"; Xstatic char COCFI[] = "sed: cannot open command-file %s\n"; Xstatic char UFLAG[] = "sed: unknown flag %c\n"; Xstatic char COOFI[] = "sed: cannot open %s\n"; Xstatic char CCOFI[] = "sed: cannot create %s\n"; Xstatic char ULABL[] = "sed: undefined label %s\n"; Xstatic char TMLBR[] = "sed: too many {'s\n"; Xstatic char FRENL[] = "sed: first RE must be non-null\n"; Xstatic char NSCAX[] = "sed: no such command as %s\n"; Xstatic char TMRBR[] = "sed: too many }'s\n"; Xstatic char DLABL[] = "sed: duplicate label %s\n"; Xstatic char TMLAB[] = "sed: too many labels: %s\n"; Xstatic char TMWFI[] = "sed: too many w files\n"; Xstatic char REITL[] = "sed: RE too long: %s\n"; Xstatic char TMLNR[] = "sed: too many line numbers\n"; Xstatic char TRAIL[] = "sed: command \"%s\" has trailing garbage\n"; X Xtypedef struct /* represent a command label */ X{ X char *name; /* the label name */ X sedcmd *last; /* it's on the label search list */ X sedcmd *address; /* pointer to the cmd it labels */ X} Xlabel; X X/* label handling */ Xstatic label labels[MAXLABS]; /* here's the label table */ Xstatic label *lab = labels + 1; /* pointer to current label */ Xstatic label *lablst = labels; /* header for search list */ X X/* string pool for regular expressions, append text, etc. etc. */ Xstatic char pool[POOLSIZE]; /* the pool */ Xstatic char *fp = pool; /* current pool pointer */ Xstatic char *poolend = pool + POOLSIZE; /* pointer past pool end */ X X/* compilation state */ Xstatic FILE *cmdf = NULL; /* current command source */ Xstatic char *cp = linebuf; /* compile pointer */ Xstatic sedcmd *cmdp = cmds; /* current compiled-cmd ptr */ Xstatic char *lastre = NULL; /* old RE pointer */ Xstatic int bdepth = 0; /* current {}-nesting level */ Xstatic int bcount = 0; /* # tagged patterns in current RE */ Xstatic char **eargv; /* scratch copy of argument list */ X X/* compilation flags */ Xstatic int eflag; /* -e option flag */ Xstatic int gflag; /* -g option flag */ X X Xmain(argc, argv) X/* main sequence of the stream editor */ Xint argc; Xchar *argv[]; X{ X void compile(), resolve(); X X eargc = argc; /* set local copy of argument count */ X eargv = argv; /* set local copy of argument list */ X cmdp->addr1 = pool; /* 1st addr expand will be at pool start */ X if (eargc == 1) X exit(0); /* exit immediately if no arguments */ XPASS("main(): setup"); X /* scan through the arguments, interpreting each one */ X while ((--eargc > 0) && (**++eargv == '-')) X switch (eargv[0][1]) X { X case 'e': X eflag++; compile(); /* compile with e flag on */ X eflag = 0; X continue; /* get another argument */ X case 'f': X if (eargc-- <= 0) /* barf if no -f file */ X exit(2); X if ((cmdf = fopen(*++eargv, "r")) == NULL) X { X fprintf(stderr, COCFI, *eargv); X exit(2); X } X compile(); /* file is O.K., compile it */ X fclose(cmdf); X continue; /* go back for another argument */ X case 'g': X gflag++; /* set global flag on all s cmds */ X continue; X case 'n': X nflag++; /* no print except on p flag or w */ X continue; X default: X fprintf(stdout, UFLAG, eargv[0][1]); X continue; X } X XPASS("main(): argscan"); X X if (cmdp == cmds) /* no commands have been compiled */ X { X eargv--; eargc++; X eflag++; compile(); eflag = 0; X eargv++; eargc--; X } X X if (bdepth) /* we have unbalanced squigglies */ X ABORT(TMLBR); X X lablst->address = cmdp; /* set up header of label linked list */ X resolve(); /* resolve label table indirections */ XPASS("main(): resolve"); X if (eargc <= 0) /* if there were no -e commands */ X execute(NULL); /* execute commands from stdin only */ X else while(--eargc>=0) /* else execute only -e commands */ X execute(*eargv++); XPASS("main(): end & exit OK"); X exit(0); /* everything was O.K. if we got here */ X} X X X#define H 0x80 /* 128 bit, on if there's really code for command */ X#define LOWCMD 56 /* = '8', lowest char indexed in cmdmask */ X X/* indirect through this to get command internal code, if it exists */ Xstatic char cmdmask[] = X{ X 0, 0, H, 0, 0, H+EQCMD,0, 0, X 0, 0, 0, 0, H+CDCMD,0, 0, CGCMD, X CHCMD, 0, 0, 0, 0, 0, CNCMD, 0, X CPCMD, 0, 0, 0, H+CTCMD,0, 0, H+CWCMD, X 0, 0, 0, 0, 0, 0, 0, 0, X 0, H+ACMD, H+BCMD, H+CCMD, DCMD, 0, 0, GCMD, X HCMD, H+ICMD, 0, 0, H+LCMD, 0, NCMD, 0, X PCMD, H+QCMD, H+RCMD, H+SCMD, H+TCMD, 0, 0, H+WCMD, X XCMD, H+YCMD, 0, H+BCMD, 0, H, 0, 0, X}; X Xstatic void compile() X/* precompile sed commands out of a file */ X{ X char ccode, *address(); X XPASS("compile(): entry"); X X for(;;) /* main compilation loop */ X { X if (*cp != ';') /* get a new command line */ X if (cmdline(cp = linebuf) < 0) X break; X SKIPWS(cp); X if (*cp=='\0' || *cp=='#') /* a comment */ X continue; X if (*cp == ';') /* ; separates cmds */ X { X cp++; X continue; X } X X /* compile first address */ X if (fp > poolend) X ABORT(TMTXT); X else if ((fp = address(cmdp->addr1 = fp)) == BAD) X ABORT(AGMSG); X X if (fp == cmdp->addr1) /* if empty RE was found */ X { X if (lastre) /* if there was previous RE */ X cmdp->addr1 = lastre; /* use it */ X else X ABORT(FRENL); X } X else if (fp == NULL) /* if fp was NULL */ X { X fp = cmdp->addr1; /* use current pool location */ X cmdp->addr1 = NULL; X } X else X { X lastre = cmdp->addr1; X if (*cp == ',' || *cp == ';') /* there's 2nd addr */ X { X cp++; X if (fp > poolend) ABORT(TMTXT); X fp = address(cmdp->addr2 = fp); X if (fp == BAD || fp == NULL) ABORT(AGMSG); X if (fp == cmdp->addr2) X cmdp->addr2 = lastre; X else X lastre = cmdp->addr2; X } X else X cmdp->addr2 = NULL; /* no 2nd address */ X } X if (fp > poolend) ABORT(TMTXT); X X SKIPWS(cp); /* discard whitespace after address */ X IFEQ(cp, '!') cmdp->flags.allbut = 1; X X SKIPWS(cp); /* get cmd char, range-check it */ X if ((*cp < LOWCMD) || (*cp > '~') X || ((ccode = cmdmask[*cp - LOWCMD]) == 0)) X ABORT(NSCAX); X X cmdp->command = ccode & ~H; /* fill in command value */ X if ((ccode & H) == 0) /* if no compile-time code */ X cp++; /* discard command char */ X else if (cmdcomp(*cp++)) /* execute it; if ret = 1 */ X continue; /* skip next line read */ X X if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS); X X SKIPWS(cp); /* look for trailing stuff */ X if (*cp != '\0') X if (*++cp == ';') X continue; X else if (cp[-1] != '#') X ABORT(TRAIL); X } X} X Xstatic int cmdcomp(cchar) X/* compile a single command */ Xregister char cchar; /* character name of command */ X{ X char *gettext(), *rhscomp(), *recomp(), *ycomp(); X static sedcmd **cmpstk[MAXDEPTH]; /* current cmd stack for {} */ X static char *fname[WFILES]; /* w file name pointers */ X static FILE *fout[WFILES]; /* w file file ptrs */ X static int nwfiles = 1; /* count of open w files */ X int i; /* indexing dummy used in w */ X sedcmd *sp1, *sp2; /* temps for label searches */ X label *lpt, *search(); /* ditto, and the searcher */ X char redelim; /* current RE delimiter */ X X fout[0] = stdout; X switch(cchar) X { X case '{': /* start command group */ X cmdp->flags.allbut = !cmdp->flags.allbut; X cmpstk[bdepth++] = &(cmdp->u.link); X if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS); X if (*cp == '\0') *cp = ';'; /* get next cmd w/o lineread */ X return(1); X X case '}': /* end command group */ X if (cmdp->addr1) ABORT(AD1NG); /* no addresses allowed */ X if (--bdepth < 0) ABORT(TMRBR); /* too many right braces */ X *cmpstk[bdepth] = cmdp; /* set the jump address */ X return(1); X X case '=': /* print current source line number */ X case 'q': /* exit the stream editor */ X if (cmdp->addr2) ABORT(AD2NG); X break; X X case ':': /* label declaration */ X if (cmdp->addr1) ABORT(AD1NG); /* no addresses allowed */ X fp = gettext(lab->name = fp); /* get the label name */ X if (lpt = search(lab)) /* does it have a double? */ X { X if (lpt->address) ABORT(DLABL); /* yes, abort */ X } X else /* check that it doesn't overflow label table */ X { X lab->last = NULL; X lpt = lab; X if (++lab >= labels + MAXLABS) ABORT(TMLAB); X } X lpt->address = cmdp; X return(1); X X case 'b': /* branch command */ X case 't': /* branch-on-succeed command */ X case 'T': /* branch-on-fail command */ X SKIPWS(cp); X if (*cp == '\0') /* if branch is to start of cmds... */ X { X /* add current command to end of label last */ X if (sp1 = lablst->last) X { X while(sp2 = sp1->u.link) X sp1 = sp2; X sp1->u.link = cmdp; X } X else /* lablst->last == NULL */ X lablst->last = cmdp; X break; X } X fp = gettext(lab->name = fp); /* else get label into pool */ X if (lpt = search(lab)) /* enter branch to it */ X { X if (lpt->address) X cmdp->u.link = lpt->address; X else X { X sp1 = lpt->last; X while(sp2 = sp1->u.link) X sp1 = sp2; X sp1->u.link = cmdp; X } X } X else /* matching named label not found */ X { X lab->last = cmdp; /* add the new label */ X lab->address = NULL; /* it's forward of here */ X if (++lab >= labels + MAXLABS) /* overflow if last */ X ABORT(TMLAB); X } X break; X X case 'a': /* append text */ X case 'i': /* insert text */ X case 'r': /* read file into stream */ X if (cmdp->addr2) ABORT(AD2NG); X case 'c': /* change text */ X if ((*cp == '\\') && (*++cp == '\n')) cp++; X fp = gettext(cmdp->u.lhs = fp); X break; X X case 'D': /* delete current line in hold space */ X cmdp->u.link = cmds; X break; X X case 's': /* substitute regular expression */ X redelim = *cp++; /* get delimiter from 1st ch */ X if ((fp = recomp(cmdp->u.lhs = fp, redelim)) == BAD) X ABORT(CGMSG); X if (fp == cmdp->u.lhs) /* if compiled RE zero len */ X cmdp->u.lhs = lastre; /* use the previous one */ X else /* otherwise */ X lastre = cmdp->u.lhs; /* save the one just found */ X if ((cmdp->rhs = fp) > poolend) ABORT(TMTXT); X if ((fp = rhscomp(cmdp->rhs, redelim)) == BAD) ABORT(CGMSG); X if (gflag) cmdp->flags.global++; X while (*cp == 'g' || *cp == 'p' || *cp == 'P') X { X IFEQ(cp, 'g') cmdp->flags.global++; X IFEQ(cp, 'p') cmdp->flags.print = 1; X IFEQ(cp, 'P') cmdp->flags.print = 2; X } X X case 'l': /* list pattern space */ X if (*cp == 'w') X cp++; /* and execute a w command! */ X else X break; /* s or l is done */ X X case 'w': /* write-pattern-space command */ X case 'W': /* write-first-line command */ X if (nwfiles >= WFILES) ABORT(TMWFI); X fp=gettext(fname[nwfiles]=fp); /* filename will be in pool */ X for(i = nwfiles-1; i >= 0; i--) /* match it in table */ X if (strcmp(fname[nwfiles], fname[i]) == 0) X { X cmdp->fout = fout[i]; X return(0); X } X /* if didn't find one, open new out file */ X if ((cmdp->fout = fopen(fname[nwfiles], "w")) == NULL) X { X fprintf(stderr, CCOFI, fname[nwfiles]); X exit(2); X } X fout[nwfiles++] = cmdp->fout; X break; X X case 'y': /* transliterate text */ X fp = ycomp(cmdp->u.lhs = fp, *cp++); /* compile translit */ X if (fp == BAD) ABORT(CGMSG); /* fail on bad form */ X if (fp > poolend) ABORT(TMTXT); /* fail on overflow */ X break; X } X return(0); /* succeeded in interpreting one command */ X} X Xstatic char *rhscomp(rhsp, delim) /* uses bcount */ X/* generate replacement string for substitute command right hand side */ Xregister char *rhsp; /* place to compile expression to */ Xregister char delim; /* regular-expression end-mark to look for */ X{ X register char *p = cp; /* strictly for speed */ X X for(;;) X if ((*rhsp = *p++) == '\\') /* copy; if it's a \, */ X { X *rhsp = *p++; /* copy escaped char */ X /* check validity of pattern tag */ X if (*rhsp > bcount + '0' && *rhsp <= '9') X return(BAD); X *rhsp++ |= 0x80; /* mark the good ones */ X continue; X } X else if (*rhsp == delim) /* found RE end, hooray... */ X { X *rhsp++ = '\0'; /* cap the expression string */ X cp = p; X return(rhsp); /* pt at 1 past the RE */ X } X else if (*rhsp++ == '\0') /* last ch not RE end, help! */ X return(BAD); X} X Xstatic char *recomp(expbuf, redelim) /* uses cp, bcount */ X/* compile a regular expression to internal form */ Xchar *expbuf; /* place to compile it to */ Xchar redelim; /* RE end-marker to look for */ X{ X register char *ep = expbuf; /* current-compiled-char pointer */ X register char *sp = cp; /* source-character ptr */ X register int c; /* current-character pointer */ X char negclass; /* all-but flag */ X char *lastep; /* ptr to last expr compiled */ X char *svclass; /* start of current char class */ X char brnest[MAXTAGS]; /* bracket-nesting array */ X char *brnestp; /* ptr to current bracket-nest */ X char *pp; /* scratch pointer */ X int classct; /* class element count */ X int tags; /* # of closed tags */ X X if (*cp == redelim) /* if first char is RE endmarker */ X return(cp++, expbuf); /* leave existing RE unchanged */ X X lastep = NULL; /* there's no previous RE */ X brnestp = brnest; /* initialize ptr to brnest array */ X tags = bcount = 0; /* initialize counters */ X X if (*ep++ = (*sp == '^')) /* check for start-of-line syntax */ X sp++; X X for (;;) X { X if (ep >= expbuf + RELIMIT) /* match is too large */ X return(cp = sp, BAD); X if ((c = *sp++) == redelim) /* found the end of the RE */ X { X cp = sp; X if (brnestp != brnest) /* \(, \) unbalanced */ X return(BAD); X *ep++ = CEOF; /* write end-of-pattern mark */ X return(ep); /* return ptr to compiled RE */ X } X if ((c != '*') && (c != '+')) /* if we're a postfix op */ X lastep = ep; /* get ready to match last */ X X switch (c) X { X case '\\': X if ((c = *sp++) == '(') /* start tagged section */ X { X if (bcount >= MAXTAGS) X return(cp = sp, BAD); X *brnestp++ = bcount; /* update tag stack */ X *ep++ = CBRA; /* enter tag-start */ X *ep++ = bcount++; /* bump tag count */ X continue; X } X else if (c == ')') /* end tagged section */ X { X if (brnestp <= brnest) /* extra \) */ X return(cp = sp, BAD); X *ep++ = CKET; /* enter end-of-tag */ X *ep++ = *--brnestp; /* pop tag stack */ X tags++; /* count closed tags */ X continue; X } X else if (c >= '1' && c <= '9') /* tag use */ X { X if ((c -= '1') >= tags) /* too few */ X return(BAD); X *ep++ = CBACK; /* enter tag mark */ X *ep++ = c; /* and the number */ X continue; X } X else if (c == '\n') /* escaped newline no good */ X return(cp = sp, BAD); X else if (c == 'n') /* match a newline */ X c = '\n'; X else if (c == 't') /* match a tab */ X c = '\t'; X else X goto defchar; /* else match \c */ X X case '\0': /* ignore nuls */ X continue; X X case '\n': /* trailing pattern delimiter is missing */ X return(cp = sp, BAD); X X case '.': /* match any char except newline */ X *ep++ = CDOT; X continue; X X case '+': /* 1 to n repeats of previous pattern */ X if (lastep == NULL) /* if + not first on line */ X goto defchar; /* match a literal + */ X if (*lastep == CKET) /* can't iterate a tag */ X return(cp = sp, BAD); X pp = ep; /* else save old ep */ X while (lastep < pp) /* so we can blt the pattern */ X *ep++ = *lastep++; X *lastep |= STAR; /* flag the copy */ X continue; X X case '*': /* 0..n repeats of previous pattern */ X if (lastep == NULL) /* if * isn't first on line */ X goto defchar; /* match a literal * */ X if (*lastep == CKET) /* can't iterate a tag */ X return(cp = sp, BAD); X *lastep |= STAR; /* flag previous pattern */ X continue; X X case '$': /* match only end-of-line */ X if (*sp != redelim) /* if we're not at end of RE */ X goto defchar; /* match a literal $ */ X *ep++ = CDOL; /* insert end-symbol mark */ X continue; X X case '[': /* begin character set pattern */ X if (ep + 17 >= expbuf + RELIMIT) X ABORT(REITL); X *ep++ = CCL; /* insert class mark */ X if (negclass = ((c = *sp++) == '^')) X c = *sp++; X svclass = sp; /* save ptr to class start */ X do { X if (c == '\0') ABORT(CGMSG); X X /* handle character ranges */ X if (c == '-' && sp > svclass && *sp != ']') X for (c = sp[-2]; c < *sp; c++) X ep[c >> 3] |= bits[c & 7]; X X /* handle escape sequences in sets */ X if (c == '\\') X if ((c = *sp++) == 'n') X c = '\n'; X else if (c == 't') X c = '\t'; X X /* enter (possibly translated) char in set */ X ep[c >> 3] |= bits[c & 7]; X } while X ((c = *sp++) != ']'); X X /* invert the bitmask if all-but was specified */ X if (negclass) X for(classct = 0; classct < 16; classct++) X ep[classct] ^= 0xFF; X ep[0] &= 0xFE; /* never match ASCII 0 */ X ep += 16; /* advance ep past set mask */ X continue; X X defchar: /* match literal character */ X default: /* which is what we'd do by default */ X *ep++ = CCHR; /* insert character mark */ X *ep++ = c; X } X } X} X Xstatic int cmdline(cbuf) /* uses eflag, eargc, cmdf */ X/* read next command from -e argument or command file */ Xregister char *cbuf; X{ X register int inc; /* not char because must hold EOF */ X X cbuf--; /* so pre-increment points us at cbuf */ X X /* e command flag is on */ X if (eflag) X { X register char *p; /* ptr to current -e argument */ X static char *savep; /* saves previous value of p */ X X if (eflag > 0) /* there are pending -e arguments */ X { X eflag = -1; X if (eargc-- <= 0) X exit(2); /* if no arguments, barf */ X X /* else transcribe next e argument into cbuf */ X p = *++eargv; X while(*++cbuf = *p++) X if (*cbuf == '\\') X { X if ((*++cbuf = *p++) == '\0') X return(savep = NULL, -1); X else X continue; X } X else if (*cbuf == '\n') /* end of 1 cmd line */ X { X *cbuf = '\0'; X return(savep = p, 1); X /* we'll be back for the rest... */ X } X X /* found end-of-string; can advance to next argument */ X return(savep = NULL, 1); X } X X if ((p = savep) == NULL) X return(-1); X X while(*++cbuf = *p++) X if (*cbuf == '\\') X { X if ((*++cbuf = *p++) == '0') X return(savep = NULL, -1); X else X continue; X } X else if (*cbuf == '\n') X { X *cbuf = '\0'; X return(savep = p, 1); X } X X return(savep = NULL, 1); X } X X /* if no -e flag read from command file descriptor */ X while((inc = getc(cmdf)) != EOF) /* get next char */ X if ((*++cbuf = inc) == '\\') /* if it's escape */ X *++cbuf = inc = getc(cmdf); /* get next char */ X else if (*cbuf == '\n') /* end on newline */ X return(*cbuf = '\0', 1); /* cap the string */ X X return(*++cbuf = '\0', -1); /* end-of-file, no more chars */ X} X Xstatic char *address(expbuf) /* uses cp, linenum */ X/* expand an address at *cp... into expbuf, return ptr at following char */ Xregister char *expbuf; X{ X static int numl = 0; /* current ind in addr-number table */ X register char *rcp; /* temp compile ptr for forwd look */ X long lno; /* computed value of numeric address */ X X if (*cp == '$') /* end-of-source address */ X { X *expbuf++ = CEND; /* write symbolic end address */ X *expbuf++ = CEOF; /* and the end-of-address mark (!) */ X cp++; /* go to next source character */ X return(expbuf); /* we're done */ X } X if (*cp == '/') /* start of regular-expression match */ X return(recomp(expbuf, *cp++)); /* compile the RE */ X X rcp = cp; lno = 0; /* now handle a numeric address */ X while(*rcp >= '0' && *rcp <= '9') /* collect digits */ X lno = lno*10 + *rcp++ - '0'; /* compute their value */ X X if (rcp > cp) /* if we caught a number... */ X { X *expbuf++ = CLNUM; /* put a numeric-address marker */ X *expbuf++ = numl; /* and the address table index */ X linenum[numl++] = lno; /* and set the table entry */ X if (numl >= MAXLINES) /* oh-oh, address table overflow */ X ABORT(TMLNR); /* abort with error message */ X *expbuf++ = CEOF; /* write the end-of-address marker */ X cp = rcp; /* point compile past the address */ X return(expbuf); /* we're done */ X } X X return(NULL); /* no legal address was found */ X} X Xstatic char *gettext(txp) /* uses global cp */ X/* accept multiline input from *cp..., discarding leading whitespace */ Xregister char *txp; /* where to put the text */ X{ X register char *p = cp; /* this is for speed */ X X SKIPWS(p); /* discard whitespace */ X do { X if ((*txp = *p++) == '\\') /* handle escapes */ X *txp = *p++; X if (*txp == '\0') /* we're at end of input */ X return(cp = --p, ++txp); X else if (*txp == '\n') /* also SKIPWS after newline */ X SKIPWS(p); X } while X (txp++); /* keep going till we find that nul */ X} X Xstatic label *search(ptr) /* uses global lablst */ X/* find the label matching *ptr, return NULL if none */ Xregister label *ptr; X{ X register label *rp; X for(rp = lablst; rp < ptr; rp++) X if (strcmp(rp->name, ptr->name) == 0) X return(rp); X return(NULL); X} X Xstatic void resolve() /* uses global lablst */ X/* write label links into the compiled-command space */ X{ X register label *lptr; X register sedcmd *rptr, *trptr; X X /* loop through the label table */ X for(lptr = lablst; lptr < lab; lptr++) X if (lptr->address == NULL) /* barf if not defined */ X { X fprintf(stderr, ULABL, lptr->name); X exit(2); X } X else if (lptr->last) /* if last is non-null */ X { X rptr = lptr->last; /* chase it */ X while(trptr = rptr->u.link) /* resolve refs */ X { X rptr->u.link = lptr->address; X rptr = trptr; X } X rptr->u.link = lptr->address; X } X} X Xstatic char *ycomp(ep, delim) X/* compile a y (transliterate) command */ Xregister char *ep; /* where to compile to */ Xchar delim; /* end delimiter to look for */ X{ X register char c, *tp, *sp; X X /* scan the 'from' section for invalid chars */ X for(sp = tp = cp; *tp != delim; tp++) X { X if (*tp == '\\') X tp++; X if ((*tp == '\n') || (*tp == '\0')) X return(BAD); X } X tp++; /* tp now points at first char of 'to' section */ X X /* now rescan the 'from' section */ X while((c = *sp++ & 0x7F) != delim) X { X if (c == '\\' && *sp == 'n') X { X sp++; X c = '\n'; X } X if ((ep[c] = *tp++) == '\\' && *tp == 'n') X { X ep[c] = '\n'; X tp++; X } X if ((ep[c] == delim) || (ep[c] == '\0')) X return(BAD); X } X X if (*tp != delim) /* 'to', 'from' parts have unequal lengths */ X return(BAD); X X cp = ++tp; /* point compile ptr past translit */ X X for(c = 0; c < 128; c++) /* fill in self-map entries in table */ X if (ep[c] == 0) X ep[c] = c; X X return(ep + 0x80); /* return first free location past table end */ X} X + END-OF-FILE sedcomp.c chmod 'u=rw,g=r,o=r' \s\e\d\c\o\m\p\.\c set `sum \s\e\d\c\o\m\p\.\c` sum=$1 case $sum in 51934) :;; *) echo 'Bad sum in '\s\e\d\c\o\m\p\.\c >&2 esac exit 0