<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv=Content-Type content="text/html; charset=utf8">
<title>/usr/web/sources/contrib/quanstro/sed.c - Plan 9 from Bell Labs</title>
<!-- THIS FILE IS AUTOMATICALLY GENERATED. -->
<!-- EDIT sources.tr INSTEAD. -->
</meta>
</head>
<body>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"><a href="/plan9/">Plan 9 from Bell Labs</a>&rsquo;s /usr/web/sources/contrib/quanstro/sed.c</span></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
Copyright © 2009 Alcatel-Lucent.<br />
Distributed under the
<a href="/plan9/license.html">Lucent Public License version 1.02</a>.
<br />
<a href="/plan9/download.html">Download the Plan 9 distribution.</a>
</font>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<table width="100%" cellspacing=0 border=0><tr><td align="center">
<table cellspacing=0 cellpadding=5 bgcolor="#eeeeff"><tr><td align="left">
<pre>
<!-- END HEADER -->
/*
* sed -- stream editor
*/
#include &lt;u.h&gt;
#include &lt;libc.h&gt;
#include &lt;bio.h&gt;
#include &lt;regexp.h&gt;

enum {
       DEPTH           = 20,           /* max nesting depth of {} */
       MAXCMDS         = 512,          /* max sed commands */
       ADDSIZE         = 10000,        /* size of add &amp; read buffer */
       MAXADDS         = 20,           /* max pending adds and reads */
       LBSIZE          = 8192,         /* input line size */
       LABSIZE         = 50,           /* max number of labels */
       MAXSUB          = 10,           /* max number of sub reg exp */
       MAXFILES        = 120,          /* max output files */
};

/*
* An address is a line #, a R.E., "$", a reference to the last
* R.E., or nothing.
*/
typedef struct {
       enum {
               A_NONE,
               A_DOL,
               A_LINE,
               A_RE,
               A_LAST,
       }type;
       union {
               long    line;           /* Line # */
               Reprog  *rp;            /* Compiled R.E. */
       };
} Addr;

typedef struct  SEDCOM {
       Addr    ad1;                    /* optional start address */
       Addr    ad2;                    /* optional end address */
       union {
               Reprog  *re1;           /* compiled R.E. */
               Rune    *text;          /* added text or file name */
               struct  SEDCOM  *lb1;   /* destination command of branch */
       };
       Rune    *rhs;                   /* Right-hand side of substitution */
       Biobuf* fcode;                  /* File ID for read and write */
       char    command;                /* command code -see below */
       char    gfl;                    /* 'Global' flag for substitutions */
       char    pfl;                    /* 'print' flag for substitutions */
       char    active;                 /* 1 =&gt; data between start and end */
       char    negfl;                  /* negation flag */
} SedCom;

/* Command Codes for field SedCom.command */
#define ACOM    01
#define BCOM    020
#define CCOM    02
#define CDCOM   025
#define CNCOM   022
#define COCOM   017
#define CPCOM   023
#define DCOM    03
#define ECOM    015
#define EQCOM   013
#define FCOM    016
#define GCOM    027
#define CGCOM   030
#define HCOM    031
#define CHCOM   032
#define ICOM    04
#define LCOM    05
#define NCOM    012
#define PCOM    010
#define QCOM    011
#define RCOM    06
#define SCOM    07
#define TCOM    021
#define WCOM    014
#define CWCOM   024
#define YCOM    026
#define XCOM    033

typedef struct label {                  /* Label symbol table */
       Rune    uninm[9];               /* Label name */
       SedCom  *chain;
       SedCom  *address;               /* Command associated with label */
} Label;

typedef struct  FILE_CACHE {            /* Data file control block */
       struct FILE_CACHE *next;        /* Forward Link */
       char    *name;                  /* Name of file */
} FileCache;

SedCom pspace[MAXCMDS];                 /* Command storage */
SedCom *pend = pspace+MAXCMDS;          /* End of command storage */
SedCom *rep = pspace;                   /* Current fill point */

Reprog  *lastre = 0;                    /* Last regular expression */
Resub   subexp[MAXSUB];                 /* sub-patterns of pattern match*/

Rune    addspace[ADDSIZE];              /* Buffer for a, c, &amp; i commands */
Rune    *addend = addspace+ADDSIZE;

SedCom  *abuf[MAXADDS];                 /* Queue of pending adds &amp; reads */
SedCom  **aptr = abuf;

struct {                                /* Sed program input control block */
       enum PTYPE {                    /* Either on command line or in file */
               P_ARG,
               P_FILE,
       } type;
       union PCTL {                    /* Pointer to data */
               Biobuf  *bp;
               char    *curr;
       };
} prog;

Rune    genbuf[LBSIZE];                 /* Miscellaneous buffer */

FileCache       *fhead = 0;             /* Head of File Cache Chain */
FileCache       *ftail = 0;             /* Tail of File Cache Chain */

Rune    *loc1;                          /* Start of pattern match */
Rune    *loc2;                          /* End of pattern match */
Rune    seof;                           /* Pattern delimiter char */

Rune    linebuf[LBSIZE+1];              /* Input data buffer */
Rune    *lbend = linebuf+LBSIZE;        /* End of buffer */
Rune    *spend = linebuf;               /* End of input data */
Rune    *cp;                            /* Current scan point in linebuf */

Rune    holdsp[LBSIZE+1];               /* Hold buffer */
Rune    *hend = holdsp+LBSIZE;          /* End of hold buffer */
Rune    *hspend = holdsp;               /* End of hold data */

int     nflag;                          /* Command line flags */
int     gflag;

int     dolflag;                        /* Set when at true EOF */
int     sflag;                          /* Set when substitution done */
int     jflag;                          /* Set when jump required */
int     delflag;                        /* Delete current line when set */

