ast@cs.vu.nl (Andy Tanenbaum) (09/14/87)
: 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 <text>
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 <text>
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 <text>
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 !<shell command>
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)<newline>
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 <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#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 <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 */
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
eshase