[comp.sources.amiga] v89i167: gnused -gnu stream/script editor, Part03/03

page%swap@Sun.COM (Bob Page) (07/13/89)

Submitted-by: ehoogerbeets@rose.waterloo.edu (Edwin Hoogerbeets)
Posting-number: Volume 89, Issue 167
Archive-name: unix/gnused.3

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	sed.1
#	sed.c
#	sed.man
# This is archive 3 of a 3-part kit.
# This archive created: Wed Jul 12 15:53:25 1989
echo "extracting sed.1"
sed 's/^X//' << \SHAR_EOF > sed.1
X.TH SED 1 "1989 May 26" "GNU Project"
X.UC 4
X.SH NAME
X.sp
Xsed \- stream editor
X.SH SYNOPSIS
X.sp
X.B sed
X[
X.B \-n
X] [
X.B \-e
X.I script
X] [
X.B \-f
X.I scriptfile
X] [
X.I files
X]
X...
X.SH DESCRIPTION
X.sp
X.I Sed
Xcopies the named files or the standard input to its standard output, while
Xperforming certain operations specified in the command line
X.IR script
Xor in the file
X.IR scriptfile.
XIf no filenames are specified, the default input is the standard input.
X.PP
X.I Sed
Xcyclically reads in input lines and places them in the pattern space.
XThe pattern space is selected by some or all of the commands in the
Xscript, and acted upon by these commands. The pattern space is then
Xwritten to the output, and the next input is read. 
X.PP
XThe script (on the command line or in the script file) is composed of one or
Xmore lines that have the following form:
X.PP
X[
X.B range
X] 
X.B function
X[ 
X.B arguments
X]
X.PP
XThe range refers to either a line address range or a regular expression in the
Xstyle of 
X.I ed(1).
XThe range selects which input lines the function should act upon. 
XIf the range is omitted, all input lines are selected.
X.PP
XThe line address is calculated sequentially from the first file to last,
Xin order. A line address range may be either a single line number or two
Xline numbers separated by a comma. A single number selects that line
Xnumber to act upon, and two line numbers separated by a comma inclusively 
Xselect all lines between those two numbers.
X.PP
XRegular expressions in sed differ from 
X.I ed(1) 
Xregular expressions in that the escape sequence `\en' matches a newline
Xembedded in the pattern space.  A $ character may also be used to
Xselect the last line of input.
X.PP
XThe following list of 
X.I sed
Xfunctions are preceded by a number indicating the maximum amount of line
Xnumbers valid in the 
X.B range 
Xof the function. A (1) would indicate that the function can only take
Xeffect after a certain line number and not a range of line numbers,
Xwhile a (2) would indicate that either one line number or two are valid
Xfor the function.
X.PP
XWhen a function takes a 
X.I text
Xargument, a newline in the 
X.I text
Xmay be escaped by placing a `\e' character before it, allowing multiline
Xtexts. 
X.PP
XEmpty commands are ignored.
X.PP
XA 
X.I wfile 
Xor 
X.I rfile 
Xargument to a function must be at the end of a command
Xline, and there must be exactly one space between the command and the
Xfile name argument. There can be at most 10 wfile and rfile arguments.
XAll 
X.I wfile
Xor 
X.I rfile
Xarguments are created before any input lines are read.
X.TP
X(1)\|a\e
X.br
X.ns
X.TP
X.I text
X.br
XAppend the
X.I text
Xto the output after the current pattern space.
X.TP
X.RI (2)\|b " label"
XBranch to the 
X.IR label 
Xdefined with the `:' command. 
XAn empty
X.I label
Ximplies a branch to the end of the script.
X.TP
X(2)\|c\e
X.br
X.ns
X.TP
X.I text
X.br
XChange text.
XThe pattern space is unconditionally deleted and the 
X.I text
Xis written to the output following the range.
X.TP
X(2)\|d
XDelete the pattern space.
X.TP
X(2)\|D
XDelete the initial segment of the 
Xpattern space up to and including the first newline.
X(the pattern space may contain more than one line of text -- see the `N'
Xcommand)
X.TP
X(2)\|g
XOverwrite the pattern space with the 
Xcontents of the hold space.
X.TP
X(2)\|G
XAppend the contents of the hold space to the pattern space.
X.TP
X(2)\|h
XHold the pattern space. The pattern space is copied into the hold space.
X.TP
X(2)\|H
XAppend the pattern space to the hold space.
X.TP
X(1)\|i\e
X.br
X.ns
X.TP
X.I text
X.br
XInsert.
XWrite
X.I text
Xto the output.
X.TP
X(2)\|n
XFlush the pattern space to the output and read the next input line into
Xthe pattern space.
X.TP
X(2)\|N
XAppend the next line of input to the pattern space, inserting a newline
Xcharacter between them.
X.TP
X(2)\|p
XPrint.
XWrite a copy of the pattern space to the output.
X.TP
X(2)\|P
XWrite a copy of the pattern space up to and including the first
Xnewline to the output.
X.TP
X(1)\|q
XQuit and do not start a new cycle.
X.TP
X.RI (2)\|r " rfile"
XRead the contents of
X.IR rfile 
Xand write them to the output.
X.TP
X.RI (2)\|s /regular\ expression/replacement/flags
XSearch for the 
X.I regular expression
Xin the pattern space and replace it with the
X.I replacement
Xstring.
XAny character may be used as a delimiter instead of the `/'.
XThe flags argument may be any number of the following:
X.RS
X.TP
Xg
XGlobally substitute the 
X.I replacement
Xfor the 
X.I regular expression.
XNormally, only the first match is found, but g forces matching of all
Xoccurances.
X.TP
Xp
XPrint the pattern space if a replacement was made.
X.TP
X.RI w " wfile"
XWrite
Xthe pattern space to the end of 
X.I wfile
Xif a replacement was made. This must be the last flag argument if it is
Xused.
X.RE
X.TP
X.RI (2)\|t " label"
XTest.
XIf a substitution was made since the last time an input line was read
Xinto the pattern space, or since the last `t' command, command branches 
Xto the 
X.I label.
XAn empty
X.I label
Xmeans to branch to the end of the script.
X.TP
X.RI (2)\|w " wfile"
XWrite the pattern space to the file
X.IR wfile .
X.TP
X.RI (2)\|x
XExchange the contents of the pattern and hold spaces.
X.TP
X.RI (2)\|y /string1/string2/
XYank all characters in
X.I string1
Xand replace them with the corresponding characters in 
X.I string2,
Xwhich should be of equal length as
X.I string1.
X.TP
X.RI (2)! " function"
XApply the 
X.I function
Xto those lines not selected by the command.
X.TP
X.RI (0)\|: " label"
XDeclare a 
X.I label
Xfor `b' and `t' commands to branch to.
X.TP
X(1)\|=
XWrite the current line number to the output.
X.TP
X(2)\|{
XPerform multiple commands on the range. The `{' character is followed by
Xone or more commands to perform on the pattern space. A '}' character
Xterminates the list of commands.
X.SH OPTIONS
X.TP 8
X.B \-n
Xsuppress the default output
X.TP
X.B \-e
Xindicates that the next argument is a script. Useful for scripts
Xstarting with a \- character.
X.TP
X.B \-f
Xindicates that the following argument is a file containing a script
Xof commands to
X.B Sed.
X.PP
X.SH SEE ALSO
Xed(1), grep(1), awk(1), lex(1)
X.PP
X.SH AVAILABILITY
X.PP
XGNU
X.I sed
Xis free; anyone may redistribute copies of
X.I sed
Xto
Xanyone under the terms stated in the
XGNU General Public License,
Xa copy of which may be found in each copy of
X.IR "GNU Emacs" .
XSee also the comment at the beginning of the source code file sed.c.
X.PP
XCopies of GNU
X.I sed
Xmay sometimes be received packaged with distributions of Unix systems,
Xbut it is never included in the scope of any license covering those
Xsystems.  Such inclusion violates the terms on which distribution
Xis permitted.  In fact, the primary purpose of the General Public
XLicense is to prohibit anyone from attaching any other restrictions
Xto redistribution of any of the Free Software Foundation programs.
X
SHAR_EOF
echo "extracting sed.c"
sed 's/^X//' << \SHAR_EOF > sed.c
X/* sed.c        emulate the UN*X sed command.
X   Copyright (C) 1989 Free Software Foundation, Inc.
X
X                       NO WARRANTY
X
X  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
XWHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
XRICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
XWITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
XBUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
XFITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
XDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
XCORRECTION.
X
X IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
XSTALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
XWHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
XLIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
XOTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
XUSE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
XDATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
XA FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
XPROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
XDAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
X
X                GENERAL PUBLIC LICENSE TO COPY
X
X  1. You may copy and distribute verbatim copies of this source file
Xas you receive it, in any medium, provided that you conspicuously
Xand appropriately publish on each copy a valid copyright notice
X"Copyright (C) 1989 Free Software Foundation, Inc.", and include
Xfollowing the copyright notice a verbatim copy of the above disclaimer
Xof warranty and of this License.
X
X  2. You may modify your copy or copies of this source file or
Xany portion of it, and copy and distribute such modifications under
Xthe terms of Paragraph 1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating
X    that you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish,
X    that in whole or in part contains or is a derivative of this
X    program or any part thereof, to be licensed at no charge to all
X    third parties on terms identical to those contained in this
X    License Agreement (except that you may choose to grant more
X    extensive warranty protection to third parties, at your option).
X
X    c) You may charge a distribution fee for the physical act of
X    transferring a copy, and you may at your option offer warranty
X    protection in exchange for a fee.
X
X  3. You may copy and distribute this program or any portion of it in
Xcompiled, executable or object code form under the terms of Paragraphs
X1 and 2 above provided that you do the following:
X
X    a) cause each such copy to be accompanied by the
X    corresponding machine-readable source code, which must
X    be distributed under the terms of Paragraphs 1 and 2 above; or,
X
X    b) cause each such copy to be accompanied by a
X    written offer, with no time limit, to give any third party
X    free (except for a nominal shipping charge) a machine readable
X    copy of the corresponding source code, to be distributed
X    under the terms of Paragraphs 1 and 2 above; or,
X
X    c) in the case of a recipient of this program in compiled, executable
X    or object code form (without the corresponding source code) you
X    shall cause copies you distribute to be accompanied by a copy
X    of the written offer of source code which you received along
X    with the copy you received.
X
X  4. You may not copy, sublicense, distribute or transfer this program
Xexcept as expressly provided under this License Agreement.  Any attempt
Xotherwise to copy, sublicense, distribute or transfer this program is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated.  However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X  5. If you wish to incorporate parts of this program into other free
Xprograms whose distribution conditions are different, write to the Free
XSoftware Foundation at 675 Mass Ave, Cambridge, MA 02139.  We have not yet
Xworked out a simple rule that can be stated here, but we will often permit
Xthis.  We will be guided by the two goals of preserving the free status of
Xall derivatives of our free software and of promoting the sharing and reuse of
Xsoftware.
X
X In other words, you are welcome to use, share and improve this program.
X You are forbidden to forbid anyone else to use, share and improve
X what you give them.   Help stamp out software-hoarding!  */
X
X#include <stdio.h>
X#include <ctype.h>
X#include "regex.h"
X
X#ifdef AZTEC_C
X#define memcpy(dst,src,n) movmem((src),(dst),(n))
X#define memset(src,value,howmany) setmem((src),(howmany),(value))
X#define bcopy(s, d, n) movmem((s),(d),(n))
X#endif
X
X/* Compile with 'gcc [-g] [-DHAS_UTILS] [-O] -o sed sed.c [-lutils]' */
X/* Use '-DHAS_UTILS', -lutils if you if you have hack's utils library */
X/* Add '-I. regex.c' if regex is not in the system include dir/library */
X#ifdef USG
X#define bcopy(s, d, n) ((void)memcpy((d),(s), (n)))
X#endif
X
X/* Struct vector is used to describe a chunk of a sed program.  There is one
X   vector for the main program, and one for each { } pair.
X */
Xstruct vector {
X        struct sed_cmd *v;
X        int v_length;
X        int v_allocated;
X        struct vector *up_one;
X        struct vector *next_one;
X};
X
X
X/* Goto structure is used to hold both GOTO's and labels.  There are two
X   separate lists, one of goto's, called 'jumps', and one of labels, called
X   'labels'.
X   the V element points to the descriptor for the program-chunk in which the
X   goto was encountered.
X   the v_index element counts which element of the vector actually IS the
X   goto/label.  The first element of the vector is zero.
X   the NAME element is the null-terminated name of the label.
X   next is the next goto/label in the list
X*/
X
Xstruct sed_label {
X        struct vector *v;
X        int v_index;
X        char *name;
X        struct sed_label *next;
X};
X
X/* ADDR_TYPE is zero for a null address,
X   one if addr_number is valid, or
X   two if addr_regex is valid,
X   three, if the address is '$'
X
X   Other values are undefined.
X */
X
X#define ADDR_NULL       0
X#define ADDR_NUM        1
X#define ADDR_REGEX      2
X#define ADDR_LAST       3
X
Xstruct addr {
X        int     addr_type;
X        struct re_pattern_buffer *addr_regex;
X        int     addr_number;
X};
X
X
X/* Aflags:  If the low order bit is set, a1 has been
X   matched; apply this command until a2 matches.
X   If the next bit is set, apply this command to all
X   lines that DON'T match the address(es).
X */
X
X#define A1_MATCHED_BIT  01
X#define ADDR_BANG_BIT   02
X
X
Xstruct sed_cmd {
X        struct addr a1,a2;
X        int aflags;
X
X        char cmd;
X
X        union {
X                /* This structure is used for a, i, and c commands */
X                struct {
X                        char *text;
X                        int text_len;
X                } cmd_txt;
X
X                /* This is used for b and t commands */
X                struct sed_cmd *label;
X
X                /* This for r and w commands */
X                FILE *io_file;
X
X                /* This for the hairy s command */
X                /* For the flags var:
X                   low order bit means the 'g' option was given,
X                   next bit means the 'p' option was given,
X                   and the next bit means a 'w' option was given,
X                      and wio_file contains the file to write to. */
X
X#define S_GLOBAL_BIT    01
X#define S_PRINT_BIT     02
X#define S_WRITE_BIT     04
X#define S_NUM_BIT       010
X
X                struct {
X                        struct re_pattern_buffer *regx;
X                        char *replacement;
X                        int replace_length;
X                        int flags;
X                        int numb;
X                        FILE *wio_file;
X                } cmd_regex;
X
X                /* This for the y command */
X                char *translate;
X
X                /* For { and } */
X                struct vector *sub;
X                struct sed_label *jump;
X        } x;
X};
X
X/* Sed operates a line at a time. */
Xstruct line {
X        char *text;             /* Pointer to line allocated by malloc. */
X        int length;             /* Length of text. */
X        int alloc;              /* Allocated space for text. */
X};
X
X/* This for all you losing compilers out there that can't handle void * */
X#ifdef __GNU__
X#define VOID void
X#else
X#define VOID char
X#endif
X
Xextern int optind;
Xextern char *optarg;
Xextern int getopt();
X
Xextern char *memchr();
Xextern VOID *memmove();
X
Xextern VOID *ck_malloc(),*ck_realloc();
Xextern VOID *init_buffer();
Xextern char *get_buffer();
Xextern FILE *ck_fopen();
Xextern void ck_fclose();
Xextern void ck_fwrite();
Xextern void flush_buffer();
Xextern void add1_buffer();
X
Xextern char *strdup();
X
Xstruct vector *compile_program();
Xvoid savchar();
Xstruct sed_label *setup_jump();
Xvoid line_copy();
Xvoid line_append();
Xvoid append_pattern_space();
X
X#ifndef HAS_UTILS
Xchar *myname;
X#else
Xextern char *myname;
X#endif
X/* This is a good idea */
Xchar *version_string = "GNU sed version 1.02 (or so)";
X/*      1.00    Began distributing this file
X *      1.01    Added s/re/rep/[digits]
X *              added #n as first line of script
X *              added filename globbing
X *              added 'l' command
X *              All in the name of POSIX
X *      1.02    Fixed D command
X *              Added Amiga porting ifdefs
X *              Edwin Hoogerbeets 1989
X */
X
X/* If set, don't write out the line unless explictly told to */
Xint no_default_output = 0;
X
X/* Current input line # */
Xint input_line_number = 0;
X
X/* Are we on the last input file? */
Xint last_input_file = 0;
X
X/* Have we hit EOF on the last input file? */
Xint input_EOF = 0;
X
X/* non-zero if a quit command has been hit */
Xint quit_cmd = 0;
X
X/* Have we done any replacements lately? */
Xint replaced = 0;
X
X/* How many '{'s are we executing at the moment */
Xint program_depth = 0;
X
X/* The current SED program */
Xstruct vector *the_program = 0;
X
X/* */
Xstruct sed_label *jumps = 0;
Xstruct sed_label *labels = 0;
X
X/* The 'current' input line. */
Xstruct line line;
X
X/* An input line that's been stored by later use by the program */
Xstruct line hold;
X
X/* A 'line' to append to the current line when it comes time to write it out */
Xstruct line append;
X
Xstatic char ONE_ADDR[] = "Command only uses one address";
Xstatic char NO_ADDR[] = "Command doesn't take any addresses";
Xstatic char LINE_JUNK[] ="Extra characters after command";
Xstatic char BAD_EOF[] =  "Unexpected End-of-file";
Xstatic char USAGE[] =   "Usage: %s [-n] [-e script...] [-f sfile...] [file...]\n";
Xstatic char NO_REGEX[] = "No previous regular expression";
X
Xstruct re_pattern_buffer *last_regex;
X
XFILE *input_file;
X
Xint bad_input = 0;
X
Xmain(argc,argv)
Xchar **argv;
X{
X        int opt;
X        int compiled = 0;
X        struct sed_label *go,*lbl;
X
X#ifdef AZTEC_C
X        extern long _Heapsize;
X
X        _Heapsize = 80 * 1024;
X#endif
X
X        myname=argv[0];
X        while((opt=getopt(argc,argv,"ne:f:"))!=EOF) {
X                switch(opt) {
X                case 'n':
X                        if(no_default_output)
X                                panic(USAGE);
X                        no_default_output++;
X                        break;
X                case 'e':
X                        compile_string(optarg);
X                        compiled++;
X                        break;
X                case 'f':
X                        compile_file(optarg);
X                        compiled++;
X                        break;
X                }
X        }
X        if(!compiled) {
X                if(argc<=optind)
X                        panic("No program to run\n");
X                compile_string(argv[optind]);
X                optind++;
X        }
X
X        for(go=jumps;go;go=go->next) {
X                for(lbl=labels;lbl;lbl=lbl->next)
X                        if(!strcmp(lbl->name,go->name))
X                                break;
X                if(!lbl)
X                        panic("Can't find label for jump to '%s'\n",go->name);
X                go->v->v[go->v_index].x.jump=lbl;
X        }
X
X        line.length=0;
X        line.alloc=50;
X        line.text=ck_malloc(50);
X
X        append.length=0;
X        append.alloc=50;
X        append.text=ck_malloc(50);
X
X        hold.length=0;
X        hold.alloc=50;
X        hold.text=ck_malloc(50);
X
X        if(argc<=optind) {
X                last_input_file++;
X                read_file("-");
X        } else while(optind<argc) {
X                if(optind==argc-1)
X                        last_input_file++;
X                read_file(argv[optind]);
X                optind++;
X                if(quit_cmd)
X                        break;
X        }
X        if(bad_input)
X                exit(2);
X        exit(0);
X}
X
Xchar *prog_start;
Xchar *prog_end;
Xchar *prog_cur;
X
Xchar *prog_name;
XFILE *prog_file;
X
Xint prog_line = 1;
X
Xcompile_string(str)
Xchar *str;
X{
X        prog_file = 0;
X        prog_line=0;
X        prog_start=prog_cur=str;
X        prog_end=str+strlen(str);
X        the_program=compile_program(the_program);
X}
X
Xcompile_file(str)
Xchar *str;
X{
X        FILE *file;
X        int ch;
X
X        prog_start=prog_cur=prog_end=0;
X        prog_name=str;
X        prog_line=1;
X        if(str[0]=='-' && str[1]=='\0')
X                prog_file=stdin;
X        else
X                prog_file=ck_fopen(str,"r");
X        ch=getc(prog_file);
X        if(ch=='#') {
X                ch=getc(prog_file);
X                if(ch=='n')
X                        no_default_output++;
X                while(ch!=EOF && ch!='\n')
X                        ch=getc(prog_file);
X        } else if(ch!=EOF)
X                ungetc(ch,prog_file);
X        the_program=compile_program(the_program);
X}
X
X#define MORE_CMDS 40
X
Xstruct vector *
Xcompile_program(vector)
Xstruct vector *vector;
X{
X        struct sed_cmd *cur_cmd;
X        int     ch;
X        int     slash;
X        VOID    *b;
X        char    *string;
X        int     num;
X
X        FILE *compile_filename();
X
X        if(!vector) {
X                vector=(struct vector *)ck_malloc(sizeof(struct vector));
X                vector->v=(struct sed_cmd *)ck_malloc(MORE_CMDS*sizeof(struct sed_cmd));
X                vector->v_allocated=MORE_CMDS;
X                vector->v_length=0;
X                vector->up_one = 0;
X                vector->next_one = 0;
X        }
X        for(;;) {
X                do ch=inchar();
X                while(ch!=EOF && (isspace(ch) || ch=='\n' || ch==';'));
X                if(ch==EOF)
X                        break;
X                savchar(ch);
X
X                if(vector->v_length==vector->v_allocated) {
X                        vector->v=(struct sed_cmd *)ck_realloc((VOID *)vector->v,(vector->v_length+MORE_CMDS)*sizeof(struct sed_cmd));
X                        vector->v_allocated+=MORE_CMDS;
X                }
X                cur_cmd=vector->v+vector->v_length;
X                vector->v_length++;
X
X                cur_cmd->a1.addr_type=0;
X                cur_cmd->a2.addr_type=0;
X                cur_cmd->aflags=0;
X                cur_cmd->cmd=0;
X
X        skip_comment:
X                if(compile_address(&(cur_cmd->a1))) {
X                        ch=inchar();
X                        if(ch==',') {
X                                do ch=inchar();
X                                while(ch!=EOF && isspace(ch));
X                                savchar(ch);
X                                if(compile_address(&(cur_cmd->a2)))
X                                        ;
X                                else
X                                        bad_prog("Unexpected ','");
X                        } else
X                                savchar(ch);
X                }
X                ch=inchar();
X                if(ch==EOF)
X                        break;
X new_cmd:
X                switch(ch) {
X                case '#':
X                        if(cur_cmd->a1.addr_type!=0)
X                                bad_prog(NO_ADDR);
X                        do ch=inchar();
X                        while(ch!=EOF && ch!='\n');
X                        goto skip_comment;
X                case '!':
X                        if(cur_cmd->aflags & ADDR_BANG_BIT)
X                                bad_prog("Multiple '!'s");
X                        cur_cmd->aflags|= ADDR_BANG_BIT;
X                        do ch=inchar();
X                        while(ch!=EOF && isspace(ch));
X                        if(ch==EOF)
X                                bad_prog(BAD_EOF);
X                        /* savchar(ch); */
X                        goto new_cmd;
X                case 'a':
X                case 'i':
X                        if(cur_cmd->a2.addr_type!=0)
X                                bad_prog(ONE_ADDR);
X                        /* Fall Through */
X                case 'c':
X                        cur_cmd->cmd=ch;
X                        if(inchar()!='\\' || inchar()!='\n')
X                                bad_prog(LINE_JUNK);
X                        b=init_buffer();
X                        while((ch=inchar())!=EOF && ch!='\n') {
X                                if(ch=='\\')
X                                        ch=inchar();
X                                add1_buffer(b,ch);
X                        }
X                        if(ch!=EOF)
X                                add1_buffer(b,ch);
X                        num=size_buffer(b);
X                        string=(char *)ck_malloc(num);
X                        bcopy(get_buffer(b),string,num);
X                        flush_buffer(b);
X                        cur_cmd->x.cmd_txt.text_len=num;
X                        cur_cmd->x.cmd_txt.text=string;
X                        break;
X                case '{':
X                        cur_cmd->cmd=ch;
X                        program_depth++;
X                        /* while((ch=inchar())!=EOF && ch!='\n')
X                                if(!isspace(ch))
X                                        bad_prog(LINE_JUNK); */
X                        cur_cmd->x.sub=compile_program((struct vector *)0);
X                        /* FOO JF is this the right thing to do? */
X                        break;
X                case '}':
X                        if(!program_depth)
X                                bad_prog("Unexpected '}'");
X                        --(vector->v_length);
X                        while((ch=inchar())!=EOF && ch!='\n')
X                                if(!isspace(ch))
X                                        bad_prog(LINE_JUNK);
X                        return vector;
X                case ':':
X                        cur_cmd->cmd=ch;
X                        if(cur_cmd->a1.addr_type!=0)
X                                bad_prog(": doesn't want any addresses");
X                        labels=setup_jump(labels,cur_cmd,vector);
X                        break;
X                case 'b':
X                case 't':
X                        cur_cmd->cmd=ch;
X                        jumps=setup_jump(jumps,cur_cmd,vector);
X                        break;
X                case 'q':
X                case '=':
X                        if(cur_cmd->a2.addr_type)
X                                bad_prog(ONE_ADDR);
X                        /* Fall Through */
X                case 'd':
X                case 'D':
X                case 'g':
X                case 'G':
X                case 'h':
X                case 'H':
X                case 'l':
X                case 'n':
X                case 'N':
X                case 'p':
X                case 'P':
X                case 'x':
X                        cur_cmd->cmd=ch;
X                        do      ch=inchar();
X                        while(ch!=EOF && isspace(ch) && ch!='\n' && ch!=';');
X                        if(ch!='\n' && ch!=';' && ch!=EOF)
X                                bad_prog(LINE_JUNK);
X                        break;
X
X                case 'r':
X                        if(cur_cmd->a2.addr_type!=0)
X                                bad_prog(ONE_ADDR);
X                        /* FALL THROUGH */
X                case 'w':
X                        cur_cmd->cmd=ch;
X                        cur_cmd->x.io_file=compile_filename(ch=='r');
X                        break;
X
X                case 's':
X                        cur_cmd->cmd=ch;
X                        b=init_buffer();
X                        slash=inchar();
X                        while((ch=inchar())!=EOF && ch!=slash) {
X                                if(ch!='\\') {
X                                        add1_buffer(b,ch);
X                                        continue;
X                                }
X                                ch=inchar();
X                                switch(ch) {
X                                case 'n':
X                                        add1_buffer(b,'\n');
X                                        break;
X                                /* case 'b':
X                                        add1_buffer(b,'\b');
X                                        break;
X                                case 'f':
X                                        add1_buffer(b,'\f');
X                                        break;
X                                case 'r':
X                                        add1_buffer(b,'\r');
X                                        break;
X                                case 't':
X                                        add1_buffer(b,'\t');
X                                        break; */
X                                case EOF:
X                                        break;
X                                default:
X                                        add1_buffer(b,'\\');
X                                        add1_buffer(b,ch);
X                                        break;
X                                }
X                        }
X                        if(ch==EOF)
X                                bad_prog(BAD_EOF);
X                        if(size_buffer(b)) {
X                                last_regex=(struct re_pattern_buffer *)ck_malloc(sizeof(struct re_pattern_buffer));
X                                last_regex->allocated=size_buffer(b);
X                                last_regex->buffer=ck_malloc(last_regex->allocated);
X                                last_regex->fastmap=0;
X                                last_regex->translate=0;
X                                re_compile_pattern(get_buffer(b),size_buffer(b),last_regex);
X                        } else if(!last_regex)
X                                bad_prog(NO_REGEX);
X                        cur_cmd->x.cmd_regex.regx=last_regex;
X                        flush_buffer(b);
X
X                        b=init_buffer();
X                        while((ch=inchar())!=EOF && ch!=slash) {
X                                if(ch=='\\') {
X                                        int ci;
X
X                                        ci=inchar();
X                                        if(ci!=EOF) {
X                                                if(ci!='\n')
X                                                        add1_buffer(b,ch);
X                                                add1_buffer(b,ci);
X                                        }
X                                } else
X                                        add1_buffer(b,ch);
X                        }
X                        cur_cmd->x.cmd_regex.replace_length=size_buffer(b);
X                        cur_cmd->x.cmd_regex.replacement=ck_malloc(cur_cmd->x.cmd_regex.replace_length);
X                        bcopy(get_buffer(b),cur_cmd->x.cmd_regex.replacement,cur_cmd->x.cmd_regex.replace_length);
X                        flush_buffer(b);
X
X                        cur_cmd->x.cmd_regex.flags=0;
X                        cur_cmd->x.cmd_regex.numb=0;
X
X                        if(ch==EOF)
X                                break;
X                        do {
X                                ch=inchar();
X                                switch(ch) {
X                                case 'p':
X                                        if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT)
X                                                bad_prog("multiple 'p' options to 's' command");
X                                        cur_cmd->x.cmd_regex.flags|=S_PRINT_BIT;
X                                        break;
X                                case 'g':
X                                        if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT)
X                                                cur_cmd->x.cmd_regex.flags&= ~S_NUM_BIT;
X                                        if(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT)
X                                                bad_prog("multiple 'g' options to 's' command");
X                                        cur_cmd->x.cmd_regex.flags|=S_GLOBAL_BIT;
X                                        break;
X                                case 'w':
X                                        cur_cmd->x.cmd_regex.flags|=S_WRITE_BIT;
X                                        cur_cmd->x.cmd_regex.wio_file=compile_filename(0);
X                                        ch='\n';
X                                        break;
X                                case '0': case '1': case '2': case '3':
X                                case '4': case '5': case '6': case '7':
X                                case '8': case '9':
X                                        if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT)
X                                                bad_prog("multiple number options to 's' command");
X                                        if(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT==0)
X                                                cur_cmd->x.cmd_regex.flags|=S_NUM_BIT;
X                                        num = 0;
X                                        while(isdigit(ch)) {
X                                                num=num*10+ch-'0';
X                                                ch=inchar();
X                                        }
X                                        savchar(ch);
X                                        cur_cmd->x.cmd_regex.numb=num;
X                                        break;
X                                case '\n':
X                                case ';':
X                                case EOF:
X                                        break;
X                                default:
X                                        bad_prog("Unknown option to 's'");
X                                        break;
X                                }
X                        } while(ch!=EOF && ch!='\n' && ch!=';');
X                        if(ch==EOF)
X                                break;
X                        break;
X
X                case 'y':
X                        cur_cmd->cmd=ch;
X                        string=ck_malloc(256);
X                        for(num=0;num<256;num++)
X                                string[num]=num;
X                        b=init_buffer();
X                        slash=inchar();
X                        while((ch=inchar())!=EOF && ch!=slash)
X                                add1_buffer(b,ch);
X                        cur_cmd->x.translate=string;
X                        string=get_buffer(b);
X                        for(num=size_buffer(b);num;--num) {
X                                ch=inchar();
X                                if(ch==EOF)
X                                        bad_prog(BAD_EOF);
X                                if(ch==slash)
X                                        bad_prog("strings for y command are different lengths");
X                                cur_cmd->x.translate[*string++]=ch;
X                        }
X                        flush_buffer(b);
X                        if(inchar()!=slash || inchar()!='\n')
X                                bad_prog(LINE_JUNK);
X                        break;
X
X                default:
X                        fprintf(stderr,"sed: Command: %c\n",ch);
X                        bad_prog("Unknown command");
X                }
X        }
X        return vector;
X}
X
Xbad_prog(why)
Xchar *why;
X{
X        if(prog_line)
X                fprintf(stderr,"%s: file %s line %d: %s\n",myname,prog_name,prog_line,why);
X        else
X                fprintf(stderr,"%s: %s\n",myname,why);
X        exit(1);
X}
X
Xint
Xinchar()
X{
X        int     ch;
X        if(prog_file)
X                ch=getc(prog_file);
X        else {
X                if(!prog_cur)
X                        return EOF;
X                else if(prog_cur==prog_end) {
X                        ch=EOF;
X                        prog_cur=0;
X                } else
X                        ch= *prog_cur++;
X        }
X        if(ch=='\n' && prog_line)
X                prog_line++;
X        return ch;
X}
X
Xvoid
Xsavchar(ch)
Xint ch;
X{
X        if(ch==EOF)
X                return;
X        if(ch=='\n' && prog_line>1)
X                --prog_line;
X        if(prog_file)
X                ungetc(ch,prog_file);
X        else
X                *--prog_cur=ch;
X}
X
X
Xcompile_address(addr)
Xstruct addr *addr;
X{
X        int     ch;
X        int     num;
X        char    *b,*init_buffer();
X
X        ch=inchar();
X
X        if(isdigit(ch)) {
X                num=ch-'0';
X                while((ch=inchar())!=EOF && isdigit(ch))
X                        num=num*10+ch-'0';
X                while(ch!=EOF && isspace(ch))
X                        ch=inchar();
X                savchar(ch);
X                addr->addr_type=ADDR_NUM;
X                addr->addr_number = num;
X                return 1;
X        } else if(ch=='/') {
X                addr->addr_type=ADDR_REGEX;
X                b=init_buffer();
X                while((ch=inchar())!=EOF && ch!='/') {
X                        add1_buffer(b,ch);
X                        if(ch=='\\') {
X                                ch=inchar();
X                                if(ch!=EOF)
X                                        add1_buffer(b,ch);
X                        }
X                }
X                if(size_buffer(b)) {
X                        last_regex=(struct re_pattern_buffer *)ck_malloc(sizeof(struct re_pattern_buffer));
X                        last_regex->allocated=size_buffer(b);
X                        last_regex->buffer=ck_malloc(last_regex->allocated);
X                        last_regex->fastmap=0;
X                        last_regex->translate=0;
X                        re_compile_pattern(get_buffer(b),size_buffer(b),last_regex);
X                } else if(!last_regex)
X                        bad_prog(NO_REGEX);
X
X                addr->addr_regex=last_regex;
X                flush_buffer(b);
X                do ch=inchar();
X                while(ch!=EOF && isspace(ch));
X                savchar(ch);
X                return 1;
X        } else if(ch=='$') {
X                addr->addr_type=ADDR_LAST;
X                do ch=inchar();
X                while(ch!=EOF && isspace(ch));
X                savchar(ch);
X                return 1;
X        } else
X                savchar(ch);
X        return 0;
X}
X
Xstruct sed_label *
Xsetup_jump(list,cmd,vec)
Xstruct sed_label *list;
Xstruct sed_cmd *cmd;
Xstruct vector *vec;
X{
X        struct sed_label *tmp;
X        VOID *b;
X        int ch;
X
X        b=init_buffer();
X        while((ch=inchar())!=EOF && ch!='\n')
X                add1_buffer(b,ch);
X        add1_buffer(b,'\0');
X        tmp=(struct sed_label *)ck_malloc(sizeof(struct sed_label));
X        tmp->v=vec;
X        tmp->v_index=cmd-vec->v;
X        tmp->name=strdup(get_buffer(b));
X        tmp->next=list;
X        flush_buffer(b);
X        return tmp;
X}
X
X#define NUM_FPS 32
Xstruct {
X        FILE *phile;
X        char *name;
X        int readit;
X} file_ptrs[NUM_FPS];
X
XFILE *
Xcompile_filename(readit)
X{
X        char *file_name;
X        int n;
X        VOID *b;
X        int ch;
X        char **globbed;
X        extern char **glob_filename();
X
X        if(inchar()!=' ')
X                bad_prog("missing ' ' before filename");
X        b=init_buffer();
X        while((ch=inchar())!=EOF && ch!='\n')
X                add1_buffer(b,ch);
X        add1_buffer(b,'\0');
X        file_name=get_buffer(b);
X        globbed=glob_filename(file_name);
X        if(globbed==0 || globbed==(char **)-1)
X                bad_prog("can't parse filename");
X        if(globbed[0] && globbed[1]!=0)
X                bad_prog("multiple files");
X        if(globbed[0])
X                file_name=globbed[0];
X        for(n=0;n<NUM_FPS;n++) {
X                if(!file_ptrs[n].name)
X                        break;
X                if(!strcmp(file_ptrs[n].name,file_name)) {
X                        if(file_ptrs[n].readit!=readit)
X                                bad_prog("Can't open file for both reading and writing");
X                        flush_buffer(b);
X                        return file_ptrs[n].phile;
X                }
X        }
X        if(n<NUM_FPS) {
X                file_ptrs[n].name=strdup(file_name);
X                file_ptrs[n].readit=readit;
X                file_ptrs[n].phile=ck_fopen(file_name,readit ? "r" : "a");
X                flush_buffer(b);
X                return file_ptrs[n].phile;
X        } else {
X                bad_prog("Hopelessely evil compiled in limit on number of open files.  re-compile sed\n");
X                return 0;
X        }
X}
X
Xread_file(name)
Xchar *name;
X{
X        if(*name=='-' && name[1]=='\0')
X                input_file=stdin;
X        else {
X                input_file=fopen(name,"r");
X                if(input_file==0) {
X                        bad_input++;
X                        return;
X                }
X        }
X        while(read_pattern_space()) {
X                execute_program(the_program);
X                if(!no_default_output)
X                        ck_fwrite(line.text,1,line.length,stdout);
X                if(append.length) {
X                        ck_fwrite(append.text,1,append.length,stdout);
X                        append.length=0;
X                }
X                if(quit_cmd)
X                        break;
X        }
X        ck_fclose(input_file);
X}
X
Xexecute_program(vec)
Xstruct vector *vec;
X{
X        struct sed_cmd *cur_cmd;
X        int     n;
X        int addr_matched;
X        static int end_cycle;
X
X        int start;
X        int remain;
X        int offset;
X
X        static struct line tmp;
X        struct line t;
X        char *rep,*rep_end,*rep_next,*rep_cur;
X
X        struct re_registers regs;
X        int count = 0;
X        void str_append();
X
X
X        end_cycle = 0;
X
X        for(cur_cmd=vec->v,n=vec->v_length;n;cur_cmd++,n--) {
X
X        exe_loop:
X                addr_matched=0;
X                if(cur_cmd->aflags&A1_MATCHED_BIT) {
X                        addr_matched=1;
X                        if(match_address(&(cur_cmd->a2)))
X                                cur_cmd->aflags&=~A1_MATCHED_BIT;
X                } else if(match_address(&(cur_cmd->a1))) {
X                        addr_matched=1;
X                        if(cur_cmd->a2.addr_type!=ADDR_NULL)
X                                cur_cmd->aflags|=A1_MATCHED_BIT;
X                }
X                if(cur_cmd->aflags&ADDR_BANG_BIT)
X                        addr_matched= !addr_matched;
X                if(!addr_matched)
X                        continue;
X                switch(cur_cmd->cmd) {
X                case '{':       /* Execute sub-program */
X                        execute_program(cur_cmd->x.sub);
X                        break;
X
X                case ':':       /* Executing labels is easy. */
X                        break;
X
X                case '=':
X                        printf("%d\n",input_line_number);
X                        break;
X
X                case 'a':
X                        if(append.alloc-append.length<cur_cmd->x.cmd_txt.text_len) {
X                                append.text=ck_realloc(append.text,append.alloc+cur_cmd->x.cmd_txt.text_len);
X                                append.alloc+=cur_cmd->x.cmd_txt.text_len;
X                        }
X                        bcopy(cur_cmd->x.cmd_txt.text,append.text+append.length,cur_cmd->x.cmd_txt.text_len);
X                        append.length+=cur_cmd->x.cmd_txt.text_len;
X                        break;
X
X                case 'b':
X                        if(!cur_cmd->x.jump)
X                                end_cycle++;
X                        else {
X                                struct sed_label *j = cur_cmd->x.jump;
X
X                                n= j->v->v_length - j->v_index;
X                                cur_cmd= j->v->v + j->v_index;
X                                goto exe_loop;
X                        }
X                        break;
X
X                case 'c':
X                        line.length=0;
X                        if(!(cur_cmd->aflags&A1_MATCHED_BIT))
X                                ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout);
X                        end_cycle++;
X                        break;
X
X                case 'd':
X                        line.length=0;
X                        end_cycle++;
X                        break;
X
X                case 'D':
X                        {
X                                char *tmp;
X                                int newlength;
X
X                                tmp=memchr(line.text,'\n',line.length);
X                                newlength=line.length-(tmp-line.text+1);
X                                if(newlength)
X                                        memmove(line.text,tmp,newlength);
X                                line.length=newlength;
X                        }
X                        end_cycle++;
X                        break;
X
X                case 'g':
X                        line_copy(&hold,&line);
X                        break;
X
X                case 'G':
X                        line_append(&hold,&line);
X                        break;
X
X                case 'h':
X                        line_copy(&line,&hold);
X                        break;
X
X                case 'H':
X                        line_append(&line,&hold);
X                        break;
X
X                case 'i':
X                        ck_fwrite(cur_cmd->x.cmd_txt.text,1,cur_cmd->x.cmd_txt.text_len,stdout);
X                        break;
X
X                case 'l':
X                        {
X                                char *tmp;
X                                int n;
X                                int width = 0;
X
X                                n=line.length;
X                                tmp=line.text;
X                                /* Use --n so this'll skip the trailing newline */
X                                while(--n) {
X                                        if(width>77) {
X                                                width=0;
X                                                putchar('\n');
X                                        }
X                                        if(isprint(*tmp)) {
X                                                putchar(*tmp);
X                                                width++;
X                                        } else switch(*tmp) {
X                                        case '\0':
X                                                printf("\\0");
X                                                width+=2;
X                                                break;
X                                        case '\a':
X                                                printf("\\a");
X                                                width+=2;
X                                                break;
X                                        case '\b':
X                                                printf("\\b");
X                                                width+=2;
X                                                break;
X                                        case '\f':
X                                                printf("\\f");
X                                                width+=2;
X                                                break;
X                                        case '\n':
X                                                printf("\\n");
X                                                width+=2;
X                                                break;
X                                        case '\r':
X                                                printf("\\r");
X                                                width+=2;
X                                                break;
X                                        case '\t':
X                                                printf("\\t");
X                                                width+=2;
X                                                break;
X                                        case '\v':
X                                                printf("\\v");
X                                                width+=2;
X                                                break;
X                                        default:
X                                                printf("/%02x",(*tmp)&0xFF);
X                                                width+=2;
X                                                break;
X                                        }
X                                        tmp++;
X                                }
X                                putchar('\n');
X                        }
X                        break;
X
X                case 'n':
X                        ck_fwrite(line.text,1,line.length,stdout);
X                        read_pattern_space();
X                        break;
X
X                case 'N':
X                        append_pattern_space();
X                        break;
X
X                case 'p':
X                        ck_fwrite(line.text,1,line.length,stdout);
X                        break;
X
X                case 'P':
X                        {
X                                char *tmp;
X
X                                tmp=memchr(line.text,'\n',line.length);
X                                ck_fwrite(line.text,1,line.length-(tmp-line.text),stdout);
X                        }
X                        break;
X
X                case 'q':
X                        quit_cmd++;
X                        end_cycle++;
X                        break;
X
X                case 'r':
X                        {
X                                int n;
X                                char tmp_buf[1024];
X
X                                rewind(cur_cmd->x.io_file);
X                                while((n=fread(tmp_buf,sizeof(char),1024,cur_cmd->x.io_file))>0)
X                                        ck_fwrite(tmp_buf,sizeof(char),n,stdout);
X                                if(ferror(cur_cmd->x.io_file))
X                                        panic("Read error on input file to 'r' command\n");
X                        }
X                        break;
X
X                case 's':
X                        if(!tmp.alloc) {
X                                tmp.alloc=50;
X                                tmp.text=ck_malloc(50);
X                        }
X                        count=0;
X                        start = 0;
X                        remain=line.length-1;
X                        tmp.length=0;
X                        rep = cur_cmd->x.cmd_regex.replacement;
X                        rep_end=rep+cur_cmd->x.cmd_regex.replace_length;
X
X                        while((offset = re_search(cur_cmd->x.cmd_regex.regx,
X                                                  line.text,
X                                                  line.length-1,
X                                                  start,
X                                                  remain,
X                                                  &regs))>=0) {
X                                count++;
X                                if(offset-start)
X                                        str_append(&tmp,line.text+start,offset-start);
X
X                                if(cur_cmd->x.cmd_regex.flags&S_NUM_BIT) {
X                                        if(count!=cur_cmd->x.cmd_regex.numb) {
X                                                str_append(&tmp,line.text+regs.start[0],regs.end[0]-regs.start[0]);
X                                                start = (offset == regs.end[0] ? offset + 1 : regs.end[0]);
X                                                remain = (line.length-1) - start;
X                                                continue;
X                                        }
X                                }
X
X                                for(rep_next=rep_cur=rep;rep_next<rep_end;rep_next++) {
X                                        if(*rep_next=='&') {
X                                                if(rep_next-rep_cur)
X                                                        str_append(&tmp,rep_cur,rep_next-rep_cur);
X                                                str_append(&tmp,line.text+regs.start[0],regs.end[0]-regs.start[0]);
X                                                rep_cur=rep_next+1;
X                                        } else if(*rep_next=='\\') {
X                                                if(rep_next-rep_cur)
X                                                        str_append(&tmp,rep_cur,rep_next-rep_cur);
X                                                rep_next++;
X                                                if(rep_next!=rep_end) {
X                                                        int n;
X
X                                                        if(*rep_next>='0' && *rep_next<='9') {
X                                                                n= *rep_next -'0';
X                                                                str_append(&tmp,line.text+regs.start[n],regs.end[n]-regs.start[n]);
X                                                        } else
X                                                                str_append(&tmp,&rep_next,1);
X                                                }
X                                                rep_cur=rep_next+1;
X                                        }
X                                }
X                                if(rep_next-rep_cur)
X                                        str_append(&tmp,rep_cur,rep_next-rep_cur);
X                                start = (offset == regs.end[0] ? offset + 1 : regs.end[0]);
X                                remain = (line.length-1) - start;
X                                if(!(cur_cmd->x.cmd_regex.flags&S_GLOBAL_BIT))
X                                        break;
X                        }
X                        if(!count)
X                                break;
X                        replaced=1;
X                        str_append(&tmp,line.text+regs.end[0],line.length-regs.end[0]);
X                        t.text=line.text;
X                        t.length=line.length;
X                        t.alloc=line.alloc;
X                        line.text=tmp.text;
X                        line.length=tmp.length;
X                        line.alloc=tmp.alloc;
X                        tmp.text=t.text;
X                        tmp.length=t.length;
X                        tmp.alloc=t.alloc;
X                        if(cur_cmd->x.cmd_regex.flags&S_WRITE_BIT)
X                                ck_fwrite(line.text,1,line.length,cur_cmd->x.cmd_regex.wio_file);
X                        if(cur_cmd->x.cmd_regex.flags&S_PRINT_BIT)
X                                ck_fwrite(line.text,1,line.length,stdout);
X                        break;
X
X                case 't':
X                        if(replaced) {
X                                replaced = 0;
X                                if(!cur_cmd->x.jump)
X                                        end_cycle++;
X                                else {
X                                        struct sed_label *j = cur_cmd->x.jump;
X
X                                        n= j->v->v_length - j->v_index;
X                                        cur_cmd= j->v->v + j->v_index;
X                                        goto exe_loop;
X                                }
X                        }
X                        break;
X
X                case 'w':
X                        ck_fwrite(line.text,1,line.length,cur_cmd->x.io_file);
X                        break;
X
X                case 'x':
X                        {
X                                struct line tmp;
X
X                                tmp=line;
X                                line=hold;
X                                hold=tmp;
X                        }
X                        break;
X
X                case 'y':
X                        {
X                                unsigned char *p,*e;
X
X                                for(p=(unsigned char *)(line.text),e=p+line.length;p<e;p++)
X                                        *p=cur_cmd->x.translate[*p];
X                        }
X                        break;
X
X                default:
X                        panic("INTERNAL ERROR: Bad cmd %c\n",cur_cmd->cmd);
X                }
X                if(end_cycle)
X                        break;
X        }
X}
X
X
Xmatch_address(addr)
Xstruct addr *addr;
X{
X        switch(addr->addr_type) {
X        case ADDR_NULL:
X                return 1;
X        case ADDR_NUM:
X                return (input_line_number==addr->addr_number);
X
X        case ADDR_REGEX:
X                return (re_search(addr->addr_regex,
X                                  line.text,
X                                  line.length-1,
X                                  0,
X                                  line.length-1,
X                                  0)>=0) ? 1 : 0;
X
X        case ADDR_LAST:
X                return (input_EOF) ? 1 : 0;
X
X        default:
X                panic("INTERNAL ERROR: bad address type\n");
X                break;
X        }
X        return -1;
X}
X
Xint
Xread_pattern_space()
X{
X        int n;
X        char *p;
X        int ch;
X
X        p=line.text;
X        n=line.alloc;
X
X        input_line_number++;
X        replaced=0;
X        for(;;) {
X                ch=getc(input_file);
X                if(ch==EOF) {
X                        if(n==line.alloc)
X                                return 0;
X                        *p++='\n';
X                        --n;
X                        line.length=line.alloc-n;
X                        break;
X                }
X                *p++=ch;
X                --n;
X                if(ch=='\n') {
X                        line.length=line.alloc-n;
X                        break;
X                }
X                if(n==0) {
X                        line.text=ck_realloc(line.text,line.alloc*2);
X                        p=line.text+line.alloc;
X                        n=line.alloc;
X                        line.alloc*=2;
X                }
X        }
X        ch=getc(input_file);
X        if(ch!=EOF)
X                ungetc(ch,input_file);
X        else if(last_input_file)
X                input_EOF++;
X        return 1;
X}
X
Xvoid
Xappend_pattern_space()
X{
X        char *p;
X        int n;
X        int ch;
X
X        p=line.text+line.length;
X        n=line.alloc-line.length;
X
X        input_line_number++;
X        replaced=0;
X        for(;;) {
X                ch=getc(input_file);
X                if(ch==EOF) {
X                        if(n==line.alloc)
X                                return;
X                        *p++='\n';
X                        --n;
X                        line.length=line.alloc-n;
X                        break;
X                }
X                *p++=ch;
X                --n;
X                if(ch=='\n') {
X                        line.length=line.alloc-n;
X                        break;
X                }
X                if(n==0) {
X                        line.text=ck_realloc(line.text,line.alloc*2);
X                        p=line.text+line.alloc;
X                        n=line.alloc;
X                        line.alloc*=2;
X                }
X        }
X        ch=getc(input_file);
X        if(ch!=EOF)
X                ungetc(ch,input_file);
X        else if(last_input_file)
X                input_EOF++;
X}
X
Xvoid
Xline_copy(from,to)
Xstruct line *from,*to;
X{
X        if(from->length>to->alloc) {
X                to->alloc=from->length;
X                to->text=ck_realloc(to->text,to->alloc);
X        }
X        bcopy(from->text,to->text,from->length);
X        to->length=from->length;
X}
X
Xvoid
Xline_append(from,to)
Xstruct line *from,*to;
X{
X        if(from->length>(to->alloc-to->length)) {
X                to->alloc+=from->length;
X                to->text=ck_realloc(to->text,to->alloc);
X        }
X        bcopy(from->text,to->text+to->length,from->length);
X        to->length+=from->length;
X}
X
Xvoid
Xstr_append(to,string,length)
Xstruct line *to;
Xchar *string;
Xint length;
X{
X        if(length>to->alloc-to->length) {
X                to->alloc+=length;
X                to->text=ck_realloc(to->text,to->alloc);
X        }
X        bcopy(string,to->text+to->length,length);
X        to->length+=length;
X}
X
X#ifndef HAS_UTILS
X
X#ifdef __STDC__
X#include "stdarg.h"
X
Xpanic(str)
Xchar *str;
X{
X        va_list iggy;
X
X        va_start(iggy,str);
X        fprintf(stderr,"%s: ",myname);
X#ifdef NO_VFPRINTF
X        _doprnt(str,&iggy,stderr);
X#else
X        vfprintf(stderr,str,iggy);
X#endif
X        putc('\n',stderr);
X        va_end(iggy);
X        exit(4);
X}
X
X#else
X#ifdef AZTEC_C
X
X/* this kludge inserted because of lack of vfprintf or _doprnt in the
X * Manx libs, and this was easier than rewriting printf, which I have
X * no source to! Delete this if and when vfprintf is written on the Amiga.
X */
Xpanic(str,arg1,arg2,arg3)
Xchar *str;
Xint arg1,arg2,arg3;
X{
X  fprintf(stderr,"%s: ",myname);
X  fprintf(stderr,str,arg1,arg2,arg3);
X  fprintf(stderr,"\n");
X  exit(4);
X}
X#else
X#include "varargs.h"
X
Xpanic(str,va_alist)
Xchar *str;
Xva_dcl
X{
X        va_list iggy;
X
X        va_start(iggy);
X        fprintf(stderr,"%s: ",myname);
X#ifdef NO_VFPRINTF
X        _doprnt(str,&iggy,stderr);
X#else
X        vfprintf(stderr,str,iggy);
X#endif
X        putc('\n',stderr);
X        va_end(iggy);
X        exit(4);
X}
X
X#endif
X#endif
X
X#define N_FILE 20
X
Xstruct id {
X        FILE *fp;
X        char *name;
X};
X
Xstatic struct id __id_s[N_FILE];
X
Xchar *
X__fp_name(fp)
XFILE *fp;
X{
X        int n;
X
X        for(n=0;n<N_FILE;n++) {
X                if(__id_s[n].fp==fp)
X                        return __id_s[n].name;
X        }
X        return "{Unknown file pointer}";
X}
X
X/* Panic on failing fopen */
XFILE *
Xck_fopen(name,mode)
Xchar *name;
Xchar *mode;
X{
X        FILE    *ret;
X        int     n;
X
X        ret=fopen(name,mode);
X        if(ret==(FILE *)0)
X                panic("Couldn't open file %s\n",name);
X        for(n=0;n<N_FILE;n++) {
X                if(ret==__id_s[n].fp) {
X                        free((VOID *)__id_s[n].name);
X                        __id_s[n].name=(char *)ck_malloc(strlen(name)+1);
X                        strcpy(__id_s[n].name,name);
X                        break;
X                }
X        }
X        if(n==N_FILE) {
X                for(n=0;n<N_FILE;n++)
X                        if(__id_s[n].fp==(FILE *)0)
X                                break;
X                if(n==N_FILE)
X                        panic("Internal error: too many files open\n");
X                __id_s[n].fp=ret;
X                __id_s[n].name=(char *)ck_malloc(strlen(name)+1);
X                strcpy(__id_s[n].name,name);
X        }
X        return ret;
X}
X
Xvoid
Xck_fwrite(ptr,size,nmemb,stream)
Xchar *ptr;
Xint size,nmemb;
XFILE *stream;
X{
X        if(fwrite(ptr,size,nmemb,stream)!=nmemb)
X                panic("couldn't write %d items to %s",nmemb,__fp_name(stream));
X}
X
Xvoid
Xck_fclose(stream)
XFILE *stream;
X{
X        if(fclose(stream)==EOF)
X                panic("Couldn't close %s\n",__fp_name(stream));
X}
X
XVOID *
Xck_malloc(size)
Xint size;
X{
X        VOID *ret;
X        VOID *malloc();
X
X        ret=malloc(size);
X        if(ret==(VOID *)0)
X                panic("Couldn't allocate memory\n");
X        return ret;
X}
X
XVOID *
Xck_realloc(ptr,size)
XVOID *ptr;
Xint size;
X{
X        VOID *ret;
X        VOID *realloc();
X
X        ret=realloc(ptr,size);
X        if(ret==(VOID *)0)
X                panic("Couldn't re-allocate memory\n");
X        return ret;
X}
X
Xchar *
Xstrdup(str)
Xchar *str;
X{
X        char *ret;
X
X        ret=(char *)ck_malloc(strlen(str)+2);
X        strcpy(ret,str);
X        return ret;
X}
X
X
X#ifndef AZTEC_C
X/*
X * memmove - copy bytes, being careful about overlap.
X */
X
XVOID *
Xmemmove(dst, src, size)
XVOID *dst;
XVOID *src;
Xint size;
X{
X        register char *d;
X        register char *s;
X        register int n;
X
X        if (size <= 0)
X                return(dst);
X
X        s = (char *)src;
X        d = (char *)dst;
X        if (s <= d && s + (size-1) >= d) {
X                /* Overlap, must copy right-to-left. */
X                s += size-1;
X                d += size-1;
X                for (n = size; n > 0; n--)
X                        *d-- = *s--;
X        } else
X                for (n = size; n > 0; n--)
X                        *d++ = *s++;
X
X        return(dst);
X}
X#else
X
XVOID *
Xmemmove(dst,src,size)
XVOID *dst;
XVOID *src;
Xint size;
X{
X  return ( (VOID *) movmem(src,dst,size) );
X}
X
XVOID *memchr(mem,val,len)
Xchar *mem;
Xint val,len;
X{
X  register int index = 0;
X
X  while ( index < len ) {
X    if ( mem[index] == (char) val ) {
X      return(&mem[index]);
X    }
X    ++index;
X  }
X
X  return(NULL);
X}
X
X#endif
X
X/* Implement a variable sized buffer of 'stuff'.  We don't know what it is,
X   nor do we care, as long as it doesn't mind being aligned on a char boundry.
X   'b' could be made non-global, with a little work, so we could have more
X   than one buffer, but we don't need more than one, so why bother? */
X
X
Xstruct buffer {
X        int     allocated;
X        int     length;
X        char    *b;
X};
X
X#define MIN_ALLOCATE 50
X
XVOID *
Xinit_buffer()
X{
X        struct buffer *b;
X
X        b=(struct buffer *)ck_malloc(sizeof(struct buffer));
X        b->allocated=MIN_ALLOCATE;
X        b->b=(char *)ck_malloc(MIN_ALLOCATE);
X        b->length=0;
X        return (VOID *)b;
X}
X
Xvoid
Xflush_buffer(bb)
XVOID *bb;
X{
X        struct buffer *b;
X
X        b=(struct buffer *)bb;
X        free(b->b);
X        b->b=0;
X        b->allocated=0;
X        b->length=0;
X        free(b);
X}
X
Xint
Xsize_buffer(b)
XVOID *b;
X{
X        struct buffer *bb;
X
X        bb=(struct buffer *)b;
X        return bb->length;
X}
X
Xvoid
Xadd_buffer(bb,p,n)
XVOID *bb;
Xchar *p;
Xint n;
X{
X        struct buffer *b;
X
X        b=(struct buffer *)bb;
X        if(b->length+n>b->allocated) {
X                b->allocated*=2;
X                b->b=(char *)ck_realloc(b->b,b->allocated);
X        }
X        bcopy(p,b->b+b->length,n);
X        b->length+=n;
X}
X
Xvoid
Xadd1_buffer(bb,ch)
XVOID *bb;
Xint ch;
X{
X        struct buffer *b;
X
X        b=(struct buffer *)bb;
X        if(b->length+1>b->allocated) {
X                b->allocated*=2;
X                b->b=(char *)ck_realloc(b->b,b->allocated);
X        }
X        b->b[b->length]=ch;
X        b->length++;
X}
X
Xchar *
Xget_buffer(bb)
XVOID *bb;
X{
X        struct buffer *b;
X
X        b=(struct buffer *)bb;
X        return b->b;
X}
X
X#endif
SHAR_EOF
echo "extracting sed.man"
sed 's/^X//' << \SHAR_EOF > sed.man
X
X
X
XSED(1)                    User Commands                     SED(1)
X
X
X
XNAME
X     sed - stream editor
X
XSYNOPSIS
X     sed [ -n ] [ -e script ] [ -f scriptfile ] [ files ]
X
XDESCRIPTION
X     Sed copies the named files or the standard input to its
X     standard output, while performing certain operations speci-
X     fied in the command line script or in the file scriptfile.
X     If no filenames are specified, the default input is the
X     standard input.
X
X     Sed cyclically reads in input lines and places them in the
X     pattern space.  The pattern space is selected by some or all
X     of the commands in the script, and acted upon by these com-
X     mands. The pattern space is then written to the output, and
X     the next input is read.
X
X     The script (on the command line or in the script file) is
X     composed of one or more lines that have the following form:
X
X     [ range ] function [ arguments ]
X
X     The range refers to either a line address range or a regular
X     expression in the style of ed(1). The range selects which
X     input lines the function should act upon. If the range is
X     omitted, all input lines are selected.
X
X     The line address is calculated sequentially from the first
X     file to last, in order. A line address range may be either a
X     single line number or two line numbers separated by a comma.
X     A single number selects that line number to act upon, and
X     two line numbers separated by a comma inclusively select all
X     lines between those two numbers.
X
X     Regular expressions in sed differ from ed(1) regular expres-
X     sions in that the escape sequence `\n' matches a newline
X     embedded in the pattern space.  A $ character may also be
X     used to select the last line of input.
X
X     The following list of sed functions are preceded by a number
X     indicating the maximum amount of line numbers valid in the
X     range of the function. A (1) would indicate that the func-
X     tion can only take effect after a certain line number and
X     not a range of line numbers, while a (2) would indicate that
X     either one line number or two are valid for the function.
X
X     When a function takes a text argument, a newline in the text
X     may be escaped by placing a `\' character before it, allow-
X     ing multiline texts.
X
X
X
X
XGNU Project                1989 May 26                          1
X
X
X
X
X
X
XSED(1)                    User Commands                     SED(1)
X
X
X
X     Empty commands are ignored.
X
X     A wfile or rfile argument to a function must be at the end
X     of a command line, and there must be exactly one space
X     between the command and the file name argument. There can be
X     at most 10 wfile and rfile arguments.  All wfile or rfile
X     arguments are created before any input lines are read.
X
X     (1)a\
X     text
X          Append the text to the output after the current pattern
X          space.
X
X     (2)b label
X          Branch to the label defined with the `:' command. An
X          empty label implies a branch to the end of the script.
X
X     (2)c\
X     text
X          Change text.  The pattern space is unconditionally
X          deleted and the text is written to the output following
X          the range.
X
X     (2)d Delete the pattern space.
X
X     (2)D Delete the initial segment of the pattern space up to
X          and including the first newline.  (the pattern space
X          may contain more than one line of text -- see the `N'
X          command)
X
X     (2)g Overwrite the pattern space with the contents of the
X          hold space.
X
X     (2)G Append the contents of the hold space to the pattern
X          space.
X
X     (2)h Hold the pattern space. The pattern space is copied
X          into the hold space.
X
X     (2)H Append the pattern space to the hold space.
X
X     (1)i\
X     text
X          Insert.  Write text to the output.
X
X     (2)n Flush the pattern space to the output and read the next
X          input line into the pattern space.
X
X     (2)N Append the next line of input to the pattern space,
X          inserting a newline character between them.
X
X
X
X
X
XGNU Project                1989 May 26                          2
X
X
X
X
X
X
XSED(1)                    User Commands                     SED(1)
X
X
X
X     (2)p Print.  Write a copy of the pattern space to the out-
X          put.
X
X     (2)P Write a copy of the pattern space up to and including
X          the first newline to the output.
X
X     (1)q Quit and do not start a new cycle.
X
X     (2)r rfile
X          Read the contents of rfile and write them to the out-
X          put.
X
X     (2)s/regular expression/replacement/flags
X          Search for the regular expression in the pattern space
X          and replace it with the replacement string.  Any char-
X          acter may be used as a delimiter instead of the `/'.
X          The flags argument may be any number of the following:
X
X          g    Globally substitute the replacement for the regu-
X               lar expression. Normally, only the first match is
X               found, but g forces matching of all occurances.
X
X          p    Print the pattern space if a replacement was made.
X
X          w wfile
X               Write the pattern space to the end of wfile if a
X               replacement was made. This must be the last flag
X               argument if it is used.
X
X     (2)t label
X          Test.  If a substitution was made since the last time
X          an input line was read into the pattern space, or since
X          the last `t' command, command branches to the label. An
X          empty label means to branch to the end of the script.
X
X     (2)w wfile
X          Write the pattern space to the file wfile.
X
X     (2)x Exchange the contents of the pattern and hold spaces.
X
X     (2)y/string1/string2/
X          Yank all characters in string1 and replace them with
X          the corresponding characters in string2, which should
X          be of equal length as string1.
X
X     (2)! function
X          Apply the function to those lines not selected by the
X          command.
X
X     (0): label
X          Declare a label for `b' and `t' commands to branch to.
X
X
X
X
XGNU Project                1989 May 26                          3
X
X
X
X
X
X
XSED(1)                    User Commands                     SED(1)
X
X
X
X     (1)= Write the current line number to the output.
X
X     (2){ Perform multiple commands on the range. The `{' charac-
X          ter is followed by one or more commands to perform on
X          the pattern space. A '}' character terminates the list
X          of commands.
X
XOPTIONS
X     -n      suppress the default output
X
X     -e      indicates that the next argument is a script. Useful
X             for scripts starting with a - character.
X
X     -f      indicates that the following argument is a file con-
X             taining a script of commands to Sed.
X
XSEE ALSO
X     ed(1), grep(1), awk(1), lex(1)
X
XAVAILABILITY
X     GNU sed is free; anyone may redistribute copies of sed to
X     anyone under the terms stated in the GNU General Public
X     License, a copy of which may be found in each copy of GNU
X     Emacs.  See also the comment at the beginning of the source
X     code file sed.c.
X
X     Copies of GNU sed may sometimes be received packaged with
X     distributions of Unix systems, but it is never included in
X     the scope of any license covering those systems.  Such
X     inclusion violates the terms on which distribution is per-
X     mitted.  In fact, the primary purpose of the General Public
X     License is to prohibit anyone from attaching any other res-
X     trictions to redistribution of any of the Free Software
X     Foundation programs.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
XGNU Project                1989 May 26                          4
X
X
X
SHAR_EOF
echo "End of archive 3 (of 3)"
# if you want to concatenate archives, remove anything after this line
exit