long    lnum = 0;                       /* Input line count */

char    fname[MAXFILES][40];            /* File name cache */
Biobuf  *fcode[MAXFILES];               /* File ID cache */
int     nfiles = 0;                     /* Cache fill point */

Biobuf  fout;                           /* Output stream */
Biobuf  stdin;                          /* Default input */
Biobuf* f = 0;                          /* Input data */

Label   ltab[LABSIZE];                  /* Label name symbol table */
Label   *labend = ltab+LABSIZE;         /* End of label table */
Label   *lab = ltab+1;                  /* Current Fill point */

int     depth = 0;                      /* {} stack pointer */

Rune    bad;                            /* Dummy err ptr reference */
Rune    *badp = &amp;bad;

int     tlwarn;                         /* during sub: have warned too long */

char    CGMES[]  =      "%S command garbled: %S";
char    TMMES[]  =      "Too much text: %S";
char    LTL[]    =      "Label too long: %S";
char    AD0MES[] =      "No addresses allowed: %S";
char    AD1MES[] =      "Only one address allowed: %S";

void    address(Addr *);
void    arout(void);
int     cmp(char *, char *);
int     rcmp(Rune *, Rune *);
void    command(SedCom *);
Reprog  *compile(void);
Rune    *compsub(Rune *, Rune *);
void    dechain(void);
void    dosub(Rune *);
int     ecmp(Rune *, Rune *, int);
void    enroll(char *);
void    errexit(void);
int     executable(SedCom *);
void    execute(void);
void    fcomp(void);
long    getrune(void);
Rune    *gline(Rune *);
int     match(Reprog *, Rune *);
void    newfile(enum PTYPE, char *);
int     opendata(void);
Biobuf  *open_file(char *);
Rune    *place(Rune *, Rune *, Rune *);
void    quit(char *, ...);
int     rline(Rune *, Rune *);
Label   *search(Label *);
int     substitute(SedCom *);
char    *text(char *);
Rune    *stext(Rune *, Rune *);
int     ycomp(SedCom *);
void    toolong(void);
char *  trans(int c);
void    putline(Biobuf *bp, Rune *buf, int n);
void    ebputc(Biobufhdr*, int);
void    ebputrune(Biobufhdr*, int);

#define ebprint(bp, ...) if(Bprint(bp, __VA_ARGS__) &lt; 0) quit("Bprint: %r"); else {}

void
main(int argc, char **argv)
{
       int compfl;

       lnum = 0;
       Binit(&amp;fout, 1, OWRITE);
       fcode[nfiles++] = &amp;fout;
       compfl = 0;

       if(argc == 1)
               exits(0);
       ARGBEGIN{
       case 'e':
               if (argc &lt;= 1)
                       quit("missing pattern");
               newfile(P_ARG, ARGF());
               fcomp();
               compfl = 1;
               continue;
       case 'f':
               if(argc &lt;= 1)
                       quit("no pattern-file");
               newfile(P_FILE, ARGF());
               fcomp();
               compfl = 1;
               continue;
       case 'g':
               gflag++;
               continue;
       case 'n':
               nflag++;
               continue;
       default:
               fprint(2, "sed: Unknown flag: %c\n", ARGC());
               continue;
       } ARGEND

       if(compfl == 0) {
               if (--argc &lt; 0)
                       quit("missing pattern");
               newfile(P_ARG, *argv++);
               fcomp();
       }

       if(depth)
               quit("Too many {'s");

       ltab[0].address = rep;

       dechain();

       if(argc &lt;= 0)
               enroll(0);              /* Add stdin to cache */
       else
               while(--argc &gt;= 0)
                       enroll(*argv++);
       execute();
       exits(0);
}

