Path: edison!uvacs!virginia!uunet!seismo!mcvax!botter!ast From: ast@cs.vu.nl (Andy Tanenbaum) Newsgroups: comp.os.minix Subject: sed part 2 of 2 (shar file) Message-ID: <1608@botter.cs.vu.nl> Date: 14 Sep 87 12:18:28 GMT Reply-To: ast@cs.vu.nl (Andy Tanenbaum) Organization: VU Informatica, Amsterdam Lines: 1379 : 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 \e\d\.\m\a\n sed 's/^X//' > \e\d\.\m\a\n << '+ END-OF-FILE '\e\d\.\m\a\n X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X XNAME X ed - text editor X XORIGIN X OSx X XSYNOPSIS X ed [ - ] [ -x ] [ name ] X XDESCRIPTION X Ed is the standard text editor. X X If a name argument is given, ed simulates an e command (see X below) on the named file; that is to say, the file is read X into ed's buffer so that it can be edited. If -x is X present, an x command is simulated first to handle an X encrypted file. The optional - suppresses the printing of X explanatory output and should be used when the standard X input is an editor script. X X Ed operates on a copy of any file it is editing; changes X made in the copy have no effect on the file until a w X (write) command is given. The copy of the text being edited X resides in a temporary file called the buffer. X X Commands to ed have a simple and regular structure: zero or X more addresses followed by a single character command, pos- X sibly followed by parameters to the command. These X addresses specify one or more lines in the buffer. Missing X addresses are supplied by default. X X In general, only one command may appear on a line. Certain X commands allow the addition of text to the buffer. While ed X is accepting text, it is said to be in input mode. In this X mode, no commands are recognized; all input is merely col- X lected. Input mode is left by typing a period `.' alone at X the beginning of a line. X X Ed supports a limited form of regular expression notation. X A regular expression specifies a set of strings of charac- X ters. A member of this set of strings is said to be matched X by the regular expression. In the following specification X for regular expressions the word `character' means any char- X acter but newline. X X 1. Any character except a special character matches X itself. Special characters are the regular expression X delimiter plus \[. and sometimes ^*$. X X 2. A . matches any character. X X 3. A \ followed by any character except a digit or () X X X XPrinted 2/15/87 1 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X matches that character. X X 4. A nonempty string s bracketed [s] (or [^s]) matches any X character in (or not in) s. In s, \ has no special X meaning, and ] may only appear as the first letter. A X substring a-b, with a and b in ascending ASCII order, X stands for the inclusive range of ASCII characters. X X 5. A regular expression of form 1-4 followed by * matches X a sequence of 0 or more matches of the regular expres- X sion. X X 6. A regular expression, x, of form 1-8, bracketed \(x\) X matches what x matches. X X 7. A \ followed by a digit n matches a copy of the string X that the bracketed regular expression beginning with X the nth \( matched. X X 8. A regular expression of form 1-8, x, followed by a reg- X ular expression of form 1-7, y matches a match for x X followed by a match for y, with the x match being as X long as possible while still permitting a y match. X X 9. A regular expression of form 1-8 preceded by ^ (or fol- X lowed by $), is constrained to matches that begin at X the left (or end at the right) end of a line. X X 10. A regular expression of form 1-9 picks out the longest X among the leftmost matches in a line. X X 11. An empty regular expression stands for a copy of the X last regular expression encountered. X X Regular expressions are used in addresses to specify lines X and in one command (see s below) to specify a portion of a X line which is to be replaced. If it is desired to use one X of the regular expression metacharacters as an ordinary X character, that character may be preceded by `\'. This also X applies to the character bounding the regular expression X (often `/') and to `\' itself. X X To understand addressing in ed it is necessary to know that X at any time there is a current line. Generally speaking, the X current line is the last line affected by a command; how- X ever, the exact effect on the current line is discussed X under the description of the command. Addresses are con- X structed as follows. X X 1. The character `.' addresses the current line. X X 2. The character `$' addresses the last line of the X X X XPrinted 2/15/87 2 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X buffer. X X 3. A decimal number n addresses the n-th line of the X buffer. X X 4. `'x' addresses the line marked with the name x, which X must be a lower-case letter. Lines are marked with the X k command described below. X X 5. A regular expression enclosed in slashes `/' addresses X the line found by searching forward from the current X line and stopping at the first line containing a string X that matches the regular expression. If necessary the X search wraps around to the beginning of the buffer. X X 6. A regular expression enclosed in queries `?' addresses X the line found by searching backward from the current X line and stopping at the first line containing a string X that matches the regular expression. If necessary the X search wraps around to the end of the buffer. X X 7. An address followed by a plus sign `+' or a minus sign X `-' followed by a decimal number specifies that address X plus (resp. minus) the indicated number of lines. The X plus sign may be omitted. X X 8. If an address begins with `+' or `-' the addition or X subtraction is taken with respect to the current line; X e.g. `-5' is understood to mean `.-5'. X X 9. If an address ends with `+' or `-', then 1 is added X (resp. subtracted). As a consequence of this rule and X rule 8, the address `-' refers to the line before the X current line. Moreover, trailing `+' and `-' charac- X ters have cumulative effect, so `--' refers to the X current line less 2. X X 10. To maintain compatibility with earlier versions of the X editor, the character `^' in addresses is equivalent to X `-'. X X Commands may require zero, one, or two addresses. Commands X which require no addresses regard the presence of an address X as an error. Commands which accept one or two addresses X assume default addresses when insufficient are given. If X more addresses are given than such a command requires, the X last one or two (depending on what is accepted) are used. X X Addresses are separated from each other typically by a comma X `,'. They may also be separated by a semicolon `;'. In X this case the current line `.' is set to the previous X address before the next address is interpreted. This X X X XPrinted 2/15/87 3 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X feature can be used to determine the starting line for for- X ward and backward searches (`/', `?'). The second address X of any two-address sequence must correspond to a line fol- X lowing the line corresponding to the first address. The X special form `%' is an abbreviation for the address pair X `1,$'. X X In the following list of ed commands, the default addresses X are shown in parentheses. The parentheses are not part of X the address, but are used to show that the given addresses X are the default. X X As mentioned, it is generally illegal for more than one com- X mand to appear on a line. However, most commands may be X suffixed by `p' or by `l', in which case the current line is X either printed or listed respectively in the way discussed X below. Commands may also be suffixed by `n', meaning the X output of the command is to be line numbered. These suf- X fixes may be combined in any order. X X (.)a X X . X The append command reads the given text and appends it X after the addressed line. `.' is left on the last line X input, if there were any, otherwise at the addressed X line. Address `0' is legal for this command; text is X placed at the beginning of the buffer. X X (., .)c X X . X The change command deletes the addressed lines, then X accepts input text which replaces these lines. `.' is X left at the last line input; if there were none, it is X left at the line preceding the deleted lines. X X (., .)d X The delete command deletes the addressed lines from the X buffer. The line originally after the last line X deleted becomes the current line; if the lines deleted X were originally at the end, the new last line becomes X the current line. X X e filename X The edit command causes the entire contents of the X buffer to be deleted, and then the named file to be X read in. `.' is set to the last line of the buffer. X The number of characters read is typed. `filename' is X remembered for possible use as a default file name in a X subsequent r or w command. If `filename' is missing, X the remembered name is used. X X X XPrinted 2/15/87 4 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X E filename X This command is the same as e, except that no diagnos- X tic results when no w has been given since the last X buffer alteration. X X f filename X The filename command prints the currently remembered X file name. If `filename' is given, the currently X remembered file name is changed to `filename'. X X (1,$)g/regular expression/command list X In the global command, the first step is to mark every X line which matches the given regular expression. Then X for every such line, the given command list is executed X with `.' initially set to that line. A single command X or the first of multiple commands appears on the same X line with the global command. All lines of a multi- X line list except the last line must be ended with `\'. X A, i, and c commands and associated input are permit- X ted; the `.' terminating input mode may be omitted if X it would be on the last line of the command list. The X commands g and v are not permitted in the command list. X X (.)i X X X . X This command inserts the given text before the X addressed line. `.' is left at the last line input, X or, if there were none, at the line before the X addressed line. This command differs from the a com- X mand only in the placement of the text. X X (., .+1)j X This command joins the addressed lines into a single X line; intermediate newlines simply disappear. `.' is X left at the resulting line. X X ( . )kx X The mark command marks the addressed line with name x, X which must be a lower-case letter. The address form X `'x' then addresses this line. X X (., .)l X The list command prints the addressed lines in an unam- X biguous way: non-graphic characters are printed in X two-digit octal, and long lines are folded. The l com- X mand may be placed on the same line after any non-i/o X command. X X (., .)ma X The move command repositions the addressed lines after X X X XPrinted 2/15/87 5 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X the line addressed by a. The last of the moved lines X becomes the current line. X X (., .)n X The number command prints the addressed lines with line X numbers and a tab at the left. X X (., .)p X The print command prints the addressed lines. `.' is X left at the last line printed. The p command may be X placed on the same line after any non-i/o command. X X (., .)P X This command is a synonym for p. X X q The quit command causes ed to exit. No automatic write X of a file is done. X X Q This command is the same as q, except that no diagnos- X tic results when no w has been given since the last X buffer alteration. X X ($)r filename X The read command reads in the given file after the X addressed line. If no file name is given, the remem- X bered file name, if any, is used (see e and f com- X mands). The file name is remembered if there was no X remembered file name already. Address `0' is legal for X r and causes the file to be read at the beginning of X the buffer. If the read is successful, the number of X characters read is typed. `.' is left at the last line X read in from the file. X X ( ., .)s/regular expression/replacement/ or, X ( ., .)s/regular expression/replacement/g X The substitute command searches each addressed line for X an occurrence of the specified regular expression. On X each line in which a match is found, all matched X strings are replaced by the replacement specified, if X the global replacement indicator `g' appears after the X command. If the global indicator does not appear, only X the first occurrence of the matched string is replaced. X It is an error for the substitution to fail on all X addressed lines. Any punctuation character may be used X instead of `/' to delimit the regular expression and X the replacement. `.' is left at the last line substi- X tuted. X X An ampersand `&' appearing in the replacement is X replaced by the string matching the regular expression. X The special meaning of `&' in this context may be X suppressed by preceding it by `\'. The characters `\n' X X X XPrinted 2/15/87 6 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X where n is a digit, are replaced by the text matched by X the n-th regular subexpression enclosed between `\(' X and `\)'. When nested, parenthesized subexpressions X are present, n is determined by counting occurrences of X `\(' starting from the left. X X Lines may be split by substituting new-line characters X into them. The new-line in the replacement string must X be escaped by preceding it by `\'. X X One or two trailing delimiters may be omitted, implying X the `p' suffix. The special form `s' followed by no X delimiters repeats the most recent substitute command X on the addressed lines. The `s' may be followed by the X letters r (use the most recent regular expression for X the left hand side, instead of the most recent left X hand side of a substitute command), p (complement the X setting of the p suffix from the previous substitu- X tion), or g (complement the setting of the g suffix). X These letters may be combined in any order. X X (., .)ta X This command acts just like the m command, except that X a copy of the addressed lines is placed after address a X (which may be 0). `.' is left on the last line of the X copy. X X (., .)u X The undo command restores the buffer to it's state X before the most recent buffer modifying command. The X current line is also restored. Buffer modifying com- X mands are a, c, d, g, i, k, and v. For purposes of X undo, g and v are considered to be a single buffer X modifying command. Undo is its own inverse. X X When ed runs out of memory (at about 8000 lines on any X 16 bit mini-computer such as the PDP-11) This full undo X is not possible, and u can only undo the effect of the X most recent substitute on the current line. This res- X tricted undo also applies to editor scripts when ed is X invoked with the - option. X X (1, $)v/regular expression/command list X This command is the same as the global command g except X that the command list is executed g with `.' initially X set to every line except those matching the regular X expression. X X (1, $)w filename X The write command writes the addressed lines onto the X given file. If the file does not exist, it is created. X The file name is remembered if there was no remembered X X X XPrinted 2/15/87 7 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X file name already. If no file name is given, the X remembered file name, if any, is used (see e and f com- X mands). `.' is unchanged. If the command is success- X ful, the number of characters written is printed. X X (1, $)W filename X This command is the same as w, except that the X addressed lines are appended to the file. X X (1, $)wq filename X This command is the same as w except that afterwards a X q command is done, exiting the editor after the file is X written. X X x A key string is demanded from the standard input. X Later r, e and w commands will encrypt and decrypt the X text with this key by the algorithm of crypt(1). An X explicitly empty key turns off encryption. X (.+1)z or, X (.+1)zn X This command scrolls through the buffer starting at the X addressed line. 22 (or n, if given) lines are printed. X The last line printed becomes the current line. The X value n is sticky, in that it becomes the default for X future z commands. X X ($)= The line number of the addressed line is typed. `.' is X unchanged by this command. X X ! X The remainder of the line after the `!' is sent to X sh(1) to be interpreted as a command. `.' is X unchanged. X X (.+1,.+1) X An address alone on a line causes the addressed line to X be printed. A blank line alone is equivalent to X `.+1p'; it is useful for stepping through text. If two X addresses are present with no intervening semicolon, ed X prints the range of lines. If they are separated by a X semicolon, the second line is printed. X X If an interrupt signal (ASCII DEL) is sent, ed prints X `?interrupted' and returns to its command level. X X Some size limitations: 512 characters per line, 256 charac- X ters per global command list, 64 characters per file name, X and, on mini computers, 128K characters in the temporary X file. The limit on the number of lines depends on the X amount of core: each line takes 2 words. X X X X X XPrinted 2/15/87 8 X X X X X X XED(1-ucb) Pyramid OSx Operating System ED(1-ucb) X X X X When reading a file, ed discards ASCII NUL characters and X all characters after the last newline. It refuses to read X files containing non-ASCII characters. X XFILES X /tmp/e* X edhup: work is saved here if terminal hangs up X XSEE ALSO X B. W. Kernighan, A Tutorial Introduction to the ED Text Edi- X tor X B. W. Kernighan, Advanced editing on UNIX X ex(1), sed(1), crypt(1) X XDIAGNOSTICS X `?name' for inaccessible file; `?self-explanatory message' X for other errors. X X To protect against throwing away valuable work, a q or e X command is considered to be in error, unless a w has X occurred since the last buffer change. A second q or e will X be obeyed regardless. X XBUGS X The l command mishandles DEL. X The undo command causes marks to be lost on affected lines. X The x command, -x option, and special treatment of hangups X only work on UNIX. X X The -x option is not supported outside the United States. X X X X X X X X X X X X X X X X X X X X X X X X X XPrinted 2/15/87 9 X X X + END-OF-FILE ed.man chmod 'u=rw,g=r,o=r' \e\d\.\m\a\n set `sum \e\d\.\m\a\n` sum=$1 case $sum in 24742) :;; *) echo 'Bad sum in '\e\d\.\m\a\n >&2 esac echo Extracting \s\e\d\e\x\e\c\.\c sed 's/^X//' > \s\e\d\e\x\e\c\.\c << '+ END-OF-FILE '\s\e\d\e\x\e\c\.\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/* 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 /* {f}puts, {f}printf, getc/putc, f{re}open, fclose */ X#include /* 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#define GENSIZ 71 /* maximum genbuf size */ X 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} 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 * */ 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 */ X + END-OF-FILE sedexec.c chmod 'u=rw,g=r,o=r' \s\e\d\e\x\e\c\.\c set `sum \s\e\d\e\x\e\c\.\c` sum=$1 case $sum in 01617) :;; *) echo 'Bad sum in '\s\e\d\e\x\e\c\.\c >&2 esac exit 0