Ultrix-3.1/src/cmd/ed/ed.c
/**********************************************************************
* Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. *
* All Rights Reserved. *
* Reference "/usr/src/COPYRIGHT" for applicable restrictions. *
**********************************************************************/
static char sccsid[] = "@(#)ed.c 3.2 12/31/87";
/*
* SYSTEM V ed text editor
*/
/* Based on: (SYSTEM V) ed.c 1.23 */
/*
* Changes for ULTRIX-11:
* Added 'W', append to an existing file.
* Added #ifdef FSPEC for format specifications.
*/
/*
** The assembly code for ed.c should be
** altered making the .data's for the
** following array .text's so that it
** can be shared (a la the Shell).
** Use the 'edfun' script to accomplish this.
*/
char *msgtab[] =
{
"write or open on pipe failed", /* 0 */
"warning: expecting `w'", /* 1 */
"mark not lower case", /* 2 */
"cannot open input file", /* 3 */
"PWB spec problem", /* 4 */
"nothing to undo", /* 5 */
"restricted shell", /* 6 */
"cannot create output file", /* 7 */
"filesystem out of space!", /* 8 */
"cannot open file", /* 9 */
"cannot link", /* 10 */
"Range endpoint too large", /* 11 */
"unknown command", /* 12 */
"search string not found", /* 13 */
"-", /* 14 */
"line out of range", /* 15 */
"bad number", /* 16 */
"bad range", /* 17 */
"Illegal address count", /* 18 */
"incomplete global expression", /* 19 */
"illegal suffix", /* 20 */
"illegal or missing filename", /* 21 */
"no space after command", /* 22 */
"fork failed - try again", /* 23 */
"maximum of 64 characters in file names", /* 24 */
"`\\digit' out of range", /* 25 */
"interrupt", /* 26 */
"line too long", /* 27 */
"illegal character in input file", /* 28 */
"write error", /* 29 */
"out of memory for append", /* 30 */
"temp file too big", /* 31 */
"I/O error on temp file", /* 32 */
"multiple globals not allowed", /* 33 */
"global too long", /* 34 */
"no match", /* 35 */
"illegal or missing delimiter", /* 36 */
"-", /* 37 */
"replacement string too long", /* 38 */
"illegal move destination", /* 39 */
"-", /* 40 */
"no remembered search string", /* 41 */
"'\\( \\)' imbalance", /* 42 */
"Too many `\\(' s", /* 43 */
"more than 2 numbers given", /* 44 */
"'\\}' expected", /* 45 */
"first number exceeds second", /* 46 */
"incomplete substitute", /* 47 */
"newline unexpected", /* 48 */
"'[ ]' imbalance", /* 49 */
"regular expression overflow", /* 50 */
"regular expression error", /* 51 */
"command expected", /* 52 */
"a, i, or c not allowed in G", /* 53 */
"end of line expected", /* 54 */
"no remembered replacement string", /* 55 */
"no remembered command", /* 56 */
"illegal redirection", /* 57 */
"possible concurrent update", /* 58 */
"that command confuses yed", /* 59 */
"the x command has become X (upper case)", /* 60 */
"Warning: 'w' may destroy input file (due to `illegal char' read earlier)", /* 61 */
"Caution: 'q' may lose data in buffer; 'w' may destroy input file", /* 62 */
0
};
/*
* Define some macros for the regular expression
* routines to use for input and error stuff.
*/
#define INIT extern int peekc;
#define GETC() getchr()
#define UNGETC(c) (peekc = c)
#define PEEKC() (peekc = getchr())
#define RETURN(c) return
#define ERROR(c) error1(c)
#include "regexp.h"
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sgtty.h>
#include <sys/ioctl.h>
#include <setjmp.h>
#define PUTM() if(xcode >= 0) puts(msgtab[xcode])
#define FNSIZE 64
#define LBSIZE 512
#define ESIZE 256
#define GBSIZE 256
#define KSIZE 9
#define READ 0
#define WRITE 1
#define PRNT 02
int Xqt = 0;
int lastc;
char savedfile[FNSIZE];
char efile[FNSIZE];
char funny[LBSIZE];
char linebuf[LBSIZE];
char expbuf[ESIZE];
char rhsbuf[LBSIZE];
struct lin {
int cur;
int sav;
};
typedef struct lin *LINE;
LINE zero;
LINE dot;
LINE dol;
LINE endcore;
LINE fendcore;
LINE addr1;
LINE addr2;
LINE savdol, savdot;
int globflg;
int initflg;
char genbuf[LBSIZE];
long count;
char *nextip;
char *linebp;
int ninbuf;
int peekc;
int io;
int (*oldhup)();
int (*oldquit)(), (*oldpipe)();
int vflag = 1;
int yflag;
int hflag;
int xcode = -1;
int col;
char *globp;
int tfile = -1;
int tline;
char *tfname;
extern char *locs;
char ibuff[512];
int iblock = -1;
char obuff[512];
int oblock = -1;
int ichanged;
int nleft;
int names[26];
int anymarks;
int subnewa;
int fchange;
int nline;
int fflg, shflg;
char prompt[16] = "*";
int wrapp=0;
int rflg;
int readflg;
int eflg;
int ncflg;
int listn;
int listf;
int pflag;
int flag28 = 0; /* Prevents write after a partial read */
int save28 = 0; /* Flag whether buffer empty at start of read */
long savtime;
char *name = "SHELL";
char *rshell = "/bin/rsh";
char *val;
char *home;
char *getenv();
char *calloc();
LINE address();
char *getline();
char *getblock();
char *place();
int (*signal())();
char *mktemp();
char *sbrk();
struct stat Fl, Tf, Lc;
#ifndef RESEARCH
/* struct ustat U; */
int Short = 0;
int oldmask; /* No umask while writing */
#endif
jmp_buf savej;
#ifdef NULLS
int nulls; /* Null count */
#endif NULLS
long ccount;
struct Fspec {
char Ftabs[22];
char Fdel;
char Flim;
char Fmov;
char Ffill;
};
struct Fspec fss;
int errcnt = 0;
onpipe()
{
error(0);
}
main(argc, argv)
char **argv;
{
register char *p1, *p2;
extern int onintr(), quit(), onhup();
int (*oldintr)();
oldquit = signal(SIGQUIT, SIG_IGN);
oldhup = signal(SIGHUP, SIG_IGN);
oldintr = signal(SIGINT, SIG_IGN);
oldpipe = signal(SIGPIPE, onpipe);
if (((int)signal(SIGTERM, SIG_IGN)&01) == 0)
signal(SIGTERM, quit);
p1 = *argv;
while(*p1++);
while(--p1 >= *argv)
if(*p1 == '/')
break;
*argv = p1 + 1;
/* if SHELL set in environment and is /bin/rsh, set rflg */
if((val = getenv(name)) != NULL)
if (strcmp(val, rshell) == 0)
rflg++;
if (**argv == 'r')
rflg++;
home = getenv("HOME");
argv++;
while (argc > 1 && **argv=='-') {
switch((*argv)[1]) {
case '\0':
vflag = 0;
break;
case 'p':
argv++;
argc--;
if(!*argv) {
printf("ed: -p arg missing\n");
exit(2);
}
strncpy(prompt, *argv, 16);
shflg = 1;
break;
case 'q':
signal(SIGQUIT, SIG_DFL);
vflag = 1;
break;
case 'y':
yflag = 03;
break;
}
argv++;
argc--;
}
if (argc>1) {
p1 = *argv;
if(strlen(p1) >= FNSIZE) {
puts("file name too long");
exit(2);
}
p2 = savedfile;
while (*p2++ = *p1++);
globp = "r";
fflg++;
}
else /* editing with no file so set savtime to 0 */
savtime = 0;
eflg++;
fendcore = (LINE )sbrk(0);
tfname = mktemp("/tmp/eXXXXX");
init();
if (((int)oldintr&01) == 0)
signal(SIGINT, onintr);
if (((int)oldhup&01) == 0)
signal(SIGHUP, onhup);
setjmp(savej);
commands();
quit();
}
commands()
{
int getfile(), gettty();
register LINE a1;
register c;
register char *p1, *p2;
int n, cc;
char buf[FNSIZE];
for (;;) {
nodelim = 0;
if ( pflag ) {
pflag = 0;
addr1 = addr2 = dot;
goto print;
}
if (shflg && globp==0)
write(1, prompt, strlen(prompt));
addr1 = 0;
addr2 = 0;
if((c=getchr()) == ',') {
addr1 = zero + 1;
addr2 = dol;
c = getchr();
goto swch;
} else if(c == ';') {
addr1 = dot;
addr2 = dol;
c = getchr();
goto swch;
} else
peekc = c;
do {
addr1 = addr2;
if ((a1 = address())==0) {
c = getchr();
break;
}
addr2 = a1;
if ((c=getchr()) == ';') {
c = ',';
dot = a1;
}
} while (c==',');
if (addr1==0)
addr1 = addr2;
swch:
switch(c) {
case 'a':
setdot();
newline();
if (!globflg) save();
append(gettty, addr2);
continue;
case 'c':
delete();
append(gettty, addr1-1);
continue;
case 'd':
delete();
continue;
case 'E':
fchange = 0;
c = 'e';
case 'e':
fflg++;
setnoaddr();
if (vflag && fchange) {
fchange = 0;
error(1);
}
filename(c);
eflg++;
init();
addr2 = zero;
goto caseread;
case 'f':
setnoaddr();
filename(c);
if (!ncflg) /* there is a filename */
getime();
else
ncflg--;
puts(savedfile);
continue;
case 'g':
global(1);
continue;
case 'G':
globaln(1);
continue;
case 'h':
newline();
setnoaddr();
PUTM();
continue;
case 'H':
newline();
setnoaddr();
if(!hflag) {
hflag = 1;
PUTM();
}
else
hflag = 0;
continue;
case 'i':
setdot();
nonzero();
newline();
if (!globflg) save();
append(gettty, addr2-1);
if (dot == addr2-1)
dot += 1;
continue;
case 'j':
if (addr2==0) {
addr1 = dot;
addr2 = dot+1;
}
setdot();
newline();
nonzero();
if (!globflg) save();
join();
continue;
case 'k':
if ((c = getchr()) < 'a' || c > 'z')
error(2);
newline();
setdot();
nonzero();
names[c-'a'] = addr2->cur & ~01;
anymarks |= 01;
continue;
case 'm':
move(0);
continue;
case '\n':
if (addr2==0)
addr2 = dot+1;
addr1 = addr2;
goto print;
case 'n':
listn++;
newline();
goto print;
case 'l':
listf++;
case 'p':
newline();
print:
setdot();
nonzero();
a1 = addr1;
do {
if (listn) {
count = a1 - zero;
putd();
putchr('\t');
}
puts(getline(a1++->cur));
}
while (a1 <= addr2);
dot = addr2;
pflag = 0;
listn = 0;
listf = 0;
continue;
case 'Q':
fchange = 0;
case 'q':
setnoaddr();
newline();
quit();
case 'r':
filename(c);
caseread:
readflg = 1;
save28 = (dol != fendcore);
if ((io = eopen(efile, 0)) < 0) {
lastc = '\n';
/* if first entering editor and file does not exist */
/* set saved access time to 0 */
if (eflg) {
savtime = 0;
eflg = 0;
}
error(3);
}
/* get last mod time of file */
/* eflg - entered editor with ed or e */
if (eflg) {
eflg = 0;
getime();
}
setall();
ninbuf = 0;
n = zero != dol;
#ifdef NULLS
nulls = 0;
#endif NULLS
if (!globflg && (c == 'r')) save();
append(getfile, addr2);
exfile();
readflg = 0;
fchange = n;
continue;
case 's':
setdot();
nonzero();
if (!globflg) save();
substitute(globp!=0);
continue;
case 't':
move(1);
continue;
case 'u':
setdot();
newline();
if (!initflg) undo();
else error(5);
fchange = 1;
continue;
case 'v':
global(0);
continue;
case 'V':
globaln(0);
continue;
case 'W':
wrapp++;
case 'w':
if(flag28){flag28 = 0; fchange = 0; error(61);}
setall();
if((zero != dol) && (addr1 <= zero || addr2 > dol))
error(15);
filename(c);
if(Xqt) {
io = eopen(efile, 1);
n = 1; /* set n so newtime will not execute */
} else {
fstat(tfile,&Tf);
if (!wrapp || ((io = open(efile, 1)) == -1) ||
((lseek(io, 0L, 2)) == -1)) {
if (stat(efile, &Fl) < 0) {
wrapp=0; /* do w not W, even if W was specified since file not found */
if((io = creat(efile, 0666)) < 0)
error(7);
fstat(io, &Fl);
Fl.st_mtime = 0;
close(io);
}
else {
#ifndef RESEARCH
oldmask = umask(0);
#endif RESEARCH
}
}
#ifndef RESEARCH
/*
ustat(Fl.st_dev, &U);
if(!Short && U.f_tfree < ((Tf.st_size>>9) + 100)) {
Short = 1;
error(8);
}
Short = 0;
*/
#endif RESEARCH
if (Fl.st_nlink == 1 && (Fl.st_mode & S_IFMT) == S_IFREG) {
if (wrapp) /* append to file */
goto outj;
if (close(open(efile, 1)) < 0)
error(9);
p1 = savedfile;
p2 = efile;
if (!(n=strcmp(p1, p2)))
chktime();
mkfunny();
if ((io = creat(funny, Fl.st_mode)) >= 0) {
chown(funny, Fl.st_uid, Fl.st_gid);
chmod(funny, Fl.st_mode);
wrapp=0;
putfile();
exfile();
lstat(efile, &Lc);
if ((Lc.st_mode & S_IFMT) == S_IFLNK) {
if( Slink( funny, efile, &Fl ) < 0 )
error(10);
} else {
if (0 != unlink(efile))
perror("From file");
if( link(funny, efile) < 0)
error(10);
}
if (0 != unlink(funny))
perror("From funny");
/* if filenames are the same */
if (!n)
newtime();
/* check if entire buffer was written */
fchange = ((addr1==zero || addr1==zero+1) && addr2==dol)?0:fchange;
continue;
}
}
else n = 1; /* set n so newtime will not execute*/
if (!wrapp) {
if((io = creat(efile, 0666)) < 0)
error(7);
} else
error(1);
}
outj:
wrapp=0;
putfile();
exfile();
if (!n) newtime();
fchange = ((addr1==zero||addr1==zero+1)&&addr2==dol)?0:fchange;
continue;
case '=':
setall();
newline();
count = (addr2-zero)&077777;
putd();
putchr('\n');
continue;
case '!':
unixcom();
continue;
case EOF:
return;
case 'P':
if (yflag)
error(59);
setnoaddr();
newline();
if (shflg)
shflg = 0;
else
shflg++;
continue;
}
if (c == 'x')
error(60);
else
error(12);
error(12);
}
}
LINE
address()
{
register minus, c;
register LINE a1;
int n, relerr;
minus = 0;
a1 = 0;
for (;;) {
c = getchr();
if ('0'<=c && c<='9') {
n = 0;
do {
n *= 10;
n += c - '0';
} while ((c = getchr())>='0' && c<='9');
peekc = c;
if (a1==0)
a1 = zero;
if (minus<0)
n = -n;
a1 += n;
minus = 0;
continue;
}
relerr = 0;
if (a1 || minus)
relerr++;
switch(c) {
case ' ':
case '\t':
continue;
case '+':
minus++;
if (a1==0)
a1 = dot;
continue;
case '-':
case '^':
minus--;
if (a1==0)
a1 = dot;
continue;
case '?':
case '/':
compile((char *) 0, expbuf, &expbuf[ESIZE], c);
a1 = dot;
for (;;) {
if (c=='/') {
a1++;
if (a1 > dol)
a1 = zero;
} else {
a1--;
if (a1 < zero)
a1 = dol;
}
if (execute(0, a1))
break;
if (a1==dot)
error(13);
}
break;
case '$':
a1 = dol;
break;
case '.':
a1 = dot;
break;
case '\'':
if ((c = getchr()) < 'a' || c > 'z')
error(2);
for (a1=zero; a1<=dol; a1++)
if (names[c-'a'] == (a1->cur & ~01))
break;
break;
case 'y' & 037:
if(yflag) {
newline();
setnoaddr();
yflag ^= 01;
continue;
}
default:
peekc = c;
if (a1==0)
return(0);
a1 += minus;
if (a1<zero || a1>dol)
error(15);
return(a1);
}
if (relerr)
error(16);
}
}
setdot()
{
if (addr2 == 0)
addr1 = addr2 = dot;
if (addr1 > addr2)
error(17);
}
setall()
{
if (addr2==0) {
addr1 = zero+1;
addr2 = dol;
if (dol==zero)
addr1 = zero;
}
setdot();
}
setnoaddr()
{
if (addr2)
error(18);
}
nonzero()
{
if (addr1<=zero || addr2>dol)
error(15);
}
newline()
{
register c;
c = getchr();
if ( c == 'p' || c == 'l' || c == 'n' ) {
pflag++;
if ( c == 'l') listf++;
if ( c == 'n') listn++;
c = getchr();
}
if ( c != '\n')
error(20);
}
filename(comm)
{
register char *p1, *p2;
register c;
register i = 0;
count = 0;
c = getchr();
if (c=='\n' || c==EOF) {
p1 = savedfile;
if (*p1==0 && comm!='f')
error(21);
/* ncflg set means do not get mod time of file */
/* since no filename followed f */
if (comm == 'f')
ncflg++;
p2 = efile;
while (*p2++ = *p1++);
red(savedfile);
return;
}
if (c!=' ')
error(22);
while ((c = getchr()) == ' ');
if(c == '!')
++Xqt, c = getchr();
if (c=='\n')
error(21);
p1 = efile;
do {
if(++i >= FNSIZE)
error(24);
*p1++ = c;
if(c==EOF || (c==' ' && !Xqt))
error(21);
} while ((c = getchr()) != '\n');
*p1++ = 0;
if(Xqt)
if (comm=='f') {
--Xqt;
error(57);
}
else
return;
if (savedfile[0]==0 || comm=='e' || comm=='f') {
p1 = savedfile;
p2 = efile;
while (*p1++ = *p2++);
}
red(efile);
}
exfile()
{
#ifdef NULLS
register c;
#endif NULLS
#ifndef RESEARCH
if(oldmask) {
umask(oldmask);
oldmask = 0;
}
#endif RESEARCH
eclose(io);
io = -1;
if (vflag) {
putd();
putchr('\n');
#ifdef NULLS
if(nulls) {
c = count;
count = nulls;
nulls = 0;
putd();
puts(" nulls replaced by '\\0'");
count = c;
}
#endif NULLS
}
}
onintr()
{
signal(SIGINT, onintr);
putchr('\n');
lastc = '\n';
if (*funny) unlink(funny); /* remove tmp file */
/* if interruped a read, only part of file may be in buffer */
if ( readflg ) {
puts ("\007read may be incomplete - beware!\007");
fchange = 0;
}
error(26);
}
onhup()
{
signal(SIGINT, SIG_IGN);
signal(SIGHUP, SIG_IGN);
/* if there are lines in file and file was */
/* not written since last update, save in ed.hup, or $HOME/ed.hup */
if (dol > zero && fchange == 1) {
addr1 = zero+1;
addr2 = dol;
io = creat("ed.hup", 0666);
if(io < 0 && home) {
char *fn;
fn = calloc(strlen(home) + 8, sizeof(char));
if(fn) {
strcpy(fn, home);
strcat(fn, "/ed.hup");
io = creat(fn, 0666);
free(fn);
}
}
if (io > 0)
putfile();
}
fchange = 0;
quit();
}
error(code)
register code;
{
register c;
if(code == 28 && save28 == 0){fchange = 0; flag28++;}
readflg = 0;
++errcnt;
listf = listn = 0;
pflag = 0;
#ifndef RESEARCH
if(oldmask) {
umask(oldmask);
oldmask = 0;
}
#endif RESEARCH
#ifdef NULLS /* Not really nulls, but close enough */
/* This is a bug because of buffering */
if(code == 28) /* illegal char. */
putd();
#endif NULLS
putchr('?');
if(code == 3) /* Cant open file */
puts(efile);
else
putchr('\n');
count = 0;
lseek(0, (long)0, 2);
if (globp)
lastc = '\n';
globp = 0;
peekc = lastc;
if(lastc)
while ((c = getchr()) != '\n' && c != EOF);
if (io > 0) {
eclose(io);
io = -1;
}
xcode = code;
if(hflag)
PUTM();
if(code==4)return(0); /* Non-fatal error. */
longjmp(savej, 1);
}
getchr()
{
char c;
if (lastc=peekc) {
peekc = 0;
return(lastc);
}
if (globp) {
if ((lastc = *globp++) != 0)
return(lastc);
globp = 0;
return(EOF);
}
if (read(0, &c, 1) <= 0)
return(lastc = EOF);
lastc = c&0177;
return(lastc);
}
gettty()
{
register c;
register char *gf;
register char *p;
p = linebuf;
gf = globp;
while ((c = getchr()) != '\n') {
if (c==EOF) {
if (gf)
peekc = c;
return(c);
}
if ((c &= 0177) == 0)
continue;
*p++ = c;
if (p >= &linebuf[LBSIZE-2])
error(27);
}
*p++ = 0;
if (linebuf[0]=='.' && linebuf[1]==0)
return(EOF);
if (linebuf[0]=='\\' && linebuf[1]=='.' && linebuf[2]==0) {
linebuf[0] = '.';
linebuf[1] = 0;
}
return(0);
}
getfile()
{
register c;
register char *lp, *fp;
int crflag;
crflag = 0;
lp = linebuf;
fp = nextip;
do {
if (--ninbuf < 0) {
if ((ninbuf = read(io, genbuf, LBSIZE)-1) < 0)
return(EOF);
fp = genbuf;
while(fp < &genbuf[ninbuf])
if(*fp++ & 0200) crflag = 1;
if(crflag){
error(28);
}
fp = genbuf;
while(fp < &genbuf[ninbuf]){
if(*fp++ & 0200) error(28);
}
fp = genbuf;
}
if (lp >= &linebuf[LBSIZE]) {
lastc = '\n';
error(27);
}
if ((*lp++ = c = *fp++ & 0177) == 0) {
#ifdef NULLS
lp[-1] = '\\';
*lp++ = '0';
nulls++;
#else
lp--;
continue;
#endif NULLS
}
count++;
} while (c != '\n');
*--lp = 0;
nextip = fp;
if (fss.Ffill && fss.Flim && lenchk(linebuf,&fss) < 0) {
write(1,"line too long: lno = ",21);
ccount = count;
count = (++dot-zero)&077777;
dot--;
putd();
count = ccount;
putchr('\n');
}
return(0);
}
putfile()
{
int n;
LINE a1;
register char *fp, *lp;
register nib;
nib = 512;
fp = genbuf;
a1 = addr1;
do {
lp = getline(a1++->cur);
if (fss.Ffill && fss.Flim && lenchk(linebuf,&fss) < 0) {
write(1,"line too long: lno = ",21);
ccount = count;
count = (++dot-zero)&077777;
dot--;
putd();
count = ccount;
putchr('\n');
}
for (;;) {
if (--nib < 0) {
n = fp-genbuf;
if(write(io, genbuf, n) != n)
error(29);
nib = 511;
fp = genbuf;
}
if(dol->cur == 0)break; /* Allow write of null file */
count++;
if ((*fp++ = *lp++) == 0) {
fp[-1] = '\n';
break;
}
}
} while (a1 <= addr2);
n = fp-genbuf;
if(write(io, genbuf, n) != n)
error(29);
}
append(f, a)
LINE a;
int (*f)();
{
register LINE a1, a2, rdot;
int tl;
nline = 0;
dot = a;
while ((*f)() == 0) {
if (dol >= endcore) {
if ((int)sbrk(512*sizeof(struct lin)) == -1) {
lastc = '\n';
error(30);
}
endcore += 512;
}
tl = putline();
nline++;
a1 = ++dol;
a2 = a1+1;
rdot = ++dot;
while (a1 > rdot)
(--a2)->cur = (--a1)->cur;
rdot->cur = tl;
}
}
unixcom()
{
register (*savint)(), pid, rpid;
int retcode;
static char savcmd[LBSIZE]; /* last command */
char curcmd[LBSIZE]; /* current command */
char *psavcmd, *pcurcmd, *psavedfile;
register c, endflg=1, shflg=0;
setnoaddr();
if(rflg)
error(6);
pcurcmd = curcmd;
/* read command til end */
/* a '!' found in beginning of command is replaced with the saved command.
a '%' found in command is replaced with the current filename */
c=getchr();
if (c == '!') {
if (savcmd[0]==0)
error(56);
else {
psavcmd = savcmd;
while (*pcurcmd++ = *psavcmd++);
--pcurcmd;
shflg = 1;
}
}
else UNGETC(c); /* put c back */
while (endflg==1) {
while ((c=getchr()) != '\n' && c != '%' && c != '\\')
*pcurcmd++ = c;
if (c=='%') {
if (savedfile[0]==0)
error(21);
else {
psavedfile = savedfile;
while(*pcurcmd++ = *psavedfile++);
--pcurcmd;
shflg = 1;
}
}
else if (c == '\\') {
c = getchr();
if (c != '%')
*pcurcmd++ = '\\';
*pcurcmd++ = c;
}
else
/* end of command hit */
endflg = 0;
}
*pcurcmd++ = 0;
if (shflg == 1)
puts(curcmd);
/* save command */
strcpy(savcmd,curcmd);
if ((pid = fork()) == 0) {
signal(SIGHUP, oldhup);
signal(SIGQUIT, oldquit);
execlp("/bin/sh", "sh", "-c", curcmd, (char *) 0);
exit(0100);
}
savint = signal(SIGINT, SIG_IGN);
while ((rpid = wait(&retcode)) != pid && rpid != -1);
signal(SIGINT, savint);
if (vflag) puts("!");
}
quit()
{
if (vflag && fchange) {
fchange = 0;
if(flag28){flag28 = 0; error(62);} /* For case where user reads
in BOTH a good file & a bad file */
error(1);
}
unlink(tfname);
exit(errcnt? 2: 0);
}
delete()
{
setdot();
newline();
nonzero();
if (!globflg) save();
rdelete(addr1, addr2);
}
rdelete(ad1, ad2)
LINE ad1, ad2;
{
register LINE a1, a2, a3;
a1 = ad1;
a2 = ad2+1;
a3 = dol;
dol -= a2 - a1;
do
a1++->cur = a2++->cur;
while (a2 <= a3);
a1 = ad1;
if (a1 > dol)
a1 = dol;
dot = a1;
fchange = 1;
}
gdelete()
{
register LINE a1, a2, a3;
a3 = dol;
for (a1=zero+1; (a1->cur&01)==0; a1++)
if (a1>=a3)
return;
for (a2=a1+1; a2<=a3;) {
if (a2->cur&01) {
a2++;
dot = a1;
} else
a1++->cur = a2++->cur;
}
dol = a1-1;
if (dot>dol)
dot = dol;
fchange = 1;
}
char *
getline(tl)
{
register char *bp, *lp;
register nl;
lp = linebuf;
bp = getblock(tl, READ);
nl = nleft;
tl &= ~0377;
while (*lp++ = *bp++)
if (--nl == 0) {
bp = getblock(tl+=0400, READ);
nl = nleft;
}
return(linebuf);
}
putline()
{
register char *bp, *lp;
register nl;
int tl;
fchange = 1;
lp = linebuf;
tl = tline;
bp = getblock(tl, WRITE);
nl = nleft;
tl &= ~0377;
while (*bp = *lp++) {
if (*bp++ == '\n') {
*--bp = 0;
linebp = lp;
break;
}
if (--nl == 0) {
bp = getblock(tl+=0400, WRITE);
nl = nleft;
}
}
nl = tline;
tline += (((lp-linebuf)+03)>>1)&077776;
return(nl);
}
char *
getblock(atl, iof)
{
extern read(), write();
register bno, off;
register char *p1, *p2;
register int n;
bno = (atl>>8)&0377;
off = (atl<<1)&0774;
if (bno >= 255) {
lastc = '\n';
error(31);
}
nleft = 512 - off;
if (bno==iblock) {
ichanged |= iof;
return(ibuff+off);
}
if (bno==oblock)
return(obuff+off);
if (iof==READ) {
if (ichanged) {
blkio(iblock, ibuff, write);
}
ichanged = 0;
iblock = bno;
blkio(bno, ibuff, read);
return(ibuff+off);
}
if (oblock>=0) {
blkio(oblock, obuff, write);
}
oblock = bno;
return(obuff+off);
}
blkio(b, buf, iofcn)
char *buf;
int (*iofcn)();
{
lseek(tfile, (long)b<<9, 0);
if ((*iofcn)(tfile, buf, 512) != 512) {
if(dol != zero)error(32); /* Bypass this if writing null file */
}
}
init()
{
register *markp;
int omask;
close(tfile);
tline = 2;
for (markp = names; markp < &names[26]; )
*markp++ = 0;
subnewa = 0;
anymarks = 0;
iblock = -1;
oblock = -1;
ichanged = 0;
initflg = 1;
omask = umask(0);
close(creat(tfname, 0600));
umask(omask);
tfile = open(tfname, 2);
brk((char *)fendcore);
dot = zero = dol = savdot = savdol = fendcore;
flag28 = save28 = 0;
endcore = fendcore - sizeof(struct lin);
}
global(k)
{
register char *gp;
register c;
register LINE a1;
char globuf[GBSIZE];
if (globp)
error(33);
setall();
nonzero();
if ((c=getchr())=='\n')
error(19);
save();
compile((char *) 0, expbuf, &expbuf[ESIZE], c);
gp = globuf;
while ((c = getchr()) != '\n') {
if (c==EOF)
error(19);
if (c=='\\') {
c = getchr();
if (c!='\n')
*gp++ = '\\';
}
*gp++ = c;
if (gp >= &globuf[GBSIZE-2])
error(34);
}
if (gp == globuf)
*gp++ = 'p';
*gp++ = '\n';
*gp++ = 0;
for (a1=zero; a1<=dol; a1++) {
a1->cur &= ~01;
if (a1>=addr1 && a1<=addr2 && execute(0, a1)==k)
a1->cur |= 01;
}
/*
* Special case: g/.../d (avoid n^2 algorithm)
*/
if (globuf[0]=='d' && globuf[1]=='\n' && globuf[2]=='\0') {
gdelete();
return;
}
for (a1=zero; a1<=dol; a1++) {
if (a1->cur & 01) {
a1->cur &= ~01;
dot = a1;
globp = globuf;
globflg = 1;
commands();
globflg = 0;
a1 = zero;
}
}
}
join()
{
register char *gp, *lp;
register LINE a1;
if (addr1 == addr2) return;
gp = genbuf;
for (a1=addr1; a1<=addr2; a1++) {
lp = getline(a1->cur);
while (*gp = *lp++)
if (gp++ >= &genbuf[LBSIZE-2])
error(27);
}
lp = linebuf;
gp = genbuf;
while (*lp++ = *gp++);
addr1->cur = putline();
if (addr1<addr2)
rdelete(addr1+1, addr2);
dot = addr1;
}
substitute(inglob)
{
register gsubf, nl;
register LINE a1;
int *markp;
int getsub();
gsubf = compsub();
for (a1 = addr1; a1 <= addr2; a1++) {
if (execute(0, a1)==0)
continue;
inglob |= 01;
dosub();
if (gsubf) {
while (*loc2) {
if (execute(1, (LINE )0)==0)
break;
dosub();
}
}
subnewa = putline();
a1->cur &= ~01;
if (anymarks) {
for (markp = names; markp < &names[26]; markp++)
if (*markp == a1->cur)
*markp = subnewa;
}
a1->cur = subnewa;
append(getsub, a1);
nl = nline;
a1 += nl;
addr2 += nl;
}
if (inglob==0)
error(35);
}
compsub()
{
register seof, c;
register char *p;
static char remem[LBSIZE]={-1};
if ((seof = getchr()) == '\n' || seof == ' ')
error(36);
compile((char *) 0, expbuf, &expbuf[ESIZE], seof);
p = rhsbuf;
for (;;) {
c = getchr();
if (c=='\\')
c = getchr() | 0200;
if (c=='\n') {
if (nodelim == 1) {
nodelim = 0;
error(36);
}
if (globp && globp[0])
c |= 0200; /* insert '\' */
else {
UNGETC(c);
pflag++;
break;
}
}
if (c==seof)
break;
*p++ = c;
if (p >= &rhsbuf[LBSIZE])
error(38);
}
*p++ = 0;
if(rhsbuf[0] == '%' && rhsbuf[1] == 0)
(remem[0]!=-1) ? strcpy(rhsbuf, remem) : error(55);
else
strcpy(remem, rhsbuf);
if ((peekc = getchr()) == 'g') {
peekc = 0;
newline();
return(1);
}
newline();
return(0);
}
getsub()
{
register char *p1, *p2;
p1 = linebuf;
if ((p2 = linebp) == 0)
return(EOF);
while (*p1++ = *p2++);
linebp = 0;
return(0);
}
dosub()
{
register char *lp, *sp, *rp;
int c;
lp = linebuf;
sp = genbuf;
rp = rhsbuf;
while (lp < loc1)
*sp++ = *lp++;
while (c = *rp++&0377) {
if (c=='&') {
sp = place(sp, loc1, loc2);
continue;
} else if(c & 0200) {
c &= 0177;
if(c >= '1' && c < nbra + '1') {
sp = place(sp, braslist[c-'1'], braelist[c-'1']);
continue;
}
}
*sp++ = c;
if (sp >= &genbuf[LBSIZE])
error(27);
}
lp = loc2;
loc2 = sp - genbuf + linebuf;
while (*sp++ = *lp++)
if (sp >= &genbuf[LBSIZE])
error(27);
lp = linebuf;
sp = genbuf;
while (*lp++ = *sp++);
}
char *
place(sp, l1, l2)
register char *sp, *l1, *l2;
{
while (l1 < l2) {
*sp++ = *l1++;
if (sp >= &genbuf[LBSIZE])
error(27);
}
return(sp);
}
move(cflag)
{
register LINE adt, ad1, ad2;
int getcopy();
setdot();
nonzero();
if ((adt = address())==0)
error(39);
newline();
if (!globflg) save();
if (cflag) {
ad1 = dol;
append(getcopy, ad1++);
ad2 = dol;
} else {
ad2 = addr2;
for (ad1 = addr1; ad1 <= ad2;)
ad1++->cur &= ~01;
ad1 = addr1;
}
ad2++;
if (adt<ad1) {
dot = adt + (ad2-ad1);
if ((++adt)==ad1)
return;
reverse(adt, ad1);
reverse(ad1, ad2);
reverse(adt, ad2);
} else if (adt >= ad2) {
dot = adt++;
reverse(ad1, ad2);
reverse(ad2, adt);
reverse(ad1, adt);
} else
error(39);
fchange = 1;
}
reverse(a1, a2)
register LINE a1, a2;
{
register int t;
for (;;) {
t = (--a2)->cur;
if (a2 <= a1)
return;
a2->cur = a1->cur;
a1++->cur = t;
}
}
getcopy()
{
if (addr1 > addr2)
return(EOF);
getline(addr1++->cur);
return(0);
}
error1(code)
{
expbuf[0] = 0;
nbra = 0;
error(code);
}
execute(gf, addr)
LINE addr;
{
register char *p1, *p2, c;
for (c=0; c<NBRA; c++) {
braslist[c] = 0;
braelist[c] = 0;
}
if (gf) {
if (circf)
return(0);
p1 = linebuf;
p2 = genbuf;
while (*p1++ = *p2++);
locs = p1 = loc2;
} else {
if (addr==zero)
return(0);
p1 = getline(addr->cur);
locs = 0;
}
return(step(p1, expbuf));
}
putd()
{
register r;
r = (int)(count%10);
count /= 10;
if (count)
putd();
putchr(r + '0');
}
puts(sp)
register char *sp;
{
int sz,i;
if (fss.Ffill && (listf == 0)) {
if ((i = expnd(sp,funny,&sz,&fss)) == -1) {
write(1,funny,fss.Flim & 0377); putchr('\n');
write(1,"too long",8);
}
else
write(1,funny,sz);
putchr('\n');
if (i == -2) write(1,"tab count\n",10);
return(0);
}
col = 0;
while (*sp)
putchr(*sp++);
putchr('\n');
}
char line[70];
char *linp = line;
putchr(ac)
{
register char *lp;
register c;
short len;
lp = linp;
c = ac;
if ( listf ) {
col++;
if (col >= 72) {
col = 0;
*lp++ = '\\';
*lp++ = '\n';
}
if (c=='\t') {
c = '>';
goto esc;
}
if (c=='\b') {
c = '<';
esc:
*lp++ = '-';
*lp++ = '\b';
*lp++ = c;
goto out;
}
if (c<' ' && c!= '\n') {
*lp++ = '\\';
*lp++ = (c>>3)+'0';
*lp++ = (c&07)+'0';
col += 2;
goto out;
}
}
*lp++ = c;
out:
if(c == '\n' || lp >= &line[64]) {
linp = line;
len = lp - line;
if(yflag & 01)
write(1, &len, sizeof(len));
write(1, line, len);
return;
}
linp = lp;
}
globaln(k)
{
register char *gp;
register c;
register LINE a1;
int nfirst;
char globuf[GBSIZE];
if (yflag)
error(59);
if (globp)
error(33);
setall();
nonzero();
if ((c=getchr())=='\n')
error(19);
save();
compile((char *) 0, expbuf, &expbuf[ESIZE], c);
for (a1=zero; a1<=dol; a1++) {
a1->cur &= ~01;
if (a1>=addr1 && a1<=addr2 && execute(0, a1)==k)
a1->cur |= 01;
}
nfirst = 0;
newline();
for (a1=zero; a1<=dol; a1++) {
if (a1->cur & 01) {
a1->cur &= ~01;
dot = a1;
puts(getline(a1->cur));
if ((c=getchr()) == EOF)
error(52);
if(c=='a' || c=='i' || c=='c')
error(53);
c &= 0177;
if (c == '\n') {
a1 = zero;
continue;
}
if (c != '&') {
gp = globuf;
*gp++ = c;
while ((c = getchr()) != '\n') {
if (c=='\\') {
c = getchr();
if (c!='\n')
*gp++ = '\\';
}
*gp++ = c;
if (gp >= &globuf[GBSIZE-2])
error(34);
}
*gp++ = '\n';
*gp++ = 0;
nfirst = 1;
}
else
if ((c=getchr()) != '\n')
error(54);
globp = globuf;
if (nfirst) {
globflg = 1;
commands();
globflg = 0;
}
else error(56);
globp = 0;
a1 = zero;
}
}
}
eopen(string, rw)
char *string;
{
#define w_or_r(a,b) (rw?a:b)
int pf[2];
int i;
int io;
int chcount; /* # of char read. */
int crflag;
char *fp;
crflag = 0; /* Is file encrypted flag; 1=yes. */
if (rflg) { /* restricted shell */
if (Xqt) {
Xqt = 0;
error(6);
}
}
if(!Xqt) {
if((io=open(string, rw)) >= 0) {
if (fflg) {
chcount = read(io,funny,LBSIZE);
/* Verify that line just read IS an encrypted file. */
fp = funny; /* Set fp to start of buffer. */
while(fp < &funny[chcount])
if(*fp++ & 0200)crflag = 1;
#ifdef FSPEC
if (fspec(funny,&fss,0) < 0) {
fss.Ffill = 0;
fflg = 0;
error(4);
}
#endif FSPEC
lseek(io,0L,0);
}
}
fflg = 0;
return(io);
}
if(pipe(pf) < 0)
xerr: error(0);
if((i = fork()) == 0) {
signal(SIGHUP, oldhup);
signal(SIGQUIT, oldquit);
signal(SIGPIPE, oldpipe);
signal(SIGINT, (int (*)()) 0);
close(w_or_r(pf[1], pf[0]));
close(w_or_r(0, 1));
dup(w_or_r(pf[0], pf[1]));
close(w_or_r(pf[0], pf[1]));
execlp("/bin/sh", "sh", "-c", string, (char *) 0);
exit(1);
}
if(i == -1)
goto xerr;
close(w_or_r(pf[0], pf[1]));
return w_or_r(pf[1], pf[0]);
}
eclose(f)
{
close(f);
if(Xqt)
Xqt = 0, wait((int *) 0);
}
mkfunny()
{
register char *p, *p1, *p2;
p2 = p1 = funny;
p = efile;
while(*p)
p++;
while(*--p == '/') /* delete trailing slashes */
*p = '\0';
p = efile;
while (*p1++ = *p)
if (*p++ == '/') p2 = p1;
p1 = &tfname[6];
*p2 = '\007'; /* add unprintable char to make funny a unique name */
while (p1 <= &tfname[11])
*++p2 = *p1++;
}
getime() /* get modified time of file and save */
{
if (stat(efile,&Fl) < 0)
savtime = 0;
else
savtime = Fl.st_mtime;
}
chktime() /* check saved mod time against current mod time */
{
if (savtime != 0 && Fl.st_mtime != 0) {
if (savtime != Fl.st_mtime)
error(58);
}
}
newtime() /* get new mod time and save */
{
stat(efile,&Fl);
savtime = Fl.st_mtime;
}
red(op) /* restricted - check for '/' in name */
/* and delete trailing '/' */
char *op;
{
register char *p;
p = op;
while(*p)
if(*p++ == '/'&& rflg) {
*op = 0;
error(6);
}
/* delete trailing '/' */
while(p > op) {
if (*--p == '/')
*p = '\0';
else break;
}
}
char *fsp, fsprtn;
#ifdef FSPEC
fspec(line,f,up)
char line[];
struct Fspec *f;
int up;
{
struct sgttyb arg;
register int havespec, n;
if(!up) clear(f);
havespec = fsprtn = 0;
for(fsp=line; *fsp && *fsp != '\n'; fsp++)
switch(*fsp) {
case '<': if(havespec) return(-1);
if(*(fsp+1) == ':') {
havespec = 1;
clear(f);
/* if(!ioctl(1, TCGETA, &arg) && ((arg.c_oflag&TAB3) == TAB3)) SYSTEM V ioctl */
if(!ioctl(1, TIOCGETP, &arg) &&
((arg.sg_flags&XTABS) == XTABS))
f->Ffill = 1;
fsp++;
continue;
}
case ' ': continue;
case 's': if(havespec && (n=numb()) >= 0)
f->Flim = n;
continue;
case 't': if(havespec) targ(f);
continue;
case 'd': continue;
case 'm': if(havespec) n = numb();
continue;
case 'e': continue;
case ':': if(!havespec) continue;
if(*(fsp+1) != '>') fsprtn = -1;
return(fsprtn);
default: if(!havespec) continue;
return(-1);
}
return(1);
}
numb()
{
register int n;
n = 0;
while(*++fsp >= '0' && *fsp <= '9')
n = 10*n + *fsp-'0';
fsp--;
return(n);
}
targ(f)
struct Fspec *f;
{
if(*++fsp == '-') {
if(*(fsp+1) >= '0' && *(fsp+1) <= '9') tincr(numb(),f);
else tstd(f);
return;
}
if(*fsp >= '0' && *fsp <= '9') {
tlist(f);
return;
}
fsprtn = -1;
fsp--;
return;
}
tincr(n,f)
int n;
struct Fspec *f;
{
register int l, i;
l = 1;
for(i=0; i<20; i++)
f->Ftabs[i] = l += n;
f->Ftabs[i] = 0;
}
tstd(f)
struct Fspec *f;
{
char std[3];
std[0] = *++fsp;
if (*(fsp+1) >= '0' && *(fsp+1) <= '9') {
std[1] = *++fsp;
std[2] = '\0';
}
else std[1] = '\0';
fsprtn = stdtab(std,f->Ftabs);
return;
}
tlist(f)
struct Fspec *f;
{
register int n, last, i;
fsp--;
last = i = 0;
do {
if((n=numb()) <= last || i >= 20) {
fsprtn = -1;
return;
}
f->Ftabs[i++] = last = n;
} while(*++fsp == ',');
f->Ftabs[i] = 0;
fsp--;
}
#endif FSPEC
expnd(line,buf,sz,f)
char line[], buf[];
int *sz;
struct Fspec *f;
{
register char *l, *t;
register int b;
l = line - 1;
b = 1;
t = f->Ftabs;
fsprtn = 0;
while(*++l && *l != '\n' && b < 511) {
if(*l == '\t') {
while(*t && b >= *t) t++;
if (*t == 0) fsprtn = -2;
do buf[b-1] = ' '; while(++b < *t);
}
else buf[b++ - 1] = *l;
}
buf[b] = '\0';
*sz = b;
if(*l != '\0' && *l != '\n') {
buf[b-1] = '\n';
return(-1);
}
buf[b-1] = *l;
if(f->Flim && b-1 > f->Flim) return(-1);
return(fsprtn);
}
clear(f)
struct Fspec *f;
{
f->Ftabs[0] = f->Fdel = f->Fmov = f->Ffill = 0;
f->Flim = 0;
}
lenchk(line,f)
char line[];
struct Fspec *f;
{
register char *l, *t;
register int b;
l = line - 1;
b = 1;
t = f->Ftabs;
while(*++l && *l != '\n' && b < 511) {
if(*l == '\t') {
while(*t && b >= *t) t++;
while(++b < *t);
}
else b++;
}
if((*l!='\0'&&*l!='\n') || (f->Flim&&b-1>f->Flim))
return(-1);
return(0);
}
#ifdef FSPEC
#define NTABS 21
/* stdtabs: standard tabs table
format: option code letter(s), null, tabs, null */
char stdtabs[] = {
'a', 0,1,10,16,36,72,0, /* IBM 370 Assembler */
'a','2',0,1,10,16,40,72,0, /* IBM Assembler alternative*/
'c', 0,1,8,12,16,20,55,0, /* COBOL, normal */
'c','2',0,1,6,10,14,49,0, /* COBOL, crunched*/
'c','3',0,1,6,10,14,18,22,26,30,34,38,42,46,50,54,58,62,67,0,
'f', 0,1,7,11,15,19,23,0, /* FORTRAN */
'p', 0,1,5,9,13,17,21,25,29,33,37,41,45,49,53,57,61,0, /* PL/I */
's', 0,1,10,55,0, /* SNOBOL */
'u', 0,1,12,20,44,0, /* UNIVAC ASM */
0};
/* stdtab: return tab list for any "canned" tab option.
entry: option points to null-terminated option string
tabvect points to vector to be filled in
exit: return(0) if legal, tabvect filled, ending with zero
return(-1) if unknown option
*/
stdtab(option,tabvect)
char option[], tabvect[NTABS];
{
char *scan;
tabvect[0] = 0;
scan = stdtabs;
while (*scan) {
if (strequal(&scan,option))
{strcopy(scan,tabvect);break;}
else while(*scan++); /* skip over tab specs */
}
/* later: look up code in /etc/something */
return(tabvect[0]?0:-1);
}
#endif FSPEC
/* strequal: checks strings for equality
entry: scan1 points to scan pointer, str points to string
exit: return(1) if equal, return(0) if not
*scan1 is advanced to next nonzero byte after null
*/
strequal(scan1,str)
char **scan1, *str;
{
char c, *scan;
scan = *scan1;
while ((c = *scan++) == *str && c) str++;
*scan1 = scan;
if (c == 0 && *str == 0) return(1);
if (c) while(*scan++);
*scan1 = scan;
return(0);
}
/* strcopy: copy source to destination */
strcopy(source,dest)
char *source, *dest;
{
while (*dest++ = *source++);
return;
}
/* This is called before a buffer modifying command so that the */
/* current array of line ptrs is saved in sav and dot and dol are saved */
save() {
LINE i;
savdot = dot;
savdol = dol;
for (i=zero+1; i<=dol; i++)
i->sav = i->cur;
initflg = 0;
}
/* The undo command calls this to restore the previous ptr array sav */
/* and swap with cur - dot and dol are swapped also. This allows user to */
/* undo an undo */
undo() {
int tmp;
LINE i, tmpdot, tmpdol;
tmpdot = dot; dot = savdot; savdot = tmpdot;
tmpdol = dol; dol = savdol; savdol = tmpdol;
/* swap arrays using the greater of dol or savdol as upper limit */
for (i=zero+1; i<=((dol>savdol) ? dol : savdol); i++) {
tmp = i->cur;
i->cur = i->sav;
i->sav = tmp;
}
}
/* Slink() -
* relace file thru symbolic link.
*
*/
Slink( old, new, st )
char *old;
char *new;
struct stat *st;
{
char buf[BUFSIZ];
int oldfd, newfd;
int cc, n;
int total = 0;
/* open new file */
if( (newfd =
open(new, O_CREAT|O_TRUNC|O_WRONLY, st->st_mode&~S_IFMT)) < 0 )
return(-1);
if( (oldfd = open( old, O_RDONLY, 0 )) < 0 )
return(-1);
while( (n = read( oldfd, buf, BUFSIZ )) > 0 )
{
total += n;
if( write( newfd, buf, n ) != n )
error(29);
}
(void) close( oldfd );
(void) close( newfd );
chown( new, st->st_uid, st->st_gid );
chmod( new, st->st_mode&~S_IFMT );
return(0);
}