void
fcomp(void)
{
       int     i;
       Label   *lpt;
       Rune    *tp;
       SedCom  *pt, *pt1;
       static Rune     *p = addspace;
       static SedCom   **cmpend[DEPTH];        /* stack of {} operations */

       while (rline(linebuf, lbend) &gt;= 0) {
               cp = linebuf;
comploop:
               while(*cp == L' ' || *cp == L'\t')
                       cp++;
               if(*cp == L'\0' || *cp == L'#')
                       continue;
               if(*cp == L';') {
                       cp++;
                       goto comploop;
               }

               address(&amp;rep-&gt;ad1);
               if (rep-&gt;ad1.type != A_NONE) {
                       if (rep-&gt;ad1.type == A_LAST) {
                               if (!lastre)
                                       quit("First RE may not be null");
                               rep-&gt;ad1.type = A_RE;
                               rep-&gt;ad1.rp = lastre;
                       }
                       if(*cp == L',' || *cp == L';') {
                               cp++;
                               address(&amp;rep-&gt;ad2);
                               if (rep-&gt;ad2.type == A_LAST) {
                                       rep-&gt;ad2.type = A_RE;
                                       rep-&gt;ad2.rp = lastre;
                               }
                       } else
                               rep-&gt;ad2.type = A_NONE;
               }
               while(*cp == L' ' || *cp == L'\t')
                       cp++;

swit:
               switch(*cp++) {
               default:
                       quit("Unrecognized command: %S", linebuf);

               case '!':
                       rep-&gt;negfl = 1;
                       goto swit;

               case '{':
                       rep-&gt;command = BCOM;
                       rep-&gt;negfl = !rep-&gt;negfl;
                       cmpend[depth++] = &amp;rep-&gt;lb1;
                       if(++rep &gt;= pend)
                               quit("Too many commands: %S", linebuf);
                       if(*cp == '\0')
                               continue;
                       goto comploop;

               case '}':
                       if(rep-&gt;ad1.type != A_NONE)
                               quit(AD0MES, linebuf);
                       if(--depth &lt; 0)
                               quit("Too many }'s");
                       *cmpend[depth] = rep;
                       if(*cp == 0)
                               continue;
                       goto comploop;

               case '=':
                       rep-&gt;command = EQCOM;
                       if(rep-&gt;ad2.type != A_NONE)
                               quit(AD1MES, linebuf);
                       break;

               case ':':
                       if(rep-&gt;ad1.type != A_NONE)
                               quit(AD0MES, linebuf);

                       while(*cp == L' ')
                               cp++;
                       tp = lab-&gt;uninm;
                       while (*cp &amp;&amp; *cp != L';' &amp;&amp; *cp != L' ' &amp;&amp;
                           *cp != L'\t' &amp;&amp; *cp != L'#') {
                               *tp++ = *cp++;
                               if(tp &gt;= &amp;lab-&gt;uninm[8])
                                       quit(LTL, linebuf);
                       }
                       *tp = L'\0';

                       if (*lab-&gt;uninm == L'\0')            /* no label? */
                               quit(CGMES, L":", linebuf);
                       if(lpt = search(lab)) {
                               if(lpt-&gt;address)
                                       quit("Duplicate labels: %S", linebuf);
                       } else {
                               lab-&gt;chain = 0;
                               lpt = lab;
                               if(++lab &gt;= labend)
                                       quit("Too many labels: %S", linebuf);
                       }
                       lpt-&gt;address = rep;
                       if (*cp == L'#')
                               continue;
                       rep--;                  /* reuse this slot */
                       break;

               case 'a':
                       rep-&gt;command = ACOM;
                       if(rep-&gt;ad2.type != A_NONE)
                               quit(AD1MES, linebuf);
                       if(*cp == L'\\')
                               cp++;
                       if(*cp++ != L'\n')
                               quit(CGMES, L"a", linebuf);
                       rep-&gt;text = p;
                       p = stext(p, addend);
                       break;
               case 'c':
                       rep-&gt;command = CCOM;
                       if(*cp == L'\\')
                               cp++;
                       if(*cp++ != L'\n')
                               quit(CGMES, L"c", linebuf);
                       rep-&gt;text = p;
                       p = stext(p, addend);
                       break;
               case 'i':
                       rep-&gt;command = ICOM;
                       if(rep-&gt;ad2.type != A_NONE)
                               quit(AD1MES, linebuf);
                       if(*cp == L'\\')
                               cp++;
                       if(*cp++ != L'\n')
                               quit(CGMES, L"i", linebuf);
                       rep-&gt;text = p;
                       p = stext(p, addend);
                       break;

               case 'g':
                       rep-&gt;command = GCOM;
                       break;

               case 'G':
                       rep-&gt;command = CGCOM;
                       break;

               case 'h':
                       rep-&gt;command = HCOM;
                       break;

               case 'H':
                       rep-&gt;command = CHCOM;
                       break;

               case 't':
                       rep-&gt;command = TCOM;
                       goto jtcommon;

               case 'b':
                       rep-&gt;command = BCOM;
jtcommon:
                       while(*cp == L' ')
                               cp++;
                       if(*cp == L'\0' || *cp == L';') {
                               /* no label; jump to end */
                               if(pt = ltab[0].chain) {
                                       while((pt1 = pt-&gt;lb1) != nil)
                                               pt = pt1;
                                       pt-&gt;lb1 = rep;
                               } else
                                       ltab[0].chain = rep;
                               break;
                       }

                       /* copy label into lab-&gt;uninm */
                       tp = lab-&gt;uninm;
                       while((*tp = *cp++) != L'\0' &amp;&amp; *tp != L';')
                               if(++tp &gt;= &amp;lab-&gt;uninm[8])
                                       quit(LTL, linebuf);
                       cp--;
                       *tp = L'\0';

                       if (*lab-&gt;uninm == L'\0')
                               /* shouldn't get here */
                               quit(CGMES, L"b or t", linebuf);
                       if((lpt = search(lab)) != nil) {
                               if(lpt-&gt;address)
                                       rep-&gt;lb1 = lpt-&gt;address;
                               else {
                                       for(pt = lpt-&gt;chain; pt != nil &amp;&amp;
                                           (pt1 = pt-&gt;lb1) != nil; pt = pt1)
                                               ;
                                       if (pt)
                                               pt-&gt;lb1 = rep;
                               }
                       } else {                        /* add new label */
                               lab-&gt;chain = rep;
                               lab-&gt;address = 0;
                               if(++lab &gt;= labend)
                                       quit("Too many labels: %S", linebuf);
                       }
                       break;

               case 'n':
                       rep-&gt;command = NCOM;
                       break;

               case 'N':
                       rep-&gt;command = CNCOM;
                       break;

               case 'p':
                       rep-&gt;command = PCOM;
                       break;

               case 'P':
                       rep-&gt;command = CPCOM;
                       break;

               case 'r':
                       rep-&gt;command = RCOM;
                       if(rep-&gt;ad2.type != A_NONE)
                               quit(AD1MES, linebuf);
                       if(*cp++ != L' ')
                               quit(CGMES, L"r", linebuf);
                       rep-&gt;text = p;
                       p = stext(p, addend);
                       break;

               case 'd':
                       rep-&gt;command = DCOM;
                       break;

               case 'D':
                       rep-&gt;command = CDCOM;
                       rep-&gt;lb1 = pspace;
                       break;

               case 'q':
                       rep-&gt;command = QCOM;
                       if(rep-&gt;ad2.type != A_NONE)
                               quit(AD1MES, linebuf);
                       break;

               case 'l':
                       rep-&gt;command = LCOM;
                       break;

               case 's':
                       rep-&gt;command = SCOM;
                       seof = *cp++;
                       if ((rep-&gt;re1 = compile()) == 0) {
                               if(!lastre)
                                       quit("First RE may not be null.");
                               rep-&gt;re1 = lastre;
                       }
                       rep-&gt;rhs = p;
                       if((p = compsub(p, addend)) == 0)
                               quit(CGMES, L"s", linebuf);
                       if(*cp == L'g') {
                               cp++;
                               rep-&gt;gfl++;
                       } else if(gflag)
                               rep-&gt;gfl++;

                       if(*cp == L'p') {
                               cp++;
                               rep-&gt;pfl = 1;
                       }

                       if(*cp == L'P') {
                               cp++;
                               rep-&gt;pfl = 2;
                       }

                       if(*cp == L'w') {
                               cp++;
                               if(*cp++ !=  L' ')
                                       quit(CGMES, L"s", linebuf);
                               text(fname[nfiles]);
                               for(i = nfiles - 1; i &gt;= 0; i--)
                                       if(cmp(fname[nfiles], fname[i]) == 0) {
                                               rep-&gt;fcode = fcode[i];
                                               goto done;
                                       }
                               if(nfiles &gt;= MAXFILES)
                                       quit("Too many files in w commands 1");
                               rep-&gt;fcode = open_file(fname[nfiles]);
                       }
                       break;

               case 'w':
                       rep-&gt;command = WCOM;
                       if(*cp++ != L' ')
                               quit(CGMES, L"w", linebuf);
                       text(fname[nfiles]);
                       for(i = nfiles - 1; i &gt;= 0; i--)
                               if(cmp(fname[nfiles], fname[i]) == 0) {
                                       rep-&gt;fcode = fcode[i];
                                       goto done;
                               }
                       if(nfiles &gt;= MAXFILES){
                               fprint(2, "sed: Too many files in w commands 2 \n");
                               fprint(2, "nfiles = %d; MAXF = %d\n",
                                       nfiles, MAXFILES);
                               errexit();
                       }
                       rep-&gt;fcode = open_file(fname[nfiles]);
                       break;

               case 'x':
                       rep-&gt;command = XCOM;
                       break;

               case 'y':
                       rep-&gt;command = YCOM;
                       seof = *cp++;
                       if (ycomp(rep) == 0)
                               quit(CGMES, L"y", linebuf);
                       break;

               }
done:
               if(++rep &gt;= pend)
                       quit("Too many commands, last: %S", linebuf);
               if(*cp++ != L'\0') {
                       if(cp[-1] == L';')
                               goto comploop;
                       quit(CGMES, cp - 1, linebuf);
               }
       }
}

