[comp.os.minix] sed part 2 of 2

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