blob: a78c9c66496d54118c6f150daf516610e6982faf [file] [log] [blame]
/*
* sed -- stream editor
*
*
*/
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <regexp.h>
enum {
DEPTH = 20, /* max nesting depth of {} */
MAXCMDS = 512, /* max sed commands */
ADDSIZE = 10000, /* size of add & read buffer */
MAXADDS = 20, /* max pending adds and reads */
LBSIZE = 8192, /* input line size */
LABSIZE = 50, /* max label name size */
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. */
} u;
} 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 */
} u;
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 => 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 asc[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, & i commands */
Rune *addend = addspace+ADDSIZE;
SedCom *abuf[MAXADDS]; /* Queue of pending adds & 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;
} pctl;
} 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 lflag;
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 bstdin; /* 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 = &bad;
char CGMES[] = "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 *, char *);
int rline(Rune *, Rune *);
Label *search(Label *);
int substitute(SedCom *);
char *text(char *);
Rune *stext(Rune *, Rune *);
int ycomp(SedCom *);
char * trans(int c);
void putline(Biobuf *bp, Rune *buf, int n);
void
main(int argc, char **argv)
{
int compfl;
lnum = 0;
Binit(&fout, 1, OWRITE);
fcode[nfiles++] = &fout;
compfl = 0;
if(argc == 1)
exits(0);
ARGBEGIN{
case 'n':
nflag++;
continue;
case 'f':
if(argc <= 1)
quit("no pattern-file", 0);
newfile(P_FILE, ARGF());
fcomp();
compfl = 1;
continue;
case 'e':
if (argc <= 1)
quit("missing pattern", 0);
newfile(P_ARG, ARGF());
fcomp();
compfl = 1;
continue;
case 'g':
gflag++;
continue;
case 'l':
lflag++;
continue;
default:
fprint(2, "sed: Unknown flag: %c\n", ARGC());
continue;
} ARGEND
if(compfl == 0) {
if (--argc < 0)
quit("missing pattern", 0);
newfile(P_ARG, *argv++);
fcomp();
}
if(depth)
quit("Too many {'s", 0);
ltab[0].address = rep;
dechain();
if(argc <= 0)
enroll(0); /* Add stdin to cache */
else while(--argc >= 0) {
enroll(*argv++);
}
execute();
exits(0);
}
void
fcomp(void)
{
Rune *tp;
SedCom *pt, *pt1;
int i;
Label *lpt;
static Rune *p = addspace;
static SedCom **cmpend[DEPTH]; /* stack of {} operations */
while (rline(linebuf, lbend) >= 0) {
cp = linebuf;
comploop:
while(*cp == ' ' || *cp == '\t')
cp++;
if(*cp == '\0' || *cp == '#')
continue;
if(*cp == ';') {
cp++;
goto comploop;
}
address(&rep->ad1);
if (rep->ad1.type != A_NONE) {
if (rep->ad1.type == A_LAST) {
if (!lastre)
quit("First RE may not be null", 0);
rep->ad1.type = A_RE;
rep->ad1.u.rp = lastre;
}
if(*cp == ',' || *cp == ';') {
cp++;
address(&rep->ad2);
if (rep->ad2.type == A_LAST) {
rep->ad1.type = A_RE;
rep->ad2.u.rp = lastre;
}
} else
rep->ad2.type = A_NONE;
}
while(*cp == ' ' || *cp == '\t')
cp++;
swit:
switch(*cp++) {
default:
quit("Unrecognized command: %S", (char *)linebuf);
case '!':
rep->negfl = 1;
goto swit;
case '{':
rep->command = BCOM;
rep->negfl = !(rep->negfl);
cmpend[depth++] = &rep->u.lb1;
if(++rep >= pend)
quit("Too many commands: %S", (char *) linebuf);
if(*cp == '\0') continue;
goto comploop;
case '}':
if(rep->ad1.type != A_NONE)
quit(AD0MES, (char *) linebuf);
if(--depth < 0)
quit("Too many }'s", 0);
*cmpend[depth] = rep;
if(*cp == 0) continue;
goto comploop;
case '=':
rep->command = EQCOM;
if(rep->ad2.type != A_NONE)
quit(AD1MES, (char *) linebuf);
break;
case ':':
if(rep->ad1.type != A_NONE)
quit(AD0MES, (char *) linebuf);
while(*cp == ' ')
cp++;
tp = lab->asc;
while (*cp && *cp != ';' && *cp != ' ' && *cp != '\t' && *cp != '#') {
*tp++ = *cp++;
if(tp >= &(lab->asc[8]))
quit(LTL, (char *) linebuf);
}
*tp = '\0';
if(lpt = search(lab)) {
if(lpt->address)
quit("Duplicate labels: %S", (char *) linebuf);
} else {
lab->chain = 0;
lpt = lab;
if(++lab >= labend)
quit("Too many labels: %S", (char *) linebuf);
}
lpt->address = rep;
if (*cp == '#')
continue;
rep--; /* reuse this slot */
break;
case 'a':
rep->command = ACOM;
if(rep->ad2.type != A_NONE)
quit(AD1MES, (char *) linebuf);
if(*cp == '\\') cp++;
if(*cp++ != '\n')
quit(CGMES, (char *) linebuf);
rep->u.text = p;
p = stext(p, addend);
break;
case 'c':
rep->command = CCOM;
if(*cp == '\\') cp++;
if(*cp++ != '\n')
quit(CGMES, (char *) linebuf);
rep->u.text = p;
p = stext(p, addend);
break;
case 'i':
rep->command = ICOM;
if(rep->ad2.type != A_NONE)
quit(AD1MES, (char *) linebuf);
if(*cp == '\\') cp++;
if(*cp++ != '\n')
quit(CGMES, (char *) linebuf);
rep->u.text = p;
p = stext(p, addend);
break;
case 'g':
rep->command = GCOM;
break;
case 'G':
rep->command = CGCOM;
break;
case 'h':
rep->command = HCOM;
break;
case 'H':
rep->command = CHCOM;
break;
case 't':
rep->command = TCOM;
goto jtcommon;
case 'b':
rep->command = BCOM;
jtcommon:
while(*cp == ' ')cp++;
if(*cp == '\0') {
if(pt = ltab[0].chain) {
while(pt1 = pt->u.lb1)
pt = pt1;
pt->u.lb1 = rep;
} else
ltab[0].chain = rep;
break;
}
tp = lab->asc;
while((*tp++ = *cp++))
if(tp >= &(lab->asc[8]))
quit(LTL, (char *) linebuf);
cp--;
tp[-1] = '\0';
if(lpt = search(lab)) {
if(lpt->address) {
rep->u.lb1 = lpt->address;
} else {
pt = lpt->chain;
while(pt1 = pt->u.lb1)
pt = pt1;
pt->u.lb1 = rep;
}
} else {
lab->chain = rep;
lab->address = 0;
if(++lab >= labend)
quit("Too many labels: %S",
(char *) linebuf);
}
break;
case 'n':
rep->command = NCOM;
break;
case 'N':
rep->command = CNCOM;
break;
case 'p':
rep->command = PCOM;
break;
case 'P':
rep->command = CPCOM;
break;
case 'r':
rep->command = RCOM;
if(rep->ad2.type != A_NONE)
quit(AD1MES, (char *) linebuf);
if(*cp++ != ' ')
quit(CGMES, (char *) linebuf);
rep->u.text = p;
p = stext(p, addend);
break;
case 'd':
rep->command = DCOM;
break;
case 'D':
rep->command = CDCOM;
rep->u.lb1 = pspace;
break;
case 'q':
rep->command = QCOM;
if(rep->ad2.type != A_NONE)
quit(AD1MES, (char *) linebuf);
break;
case 'l':
rep->command = LCOM;
break;
case 's':
rep->command = SCOM;
seof = *cp++;
if ((rep->u.re1 = compile()) == 0) {
if(!lastre)
quit("First RE may not be null.", 0);
rep->u.re1 = lastre;
}
rep->rhs = p;
if((p = compsub(p, addend)) == 0)
quit(CGMES, (char *) linebuf);
if(*cp == 'g') {
cp++;
rep->gfl++;
} else if(gflag)
rep->gfl++;
if(*cp == 'p') {
cp++;
rep->pfl = 1;
}
if(*cp == 'P') {
cp++;
rep->pfl = 2;
}
if(*cp == 'w') {
cp++;
if(*cp++ != ' ')
quit(CGMES, (char *) linebuf);
text(fname[nfiles]);
for(i = nfiles - 1; i >= 0; i--)
if(cmp(fname[nfiles],fname[i]) == 0) {
rep->fcode = fcode[i];
goto done;
}
if(nfiles >= MAXFILES)
quit("Too many files in w commands 1", 0);
rep->fcode = open_file(fname[nfiles]);
}
break;
case 'w':
rep->command = WCOM;
if(*cp++ != ' ')
quit(CGMES, (char *) linebuf);
text(fname[nfiles]);
for(i = nfiles - 1; i >= 0; i--)
if(cmp(fname[nfiles], fname[i]) == 0) {
rep->fcode = fcode[i];
goto done;
}
if(nfiles >= MAXFILES){
fprint(2, "sed: Too many files in w commands 2 \n");
fprint(2, "nfiles = %d; MAXF = %d\n", nfiles, MAXFILES);
errexit();
}
rep->fcode = open_file(fname[nfiles]);
break;
case 'x':
rep->command = XCOM;
break;
case 'y':
rep->command = YCOM;
seof = *cp++;
if (ycomp(rep) == 0)
quit(CGMES, (char *) linebuf);
break;
}
done:
if(++rep >= pend)
quit("Too many commands, last: %S", (char *) linebuf);
if(*cp++ != '\0') {
if(cp[-1] == ';')
goto comploop;
quit(CGMES, (char *) linebuf);
}
}
}
Biobuf *
open_file(char *name)
{
Biobuf *bp;
int fd;
if ((bp = malloc(sizeof(Biobuf))) == 0)
quit("Out of memory", 0);
if ((fd = open(name, OWRITE)) < 0 &&
(fd = create(name, OWRITE, 0666)) < 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 < end)
*rhs++ = Runemax;
else
return 0;
r = *cp++;
if(r == 'n')
r = '\n';
} else {
if(r == seof) {
if (rhs < end)
*rhs++ = '\0';
else
return 0;
return rhs;
}
}
if (rhs < end)
*rhs++ = r;
else
return 0;
}
return 0;
}
Reprog *
compile(void)
{
Rune c;
char *ep;
char expbuf[512];
if((c = *cp++) == seof) /* '//' */
return 0;
ep = expbuf;
do {
if (c == 0 || c == '\n')
quit(TMMES, (char *) linebuf);
if (c == '\\') {
if (ep >= expbuf+sizeof(expbuf))
quit(TMMES, (char *) linebuf);
ep += runetochar(ep, &c);
if ((c = *cp++) == 'n')
c = '\n';
}
if (ep >= expbuf+sizeof(expbuf))
quit(TMMES, (char *) linebuf);
ep += runetochar(ep, &c);
} while ((c = *cp++) != seof);
*ep = 0;
return lastre = regcomp(expbuf);
}
void
regerror(char *s)
{
USED(s);
quit(CGMES, (char *) linebuf);
}
void
newfile(enum PTYPE type, char *name)
{
if (type == P_ARG)
prog.pctl.curr = name;
else if ((prog.pctl.bp = Bopen(name, OREAD)) == 0)
quit("Cannot open pattern-file: %s\n", name);
prog.type = type;
}
int
rline(Rune *buf, Rune *end)
{
long c;
Rune r;
while ((c = getrune()) >= 0) {
r = c;
if (r == '\\') {
if (buf <= end)
*buf++ = r;
if ((c = getrune()) < 0)
break;
r = c;
} else if (r == '\n') {
*buf = '\0';
return(1);
}
if (buf <= end)
*buf++ = r;
}
*buf = '\0';
return(-1);
}
long
getrune(void)
{
char *p;
long c;
Rune r;
if (prog.type == P_ARG) {
if ((p = prog.pctl.curr) != 0) {
if (*p) {
prog.pctl.curr += chartorune(&r, p);
c = r;
} else {
c = '\n'; /* fake an end-of-line */
prog.pctl.curr = 0;
}
} else
c = -1;
} else if ((c = Bgetrune(prog.pctl.bp)) < 0)
Bterm(prog.pctl.bp);
return c;
}
void
address(Addr *ap)
{
int c;
long lno;
if((c = *cp++) == '$')
ap->type = A_DOL;
else if(c == '/') {
seof = c;
if (ap->u.rp = compile())
ap->type = A_RE;
else
ap->type = A_LAST;
}
else if (c >= '0' && c <= '9') {
lno = c-'0';
while ((c = *cp) >= '0' && c <= '9')
lno = lno*10 + *cp++-'0';
if(!lno)
quit("line number 0 is illegal",0);
ap->type = A_LINE;
ap->u.line = lno;
}
else {
cp--;
ap->type = A_NONE;
}
}
int
cmp(char *a, char *b) /* compare characters */
{
while(*a == *b++)
if (*a == '\0')
return(0);
else a++;
return(1);
}
int
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 == '\t' || *cp == ' ')
cp++;
while (*cp) {
if ((r = *cp++) == '\\')
if ((r = *cp++) == 0)
break;;
if (r == '\n')
while (*cp == '\t' || *cp == ' ')
cp++;
p += runetochar(p, &r);
}
*p++ = '\0';
return p;
}
Rune *
stext(Rune *p, Rune *end) /* extract rune string */
{
while(*cp == '\t' || *cp == ' ')
cp++;
while (*cp) {
if (*cp == '\\')
if (*++cp == 0)
break;
if (p >= end-1)
quit(TMMES, (char *) linebuf);
if ((*p++ = *cp++) == '\n')
while(*cp == '\t' || *cp == ' ')
cp++;
}
*p++ = 0;
return p;
}
Label *
search (Label *ptr)
{
Label *rp;
for (rp = ltab; rp < ptr; rp++)
if(rcmp(rp->asc, ptr->asc) == 0)
return(rp);
return(0);
}
void
dechain(void)
{
Label *lptr;
SedCom *rptr, *trptr;
for(lptr = ltab; lptr < lab; lptr++) {
if(lptr->address == 0)
quit("Undefined label: %S", (char *) lptr->asc);
if(lptr->chain) {
rptr = lptr->chain;
while(trptr = rptr->u.lb1) {
rptr->u.lb1 = lptr->address;
rptr = trptr;
}
rptr->u.lb1 = lptr->address;
}
}
}
int
ycomp(SedCom *r)
{
int i;
Rune *rp;
Rune c, *tsp, highc;
Rune *sp;
highc = 0;
for(tsp = cp; *tsp != seof; tsp++) {
if(*tsp == '\\')
tsp++;
if(*tsp == '\n' || *tsp == '\0')
return(0);
if (*tsp > highc) highc = *tsp;
}
tsp++;
if ((rp = r->u.text = (Rune *) malloc(sizeof(Rune)*(highc+2))) == 0)
quit("Out of memory", 0);
*rp++ = highc; /* save upper bound */
for (i = 0; i <= highc; i++)
rp[i] = i;
sp = cp;
while((c = *sp++) != seof) {
if(c == '\\' && *sp == 'n') {
sp++;
c = '\n';
}
if((rp[c] = *tsp++) == '\\' && *tsp == 'n') {
rp[c] = '\n';
tsp++;
}
if(rp[c] == seof || rp[c] == '\0') {
free(r->u.re1);
r->u.re1 = 0;
return(0);
}
}
if(*tsp != seof) {
free(r->u.re1);
r->u.re1 = 0;
return(0);
}
cp = tsp+1;
return(1);
}
void
execute(void)
{
SedCom *ipc;
while (spend = gline(linebuf)){
for(ipc = pspace; ipc->command; ) {
if (!executable(ipc)) {
ipc++;
continue;
}
command(ipc);
if(delflag)
break;
if(jflag) {
jflag = 0;
if((ipc = ipc->u.lb1) == 0)
break;
} else
ipc++;
}
if(!nflag && !delflag)
putline(&fout, linebuf, spend-linebuf);
if(aptr > abuf) {
arout();
}
delflag = 0;
}
}
/* determine if a statement should be applied to an input line */
int
executable(SedCom *ipc)
{
if (ipc->active) { /* Addr1 satisfied - accept until Addr2 */
if (ipc->active == 1) /* Second line */
ipc->active = 2;
switch(ipc->ad2.type) {
case A_NONE: /* No second addr; use first */
ipc->active = 0;
break;
case A_DOL: /* Accept everything */
return !ipc->negfl;
case A_LINE: /* Line at end of range? */
if (lnum <= ipc->ad2.u.line) {
if (ipc->ad2.u.line == lnum)
ipc->active = 0;
return !ipc->negfl;
}
ipc->active = 0; /* out of range */
return ipc->negfl;
case A_RE: /* Check for matching R.E. */
if (match(ipc->ad2.u.rp, linebuf))
ipc->active = 0;
return !ipc->negfl;
default: /* internal error */
quit("Internal error", 0);
}
}
switch (ipc->ad1.type) { /* Check first address */
case A_NONE: /* Everything matches */
return !ipc->negfl;
case A_DOL: /* Only last line */
if (dolflag)
return !ipc->negfl;
break;
case A_LINE: /* Check line number */
if (ipc->ad1.u.line == lnum) {
ipc->active = 1; /* In range */
return !ipc->negfl;
}
break;
case A_RE: /* Check R.E. */
if (match(ipc->ad1.u.rp, linebuf)) {
ipc->active = 1; /* In range */
return !ipc->negfl;
}
break;
default:
quit("Internal error", 0);
}
return ipc->negfl;
}
int
match(Reprog *pattern, Rune *buf)
{
if (!pattern)
return 0;
subexp[0].s.rsp = buf;
subexp[0].e.rep = 0;
if (rregexec(pattern, linebuf, subexp, MAXSUB) > 0) {
loc1 = subexp[0].s.rsp;
loc2 = subexp[0].e.rep;
return 1;
}
loc1 = loc2 = 0;
return 0;
}
int
substitute(SedCom *ipc)
{
int len;
if(!match(ipc->u.re1, linebuf))
return 0;
/*
* we have at least one match. some patterns, e.g. '$' or '^', can
* produce zero-length matches, so during a global substitute we
* must bump to the character after a zero-length match to keep from looping.
*/
sflag = 1;
if(ipc->gfl == 0) /* single substitution */
dosub(ipc->rhs);
else
do{ /* global substitution */
len = loc2-loc1; /* length of match */
dosub(ipc->rhs); /* dosub moves loc2 */
if(*loc2 == 0) /* end of string */
break;
if(len == 0) /* zero-length R.E. match */
loc2++; /* bump over zero-length match */
if(*loc2 == 0) /* end of string */
break;
} while(match(ipc->u.re1, loc2));
return 1;
}
void
dosub(Rune *rhsbuf)
{
Rune *lp, *sp;
Rune *rp;
int c, n;
lp = linebuf;
sp = genbuf;
rp = rhsbuf;
while (lp < loc1)
*sp++ = *lp++;
while(c = *rp++) {
if (c == '&') {
sp = place(sp, loc1, loc2);
continue;
}
if (c == Runemax && (c = *rp++) >= '1' && c < MAXSUB+'0') {
n = c-'0';
if (subexp[n].s.rsp && subexp[n].e.rep) {
sp = place(sp, subexp[n].s.rsp, subexp[n].e.rep);
continue;
}
else {
fprint(2, "sed: Invalid back reference \\%d\n",n);
errexit();
}
}
*sp++ = c;
if (sp >= &genbuf[LBSIZE])
fprint(2, "sed: Output line too long.\n");
}
lp = loc2;
loc2 = sp - genbuf + linebuf;
while (*sp++ = *lp++)
if (sp >= &genbuf[LBSIZE])
fprint(2, "sed: Output line too long.\n");
lp = linebuf;
sp = genbuf;
while (*lp++ = *sp++)
;
spend = lp-1;
}
Rune *
place(Rune *sp, Rune *l1, Rune *l2)
{
while (l1 < l2) {
*sp++ = *l1++;
if (sp >= &genbuf[LBSIZE])
fprint(2, "sed: Output line too long.\n");
}
return(sp);
}
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>>12)&0xF];
buf[3] = hex[(c>>8)&0xF];
buf[4] = hex[(c>>4)&0xF];
buf[5] = hex[c&0xF];
return buf;
}
void
command(SedCom *ipc)
{
int i, c;
Rune *p1, *p2;
char *ucp;
Rune *rp;
Rune *execp;
switch(ipc->command) {
case ACOM:
*aptr++ = ipc;
if(aptr >= abuf+MAXADDS) {
quit("sed: Too many appends after line %ld\n",
(char *) lnum);
}
*aptr = 0;
break;
case CCOM:
delflag = 1;
if(ipc->active == 1) {
for(rp = ipc->u.text; *rp; rp++)
Bputrune(&fout, *rp);
Bputc(&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:
Bprint(&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 >= 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 >= hend)
break;
hspend = p1-1;
break;
case ICOM:
for(rp = ipc->u.text; *rp; rp++)
Bputrune(&fout, *rp);
Bputc(&fout, '\n');
break;
case BCOM:
jflag = 1;
break;
case LCOM:
c = 0;
for (i = 0, rp = linebuf; *rp; rp++) {
c = *rp;
if(c >= 0x20 && c < 0x7F && c != '\\') {
Bputc(&fout, c);
if(i++ > 71) {
Bprint(&fout, "\\\n");
i = 0;
}
} else {
for (ucp = trans(*rp); *ucp; ucp++){
c = *ucp;
Bputc(&fout, c);
if(i++ > 71) {
Bprint(&fout, "\\\n");
i = 0;
}
}
}
}
if(c == ' ')
Bprint(&fout, "\\n");
Bputc(&fout, '\n');
break;
case NCOM:
if(!nflag)
putline(&fout, linebuf, spend-linebuf);
if(aptr > abuf)
arout();
if((execp = gline(linebuf)) == 0) {
delflag = 1;
break;
}
spend = execp;
break;
case CNCOM:
if(aptr > abuf)
arout();
*spend++ = '\n';
if((execp = gline(spend)) == 0) {
delflag = 1;
break;
}
spend = execp;
break;
case PCOM:
putline(&fout, linebuf, spend-linebuf);
break;
case CPCOM:
cpcom:
for(rp = linebuf; *rp && *rp != '\n'; rp++)
Bputc(&fout, *rp);
Bputc(&fout, '\n');
break;
case QCOM:
if(!nflag)
putline(&fout, linebuf, spend-linebuf);
if(aptr > abuf)
arout();
exits(0);
case RCOM:
*aptr++ = ipc;
if(aptr >= &abuf[MAXADDS])
quit("sed: Too many reads after line %ld\n",
(char *) lnum);
*aptr = 0;
break;
case SCOM:
i = substitute(ipc);
if(i && ipc->pfl)
if(ipc->pfl == 1)
putline(&fout, linebuf, spend-linebuf);
else
goto cpcom;
if(i && ipc->fcode)
goto wcom;
break;
case TCOM:
if(sflag == 0) break;
sflag = 0;
jflag = 1;
break;
wcom:
case WCOM:
putline(ipc->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->u.text;
for (i = *p2++; *p1; p1++){
if (*p1 <= i) *p1 = p2[*p1];
}
break;
}
}
void
putline(Biobuf *bp, Rune *buf, int n)
{
while (n--)
Bputrune(bp, *buf++);
Bputc(bp, '\n');
if(lflag)
Bflush(bp);
}
int
ecmp(Rune *a, Rune *b, int count)
{
while(count--)
if(*a++ != *b++) return(0);
return(1);
}
void
arout(void)
{
Rune *p1;
Biobuf *fi;
int c;
char *s;
char buf[128];
for (aptr = abuf; *aptr; aptr++) {
if((*aptr)->command == ACOM) {
for(p1 = (*aptr)->u.text; *p1; p1++ )
Bputrune(&fout, *p1);
Bputc(&fout, '\n');
} else {
for(s = buf, p1= (*aptr)->u.text; *p1; p1++)
s += runetochar(s, p1);
*s = '\0';
if((fi = Bopen(buf, OREAD)) == 0)
continue;
while((c = Bgetc(fi)) >= 0)
Bputc(&fout, c);
Bterm(fi);
}
}
aptr = abuf;
*aptr = 0;
}
void
errexit(void)
{
exits("error");
}
void
quit (char *msg, char *arg)
{
fprint(2, "sed: ");
fprint(2, msg, arg);
fprint(2, "\n");
errexit();
}
Rune *
gline(Rune *addr)
{
long c;
Rune *p;
static long peekc = 0;
if (f == 0 && opendata() < 0)
return 0;
sflag = 0;
lnum++;
/* Bflush(&fout);********* dumped 4/30/92 - bobf****/
do {
p = addr;
for (c = (peekc ? peekc : Bgetrune(f)); c >= 0; c = Bgetrune(f)) {
if (c == '\n') {
if ((peekc = Bgetrune(f)) < 0) {
if (fhead == 0)
dolflag = 1;
}
*p = '\0';
return p;
}
if (c && p < lbend)
*p++ = c;
}
/* return partial final line, adding implicit newline */
if(p != addr) {
*p = '\0';
peekc = -1;
if (fhead == 0)
dolflag = 1;
return p;
}
peekc = 0;
Bterm(f);
} while (opendata() > 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))) == 0)
quit("Out of memory", 0);
if (ftail == 0)
fhead = fp;
else
ftail->next = fp;
ftail = fp;
fp->next = 0;
fp->name = filename; /* 0 => stdin */
}
int
opendata(void)
{
if (fhead == 0)
return -1;
if (fhead->name) {
if ((f = Bopen(fhead->name, OREAD)) == 0)
quit("Can't open %s", fhead->name);
} else {
Binit(&bstdin, 0, OREAD);
f = &bstdin;
}
fhead = fhead->next;
return 1;
}