Biobuf *
open_file(char *name)
{
       int fd;
       Biobuf *bp;

       if ((bp = malloc(sizeof(Biobuf))) == 0)
               quit("Out of memory");
       if ((fd = open(name, OWRITE)) &lt; 0 &amp;&amp;
           (fd = create(name, OWRITE, 0666)) &lt; 0)
               quit("Cannot create %s", name);
       Binit(bp, fd, OWRITE);
       Bseek(bp, 0, 2);
       fcode[nfiles++] = bp;
       return bp;
}

Rune *
compsub(Rune *rhs, Rune *end)
{
       Rune r;

       while ((r = *cp++) != '\0') {
               if(r == '\\') {
                       if (rhs &lt; end)
                               *rhs++ = Runemax;
                       else
                               return 0;
                       r = *cp++;
                       if(r == 'n')
                               r = '\n';
               } else {
                       if(r == seof) {
                               if (rhs &lt; end)
                                       *rhs++ = '\0';
                               else
                                       return 0;
                               return rhs;
                       }
               }
               if (rhs &lt; end)
                       *rhs++ = r;
               else
                       return 0;
       }
       return 0;
}

Reprog *
compile(void)
{
       Rune c;
       char *ep;
       char expbuf[512];

       if((c = *cp++) == seof)         /* L'//' */
               return 0;
       ep = expbuf;
       do {
               if (c == L'\0' || c == L'\n')
                       quit(TMMES, linebuf);
               if (c == L'\\') {
                       if (ep &gt;= expbuf+sizeof(expbuf))
                               quit(TMMES, linebuf);
                       ep += runetochar(ep, &amp;c);
                       if ((c = *cp++) == L'n')
                               c = L'\n';
               }
               if (ep &gt;= expbuf + sizeof(expbuf))
                       quit(TMMES, linebuf);
               ep += runetochar(ep, &amp;c);
       } while ((c = *cp++) != seof);
       *ep = 0;
       return lastre = regcomp(expbuf);
}

void
regerror(char *s)
{
       quit("r.e.-using: %s: %S", s, linebuf);
}

void
newfile(enum PTYPE type, char *name)
{
       if (type == P_ARG)
               prog.curr = name;
       else if ((prog.bp = Bopen(name, OREAD)) == 0)
               quit("Cannot open pattern-file: %s\n", name);
       prog.type = type;
}

int
rline(Rune *buf, Rune *end)
{
       long c, w;
       Rune r;

       w = 0;
       while ((c = getrune()) &gt;= 0) {
               r = c;
               if (r == '\\') {
                       if (buf &lt;= end)
                               *buf++ = r;
                       if ((c = getrune()) &lt; 0)
                               break;
                       r = c;
               } else if (r == '\n') {
                       *buf = '\0';
                       return 1;
               }
               if (buf &lt;= end)
                       *buf++ = r;
               else if(w == 0){
                       fprint(2, "sed: Input line too long.\n");
                       w = 1;
               }
       }
       *buf = '\0';
       return -1;
}

long
getrune(void)
{
       long c;
       Rune r;
       char *p;

       if (prog.type == P_ARG) {
               if ((p = prog.curr) != 0) {
                       if (*p) {
                               prog.curr += chartorune(&amp;r, p);
                               c = r;
                       } else {
                               c = '\n';       /* fake an end-of-line */
                               prog.curr = 0;
                       }
               } else
                       c = -1;
       } else if ((c = Bgetrune(prog.bp)) &lt; 0)
               if(Bterm(prog.bp) &lt; 0)
                       quit("write: %r");
       return c;
}

void
address(Addr *ap)
{
       int c;
       long lno;

       if((c = *cp++) == '$')
               ap-&gt;type = A_DOL;
       else if(c == '/') {
               seof = c;
               if (ap-&gt;rp = compile())
                       ap-&gt;type = A_RE;
               else
                       ap-&gt;type = A_LAST;
       }
       else if (c &gt;= '0' &amp;&amp; c &lt;= '9') {
               lno = c - '0';
               while ((c = *cp) &gt;= '0' &amp;&amp; c &lt;= '9')
                       lno = lno*10 + *cp++ - '0';
               if(!lno)
                       quit("line number 0 is illegal",0);
               ap-&gt;type = A_LINE;
               ap-&gt;line = lno;
       }
       else {
               cp--;
               ap-&gt;type = A_NONE;
       }
}

cmp(char *a, char *b)           /* compare characters */
{
       while(*a == *b++)
               if (*a == '\0')
                       return 0;
               else
                       a++;
       return 1;
}
rcmp(Rune *a, Rune *b)          /* compare runes */
{
       while(*a == *b++)
               if (*a == '\0')
                       return 0;
               else
                       a++;
       return 1;
}

char *
text(char *p)           /* extract character string */
{
       Rune r;

       while(*cp == ' ' || *cp == '\t')
               cp++;
       while (*cp) {
               if ((r = *cp++) == '\\' &amp;&amp; (r = *cp++) == '\0')
                       break;
               if (r == '\n')
                       while (*cp == ' ' || *cp == '\t')
                               cp++;
               p += runetochar(p, &amp;r);
       }
       *p++ = '\0';
       return p;
}

Rune *
stext(Rune *p, Rune *end)               /* extract rune string */
{
       while(*cp == L' ' || *cp == L'\t')
               cp++;
       while (*cp) {
               if (*cp == L'\\' &amp;&amp; *++cp == L'\0')
                       break;
               if (p &gt;= end-1)
                       quit(TMMES, linebuf);
               if ((*p++ = *cp++) == L'\n')
                       while(*cp == L' ' || *cp == L'\t')
                               cp++;
       }
       *p++ = 0;
       return p;
}


Label *
search(Label *ptr)
{
       Label   *rp;

       for (rp = ltab; rp &lt; ptr; rp++)
               if(rcmp(rp-&gt;uninm, ptr-&gt;uninm) == 0)
                       return(rp);
       return(0);
}

void
dechain(void)
{
       Label   *lptr;
       SedCom  *rptr, *trptr;

       for(lptr = ltab; lptr &lt; lab; lptr++) {
               if(lptr-&gt;address == 0)
                       quit("Undefined label: %S", lptr-&gt;uninm);
               if(lptr-&gt;chain) {
                       rptr = lptr-&gt;chain;
                       while((trptr = rptr-&gt;lb1) != nil) {
                               rptr-&gt;lb1 = lptr-&gt;address;
                               rptr = trptr;
                       }
                       rptr-&gt;lb1 = lptr-&gt;address;
               }
       }
}

int
ycomp(SedCom *r)
{
       int i;
       Rune *rp, *sp, *tsp;
       Rune c, highc;

       highc = 0;
       for(tsp = cp; *tsp != seof; tsp++) {
               if(*tsp == L'\\')
                       tsp++;
               if(*tsp == L'\n' || *tsp == L'\0')
                       return 0;
               if (*tsp &gt; highc)
                       highc = *tsp;
       }
       tsp++;
       if ((rp = r-&gt;text = (Rune *)malloc(sizeof(Rune) * (highc+2))) == nil)
               quit("Out of memory");
       *rp++ = highc;                          /* save upper bound */
       for (i = 0; i &lt;= highc; i++)
               rp[i] = i;
       sp = cp;
       while((c = *sp++) != seof) {
               if(c == L'\\' &amp;&amp; *sp == L'n') {
                       sp++;
                       c = L'\n';
               }
               if((rp[c] = *tsp++) == L'\\' &amp;&amp; *tsp == L'n') {
                       rp[c] = L'\n';
                       tsp++;
               }
               if(rp[c] == seof || rp[c] == L'\0') {
                       free(r-&gt;re1);
                       r-&gt;re1 = nil;
                       return 0;
               }
       }
       if(*tsp != seof) {
               free(r-&gt;re1);
               r-&gt;re1 = nil;
               return 0;
       }
       cp = tsp+1;
       return 1;
}

void
execute(void)
{
       SedCom  *ipc;

       while (spend = gline(linebuf)){
               for(ipc = pspace; ipc-&gt;command; ) {
                       if (!executable(ipc)) {
                               ipc++;
                               continue;
                       }
                       command(ipc);

                       if(delflag)
                               break;
                       if(jflag) {
                               jflag = 0;
                               if((ipc = ipc-&gt;lb1) == 0)
                                       break;
                       } else
                               ipc++;
               }
               if(!nflag &amp;&amp; !delflag)
                       putline(&amp;fout, linebuf, spend - linebuf);
               if(aptr &gt; abuf)
                       arout();
               delflag = 0;
       }
}

/* determine if a statement should be applied to an input line */
int
executable(SedCom *ipc)
{
       if (ipc-&gt;active) {   /* Addr1 satisfied - accept until Addr2 */
               if (ipc-&gt;active == 1)                /* Second line */
                       ipc-&gt;active = 2;
               switch(ipc-&gt;ad2.type) {
               case A_NONE:            /* No second addr; use first */
                       ipc-&gt;active = 0;
                       break;
               case A_DOL:             /* Accept everything */
                       return !ipc-&gt;negfl;
               case A_LINE:            /* Line at end of range? */
                       if (lnum &lt;= ipc-&gt;ad2.line) {
                               if (ipc-&gt;ad2.line == lnum)
                                       ipc-&gt;active = 0;
                               return !ipc-&gt;negfl;
                       }
                       ipc-&gt;active = 0;     /* out of range */
                       return ipc-&gt;negfl;
               case A_RE:              /* Check for matching R.E. */
                       if (match(ipc-&gt;ad2.rp, linebuf))
                               ipc-&gt;active = 0;
                       return !ipc-&gt;negfl;
               default:
                       quit("Internal error");
               }
       }
       switch (ipc-&gt;ad1.type) {     /* Check first address */
       case A_NONE:                    /* Everything matches */
               return !ipc-&gt;negfl;
       case A_DOL:                     /* Only last line */
               if (dolflag)
                       return !ipc-&gt;negfl;
               break;
       case A_LINE:                    /* Check line number */
               if (ipc-&gt;ad1.line == lnum) {
                       ipc-&gt;active = 1;     /* In range */
                       return !ipc-&gt;negfl;
               }
               break;
       case A_RE:                      /* Check R.E. */
               if (match(ipc-&gt;ad1.rp, linebuf)) {
                       ipc-&gt;active = 1;     /* In range */
                       return !ipc-&gt;negfl;
               }
               break;
       default:
               quit("Internal error");
       }
       return ipc-&gt;negfl;
}

int
match(Reprog *pattern, Rune *buf)
{
       if (!pattern)
               return 0;
       subexp[0].rsp = buf;
       subexp[0].ep = 0;
       if (rregexec(pattern, linebuf, subexp, MAXSUB) &gt; 0) {
               loc1 = subexp[0].rsp;
               loc2 = subexp[0].rep;
               return 1;
       }
       loc1 = loc2 = 0;
       return 0;
}

int
substitute(SedCom *ipc)
{
       int len;

       if(!match(ipc-&gt;re1, linebuf))
               return 0;

       /*
        * we have at least one match.  some patterns, e.g. '$' or '^', can
        * produce 0-length matches, so during a global substitute we must
        * bump to the character after a 0-length match to keep from looping.
        */
       sflag = 1;
       tlwarn = 0;
       if(ipc-&gt;gfl == 0)                    /* single substitution */
               dosub(ipc-&gt;rhs);
       else
               do{                             /* global substitution */
                       len = loc2 - loc1;      /* length of match */
                       dosub(ipc-&gt;rhs);     /* dosub moves loc2 */
                       if(*loc2 == 0)          /* end of string */
                               break;
                       if(len == 0)            /* zero-length R.E. match */
                               loc2++;         /* bump over 0-length match */
                       if(*loc2 == 0)          /* end of string */
                               break;
               } while(match(ipc-&gt;re1, loc2));
       return 1;
}

void
dosub(Rune *rhsbuf)
{
       int c, n;
       Rune *lp, *sp, *rp;

       lp = linebuf;
       sp = genbuf;
       rp = rhsbuf;
       while (lp &lt; loc1)
               *sp++ = *lp++;
       while(c = *rp++) {
               if (c == '&amp;') {
                       sp = place(sp, loc1, loc2);
                       continue;
               }
               if (c == Runemax &amp;&amp; (c = *rp++) &gt;= '1' &amp;&amp; c &lt; MAXSUB + '0') {
                       n = c-'0';
                       if (subexp[n].rsp &amp;&amp; subexp[n].rep) {
                               sp = place(sp, subexp[n].rsp, subexp[n].rep);
                               continue;
                       }
                       else {
                               fprint(2, "sed: Invalid back reference \\%d\n",n);
                               errexit();
                       }
               }
               if(sp &lt; &amp;genbuf[LBSIZE]){
                       *sp++ = c;
                       if (sp &gt;= &amp;genbuf[LBSIZE])
                               toolong();
               }
       }
       lp = loc2;
       loc2 = sp - genbuf + linebuf;
       while (*sp++ = *lp++)
               if (sp &gt;= &amp;genbuf[LBSIZE]){
                       toolong();
                       break;
               }
       lp = linebuf;
       sp = genbuf;
       while (*lp++ = *sp++)
               if (sp &gt;= &amp;genbuf[LBSIZE])
                       break;
       spend = lp - 1;
}

Rune *
place(Rune *sp, Rune *l1, Rune *l2)
{
       while (l1 &lt; l2) {
               *sp++ = *l1++;
               if (sp &gt;= &amp;genbuf[LBSIZE]){
                       toolong();
                       break;
               }
       }
       return sp;
}

void
toolong(void)
{
       if(tlwarn == 0)
               fprint(2, "sed: Output line too long.\n");
       tlwarn = 1;
}

char *
trans(int c)
{
       static char buf[] = "\\x0000";
       static char hex[] = "0123456789abcdef";

       switch(c) {
       case '\b':
               return "\\b";
       case '\n':
               return "\\n";
       case '\r':
               return "\\r";
       case '\t':
               return "\\t";
       case '\\':
               return "\\\\";
       }
       buf[2] = hex[(c&gt;&gt;12)&amp;0xF];
       buf[3] = hex[(c&gt;&gt;8)&amp;0xF];
       buf[4] = hex[(c&gt;&gt;4)&amp;0xF];
       buf[5] = hex[c&amp;0xF];
       return buf;
}

void
command(SedCom *ipc)
{
       int i, c;
       char *ucp;
       Rune *execp, *p1, *p2, *rp;

       switch(ipc-&gt;command) {
       case ACOM:
               *aptr++ = ipc;
               if(aptr &gt;= abuf+MAXADDS)
                       quit("sed: Too many appends after line %ld\n",
                               (char *)lnum);
               *aptr = 0;
               break;
       case CCOM:
               delflag = 1;
               if(ipc-&gt;active == 1) {
                       for(rp = ipc-&gt;text; *rp; rp++)
                               ebputrune(&amp;fout, *rp);
                       ebputc(&amp;fout, '\n');
               }
               break;
       case DCOM:
               delflag++;
               break;
       case CDCOM:
               p1 = p2 = linebuf;
               while(*p1 != '\n') {
                       if(*p1++ == 0) {
                               delflag++;
                               return;
                       }
               }
               p1++;
               while(*p2++ = *p1++)
                       ;
               spend = p2 - 1;
               jflag++;
               break;
       case EQCOM:
               ebprint(&amp;fout, "%ld\n", lnum) ;
               break;
       case GCOM:
               p1 = linebuf;
               p2 = holdsp;
               while(*p1++ = *p2++)
                       ;
               spend = p1 - 1;
               break;
       case CGCOM:
               *spend++ = '\n';
               p1 = spend;
               p2 = holdsp;
               while(*p1++ = *p2++)
                       if(p1 &gt;= lbend)
                               break;
               spend = p1 - 1;
               break;
       case HCOM:
               p1 = holdsp;
               p2 = linebuf;
               while(*p1++ = *p2++);
               hspend = p1 - 1;
               break;
       case CHCOM:
               *hspend++ = '\n';
               p1 = hspend;
               p2 = linebuf;
               while(*p1++ = *p2++)
                       if(p1 &gt;= hend)
                               break;
               hspend = p1 - 1;
               break;
       case ICOM:
               for(rp = ipc-&gt;text; *rp; rp++)
                       ebputrune(&amp;fout, *rp);
               ebputc(&amp;fout, '\n');
               break;
       case BCOM:
               jflag = 1;
               break;
       case LCOM:
               c = 0;
               for (i = 0, rp = linebuf; *rp; rp++) {
                       c = *rp;
                       if(c &gt;= 0x20 &amp;&amp; c &lt; 0x7F &amp;&amp; c != '\\') {
                               ebputc(&amp;fout, c);
                               if(i++ &gt; 71) {
                                       ebprint(&amp;fout, "\\\n");
                                       i = 0;
                               }
                       } else {
                               for (ucp = trans(*rp); *ucp; ucp++){
                                       c = *ucp;
                                       ebputc(&amp;fout, c);
                                       if(i++ &gt; 71) {
                                               ebprint(&amp;fout, "\\\n");
                                               i = 0;
                                       }
                               }
                       }
               }
               if(c == ' ')
                       ebprint(&amp;fout, "\\n");
               ebputc(&amp;fout, '\n');
               break;
       case NCOM:
               if(!nflag)
                       putline(&amp;fout, linebuf, spend-linebuf);

               if(aptr &gt; abuf)
                       arout();
               if((execp = gline(linebuf)) == 0) {
                       delflag = 1;
                       break;
               }
               spend = execp;
               break;
       case CNCOM:
               if(aptr &gt; abuf)
                       arout();
               *spend++ = '\n';
               if((execp = gline(spend)) == 0) {
                       delflag = 1;
                       break;
               }
               spend = execp;
               break;
       case PCOM:
               putline(&amp;fout, linebuf, spend-linebuf);
               break;
       case CPCOM:
cpcom:
               for(rp = linebuf; *rp &amp;&amp; *rp != '\n'; rp++)
                       ebputc(&amp;fout, *rp);
               ebputc(&amp;fout, '\n');
               break;
       case QCOM:
               if(!nflag)
                       putline(&amp;fout, linebuf, spend-linebuf);
               if(aptr &gt; abuf)
                       arout();
               exits(0);
       case RCOM:
               *aptr++ = ipc;
               if(aptr &gt;= &amp;abuf[MAXADDS])
                       quit("sed: Too many reads after line %ld\n",
                               (char *)lnum);
               *aptr = 0;
               break;
       case SCOM:
               i = substitute(ipc);
               if(i &amp;&amp; ipc-&gt;pfl)
                       if(ipc-&gt;pfl == 1)
                               putline(&amp;fout, linebuf, spend-linebuf);
                       else
                               goto cpcom;
               if(i &amp;&amp; ipc-&gt;fcode)
                       goto wcom;
               break;

       case TCOM:
               if(sflag) {
                       sflag = 0;
                       jflag = 1;
               }
               break;

       case WCOM:
wcom:
               putline(ipc-&gt;fcode,linebuf, spend - linebuf);
               break;
       case XCOM:
               p1 = linebuf;
               p2 = genbuf;
               while(*p2++ = *p1++)
                       ;
               p1 = holdsp;
               p2 = linebuf;
               while(*p2++ = *p1++)
                       ;
               spend = p2 - 1;
               p1 = genbuf;
               p2 = holdsp;
               while(*p2++ = *p1++)
                       ;
               hspend = p2 - 1;
               break;
       case YCOM:
               p1 = linebuf;
               p2 = ipc-&gt;text;
               for (i = *p2++; *p1; p1++)
                       if (*p1 &lt;= i)
                               *p1 = p2[*p1];
               break;
       }
}

void
putline(Biobuf *bp, Rune *buf, int n)
{
       while (n--)
               ebputrune(bp, *buf++);
       ebputc(bp, '\n');
}
ecmp(Rune *a, Rune *b, int count)
{
       while(count--)
               if(*a++ != *b++)
                       return 0;
       return 1;
}

void
arout(void)
{
       int     c;
       char    *s;
       char    buf[128];
       Rune    *p1;
       Biobuf  *fi;

       for (aptr = abuf; *aptr; aptr++) {
               if((*aptr)-&gt;command == ACOM) {
                       for(p1 = (*aptr)-&gt;text; *p1; p1++ )
                               ebputrune(&amp;fout, *p1);
                       ebputc(&amp;fout, '\n');
               } else {
                       for(s = buf, p1 = (*aptr)-&gt;text; *p1; p1++)
                               s += runetochar(s, p1);
                       *s = '\0';
                       if((fi = Bopen(buf, OREAD)) == nil){
                               /*
                                * this is a botch.  we should quit.
                                * but i'm worried about breaking
                                * old scripts.  — quanstro
                                */
                               fprint(2, "Bopen: %r\n");
                               continue;
                       }
                       while((c = Bgetc(fi)) &gt;= 0)
                               ebputc(&amp;fout, c);
                       if(Bterm(fi) &lt; 0)
                               quit("Bterm: %r");
               }
       }
       aptr = abuf;
       *aptr = 0;
}

void
errexit(void)
{
       exits("error");
}

void
quit(char *fmt, ...)
{
       char *p, *ep;
       char msg[256];
       va_list arg;

       ep = msg + sizeof msg;
       p = seprint(msg, ep, "sed: ");
       va_start(arg, fmt);
       p = vseprint(p, ep, fmt, arg);
       va_end(arg);
       p = seprint(p, ep, "\n");
       write(2, msg, p - msg);
       errexit();
}

void
ebputc(Biobufhdr *bp, int c)
{
       if(Bputc(bp, c) &lt; 0)
               quit("Bputc: %r");
}

void
ebputrune(Biobufhdr *bp, int r)
{
       if(Bputrune(bp, r) &lt; 0)
               quit("Bputrune: %r");
}

Rune *
gline(Rune *addr)
{
       long c, w;
       Rune *p;
       static long peekc = 0;

       if (f == 0 &amp;&amp; opendata() &lt; 0)
               return 0;
       sflag = 0;
       lnum++;
/*      Bflush(&amp;fout);********* dumped 4/30/92 - bobf****/
       w = 0;
       do {
               p = addr;
               for (c = (peekc? peekc: Bgetrune(f)); c &gt;= 0; c = Bgetrune(f)) {
                       if (c == '\n') {
                               if ((peekc = Bgetrune(f)) &lt; 0 &amp;&amp; fhead == 0)
                                       dolflag = 1;
                               *p = '\0';
                               return p;
                       }
                       if (c) {
                               if (p &lt; lbend)
                                       *p++ = c;
                               else if(w == 0) {
                                       w = 1;
                                       fprint(2, "sed: Input line too long.\n");
                               }
                       }
               }
               /* return partial final line, adding implicit newline */
               if(p != addr) {
                       *p = '\0';
                       peekc = -1;
                       if (fhead == 0)
                               dolflag = 1;
                       return p;
               }
               peekc = 0;
               if(Bterm(f) &lt; 0)
                       quit("write: %r");
       } while (opendata() &gt; 0);            /* Switch to next stream */
       f = 0;
       return 0;
}

/*
* Data file input section - the intent is to transparently
*      catenate all data input streams.
*/
void
enroll(char *filename)          /* Add a file to the input file cache */
{
       FileCache *fp;

       if ((fp = (FileCache *)malloc(sizeof (FileCache))) == nil)
               quit("Out of memory");
       if (ftail == nil)
               fhead = fp;
       else
               ftail-&gt;next = fp;
       ftail = fp;
       fp-&gt;next = nil;
       fp-&gt;name = filename;         /* 0 =&gt; stdin */
}

int
opendata(void)
{
       if (fhead == nil)
               return -1;
       if (fhead-&gt;name) {
               if ((f = Bopen(fhead-&gt;name, OREAD)) == nil)
                       quit("Can't open %s", fhead-&gt;name);
       } else {
               Binit(&amp;stdin, 0, OREAD);
               f = &amp;stdin;
       }
       fhead = fhead-&gt;next;
       return 1;
}
<!-- BEGIN TAIL -->
</pre>
</td></tr></table>
</td></tr></table>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<p style="line-height: 1.2em; margin-left: 1.00in; text-indent: 0.00in; margin-right: 1.00in; margin-top: 0; margin-bottom: 0; text-align: center;">
<span style="font-size: 10pt"></span></p>
<p style="margin-top: 0; margin-bottom: 0.50in"></p>
<p style="margin-top: 0; margin-bottom: 0.33in"></p>
<center><table border="0"><tr>
<td valign="middle"><a href="http://www.alcatel-lucent.com/"><img border="0" src="/plan9/img/logo_ft.gif" alt="Bell Labs" />
</a></td>
<td valign="middle"><a href="http://www.opensource.org"><img border="0" alt="OSI certified" src="/plan9/img/osi-certified-60x50.gif" />
</a></td>
<td><img style="padding-right: 45px;" alt="Powered by Plan 9" src="/plan9/img/power36.gif" />
</td>
</tr></table></center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center>
<span style="font-size: 10pt">(<a href="/plan9/">Return to Plan 9 Home Page</a>)</span>
</center>
<p style="margin-top: 0; margin-bottom: 0.17in"></p>
<center><font size=-1>
<span style="font-size: 10pt"><a href="http://www.lucent.com/copyright.html">Copyright</a></span>
<span style="font-size: 10pt">© 2009 Alcatel-Lucent.</span>
<span style="font-size: 10pt">All Rights Reserved.</span>
<br />
<span style="font-size: 10pt">Comments to</span>
<span style="font-size: 10pt"><a href="mailto:[email protected]">[email protected]</a>.</span>
</font></center>
</body>
</html>