USG_PG3/usr/source/sccscmds/delta.c
#include "../sccshead/sfile.h"
#include "../sccshead/statbuf.h"
#include "../sccshead/sint.h"
# include "../sccshead/had.h"
# define debug if(DEBUG) printf
int DEBUG 0;
char delta____[] "@(#)delta.c 3.13";
int num_files;
char had[26];
long cutoff 0X7FFFFFFFL; /* max positive long */
int verbosity;
int heur 3;
int lookahd 50;
char *why;
struct Deltab ndt;
main(argc,argv)
int argc;
char *argv[];
{
register int i;
register char *p;
char c;
int testmore;
int needy;
int intrp;
extern delta();
extern char *acklist;
needy = 0;
for(i=1; i<argc; i++)
if(argv[i][0] == '-' && (c=argv[i][1])) {
p = &argv[i][2];
testmore = 0;
switch (c) {
case 'a':
acklist = p;
break;
case 'h':
if ((heur=patoi(p)) < 0 || heur > 3)
fatal("bad heuristic level (7)");
break;
case 'y':
why = p;
break;
case 'l':
if ((lookahd=patoi(p)) < 1)
fatal("bad lookahead (8)");
break;
case 'n':
case 's':
testmore++;
break;
default:
fatal("unknown key letter (69)");
}
if (testmore) {
testmore = 0;
if (*p)
fatal(stringf(
"value after %c arg (232)",c));
}
if (had[c - 'a']++)
fatal("key letter twice (464)");
argv[i] = 0;
}
else {
if (argv[i][0] == '-')
needy++;
num_files++;
}
if(num_files == 0) fatal("missing file arg (49)");
if (needy && !HADY)
fatal("missing history (326)");
if (!HADS)
verbosity = WARNING;
dohist(&ndt,why);
if ((intrp=signal(2,1))==1)
signal(1,1);
else
signal(2,intrp);
setsig();
for (i=1; i<argc; i++)
if (p=argv[i])
do_file(p,delta);
exit(0);
}
struct Ln {
char *Ltext;
int Llnno;
int Llen;
};
struct Info {
char Iwhich;
char Ieof;
int Ilookahead;
int Ilast;
char *Iinput;
int Itop;
struct Ln Ilines[1];
};
int inserted, deleted, unchanged;
struct Packet gpkt;
struct Ibufr bufb;
struct Statbuf sb;
/* `Main' routine.
* Sets everything up for `diff()' which does the real work.
*/
char *oldrec;
int oldlnno;
int curlnno;
int noteach;
int did_id;
struct Packet wpacket;
int wopen, weof;
delta(dfile)
char *dfile;
{
char gfile[SIZEOFPfile];
register char *pfile;
extern int had_dir, had_standinp;
sinit(&gpkt,dfile,1);
dohead(&gpkt);
copy(auxf(&gpkt,'g'),gfile);
did_id = oldrec = oldlnno = curlnno = noteach = wopen = weof = 0;
inserted = deleted = unchanged = 0;
if (verbosity && (num_files > 1 || had_dir || had_standinp))
printf("\n%s:\n",gpkt.Pfile);
if (diff(gpkt.Pfile,gfile,lookahd)) {
rename(auxf(&gpkt,'x'),gpkt.Pfile);
xrm(&gpkt);
remove(auxf(&gpkt,'p'));
if (!HADN) {
setuid(getuid()&0377);
remove(gfile);
}
}
if (!did_id && verbosity)
msg2("No id keywords (305)\n");
xrm(&gpkt);
freeall();
}
/* Init sets up the `Info' structures for the two files,
* calls opena() or openb(), and reads in the first window
* for the given file.
*/
init(f,file,lookahead,which)
struct Info **f;
char file[], which;
int lookahead;
{
register struct Info *p;
register struct Ln *lp;
register int i;
i = (sizeof(*p)-sizeof(*lp)) + lookahead*sizeof(*lp);
debug("(sizeof(*p)-sizeof(*lp))=%d sizeof(*lp)=%d\n",
(sizeof(*p)-sizeof(*lp)),sizeof(*lp));
p = alloc(i,0);
*f = p;
p->Iwhich = which;
p->Ilookahead = lookahead;
p->Itop = 0;
if (which == 'A') opena(p,file);
else openb(p,file);
for(i= -1; ++i<lookahead;) {
lp = &p->Ilines[i];
if(rdline(p,&lp->Ltext,&lp->Llen,&lp->Llnno) == -1) break;
}
p->Ilast = i;
}
/* Opena() does the initial SCCS file stuff.
*/
opena(p,mod)
register struct Info *p;
char mod[];
{
register int f;
char user[8];
p->Iinput = &gpkt;
rdpfile(auxf(&gpkt,'p'),&gpkt.Prel,user);
if (!equal(user,getlnam()))
fatal("you are not `",user,"' (322)");
gpkt.Plev = -1;
gpkt.Pcutoff = cutoff;
doreltab(&gpkt);
permiss(&gpkt);
dodeltab(&gpkt);
}
/* Openb() does the initial text file stuff.
*/
openb(p,file)
struct Info *p;
char file[];
{
p->Iinput = &bufb;
opnl(p->Iinput,file);
}
/* Rdline() reads a line.
* It determines if it should read the SCCS file or the text file.
* It returns -1 on error or (equivalently) EOF,
* and 0 on success.
*/
rdline(p,text,len,lnno)
register struct Info *p;
char **text;
int *len, *lnno;
{
register struct Packet *pkt;
register struct Ibufr *buf;
if (p->Ieof)
return(-1);
if(p->Iwhich == 'A') {
pkt = p->Iinput;
if(readmod(pkt)) {
p->Ieof = 1;
close(pkt->Pibuf.Ifildes);
return(-1);
}
*text = alloc((*len=pkt->Pibuf.Ilen)+1);
move(pkt->Pibuf.Irecptr,*text,*len);
(*text)[*len] = '\0';
*lnno = pkt->Precno;
}
else {
buf = p->Iinput;
if(getl(buf)==1) {
p->Ieof = 1;
close(buf->Ifildes);
return(-1);
}
*text = alloc((*len=buf->Ilen-1)+1);
move(buf->Irecptr,*text,*len);
(*text)[*len] = '\0';
*lnno = ++curlnno;
checkid(*text);
}
return(0);
}
/*
* checkid() checks for "%.%" strings
*/
checkid(line)
{
extern did_id;
register char *p;
if (!did_id)
for (p = line; *p; p++)
if (p[0] == '%' && p[1] != 0 && p[2] == '%') {
did_id = 1;
break;
}
}
/* Wrtctl() writes a control record.
*/
wrtctl(p,ctl)
register struct Info *p;
int ctl;
{
struct Control ctlrec;
if (!wopen) wrtopn(p);
ctlrec.Cctl = ctl;
ctlrec.Crel = (p->Iinput)->Prel;
ctlrec.Clev = (p->Iinput)->Plev + 1;
wrtrec(&wpacket,&ctlrec,SIZEOFCONTROL);
}
/* Wrtopn() opens the SCCS file with the update (Pupd)
* flag set. The x-file is generated by this instance
* of the open SCCS file.
*/
wrtopn(p)
struct Info *p;
{
register struct Packet *p1, *p2;
extern int verbosity;
p1 = &gpkt;
p2 = wopen = &wpacket;
zero(p2,sizeof(*p2));
copy(p1->Pfile,p2->Pfile);
p2->Prel = p1->Prel;
p2->Plev = p1->Plev;
p2->Pupd = 1;
p2->Pcutoff = cutoff;
p2->Pwrttn = 1;
p2->Pverbose = verbosity;
opnr(&p2->Pibuf,p2->Pfile);
dohead(p2);
doreltab(p2);
dodeltab(p2);
}
/* Wrtend() flushes the SCCS file (and the x-file).
*/
wrtend(p)
struct Info *p;
{
if (!wopen) wrtopn(p);
if (weof==0) while(readmod(&wpacket)==0);
close(wpacket.Pibuf.Ifildes);
wrtrec(&wpacket,0,-1);
}
/* Before() rolls the wpacket instance of the SCCS file to
* the record before the record at the top of the window.
*/
before(p)
register struct Info *p;
{
register int n;
if(weof) return;
if (!wopen) wrtopn(p);
if (p->Ilast==0) n = 0;
else n = (&p->Ilines[(p->Itop)%(p->Ilookahead)])->Llnno;
if(wpacket.Precno == n) return;
while(wpacket.Precno != n)
if (weof=readmod(&wpacket)) break;
}
/* After() rolls the wpacket instance of the SCCS file to
* the record after the record at the top of the window.
*/
after(p)
struct Info *p;
{
before(p);
wrtrec(&wpacket,0,0);
}
status()
{
printf("%d unchanged\n%d inserted\n%d deleted\n",
unchanged,inserted,deleted);
}
/* Enter() is called by `dolist()' (which see),
* and is used in processing the a_ argument.
*/
enter(fill1,fill2,r,l)
{
extern int ackrel, acklev, acknowl;
if (ackrel==r && acklev==l) acknowl = 1;
}
/* Clean_up() is called by fatal(), which might be called
* because of a signal being caught.
*/
clean_up()
{
xrm(&gpkt);
}
/* Mkdeltab() builds a new delta table entry.
*/
mkdeltab(pkt)
struct Packet *pkt;
{
int level;
extern long timenow;
if (pkt->Pnoprop) {
ndt.Dtype = 'U';
if (pkt->Pverbose&WARNING)
msg2("Delta will not propagate (324)\n");
}
else ndt.Dtype = 'D';
ndt.Drel = pkt->Prel;
ndt.Dlev = level = (pkt->Plev+1);
ndt.Dfill = ' ';
ndt.Ddatetime = timenow;
copy(getlnam(),ndt.Dpgmr);
wrtrec(pkt,&ndt,size(ndt.Dhist)+sizeof(ndt)-sizeof(ndt.Dhist));
if (pkt->Pverbose&WARNING)
printf("%d.%d\n",pkt->Prel,level);
}
/* Now comes the hard stuff!
* Read this code very slowly and very carefully.
*/
diff(filea,fileb,lookahead)
char filea[], fileb[];
int lookahead;
{
struct Info *ainfo, *binfo;
int a[2], b[2], deg[2], len;
register int pick;
init(&ainfo,filea,lookahead,'A');
init(&binfo,fileb,lookahead,'B');
while(ainfo->Ilast && binfo->Ilast) {
match(ainfo,binfo,ainfo->Ilast,1,a,b,deg,0);
match(binfo,ainfo,b[0]-1,a[0]+1,b,a,deg,1);
pick = choose(ainfo,binfo,a,b,deg);
insert(ainfo,binfo,b[pick]-1);
delete(ainfo,binfo,a[pick]-1);
unchanged =+ deg[pick];
while(deg[pick]--) {
take(ainfo,&len);
take(binfo,&len);
}
while(ainfo->Ilast && binfo->Ilast) {
if (equal(
ainfo->Ilines[ainfo->Itop%ainfo->Ilookahead].Ltext,
binfo->Ilines[binfo->Itop%binfo->Ilookahead].Ltext)) {
take(ainfo,&len);
take(binfo,&len);
unchanged++;
}
else break;
}
}
while(binfo->Ilast) insert(ainfo,binfo,binfo->Ilast);
while(ainfo->Ilast) delete(ainfo,binfo,ainfo->Ilast);
wrtend(ainfo);
if (!HADS)
status();
if (!HADH) return(1);
return(update()=='y');
}
match(f1,f2,stop1,start2,mark1,mark2,deg,sub)
register struct Info *f1, *f2;
int stop1, start2, mark1[2], mark2[2], deg[2], sub;
{
register int i1;
int i2, found;
found = 0;
for(i1=0; ++i1<=stop1;) {
for(i2=start2-1; ++i2<=f2->Ilast;) if (equal(
f1->Ilines[(f1->Itop+i1-1)%(f1->Ilookahead)].Ltext,
f2->Ilines[(f2->Itop+i2-1)%(f2->Ilookahead)].Ltext)){
found = 1;
break;
}
if(found) break;
}
if(!found) {
mark1[sub] = 0;
mark2[sub] = f2->Ilast+1;
deg[sub] = 0;
return;
}
mark1[sub] = i1;
mark2[sub] = i2;
deg[sub] = 1;
while(i1++ < f1->Ilast && i2++ < f2->Ilast &&
equal(f1->Ilines[(f1->Itop+i1-1)%(f1->Ilookahead)].Ltext,
f2->Ilines[(f2->Itop+i2-1)%(f2->Ilookahead)].Ltext))
deg[sub]++;
return;
}
insert(f1,f2,mark2)
struct Info *f1, *f2;
register int mark2;
{
register struct Control *rec;
int len;
if(mark2 < 1) return;
inserted =+ mark2;
before(f1);
wrtctl(f1,INS);
while(mark2--) {
rec = take(f2,&len);
if (ctlrec(rec,len))
fatal(stringf("illegal data on line %d (312)",
oldlnno));
wrtrec(&wpacket,rec,len);
}
wrtctl(f1,END);
}
delete(f1,f2,mark1)
struct Info *f1, *f2;
register int mark1;
{
int len;
if(mark1 < 1) return;
deleted =+ mark1;
before(f1);
wrtctl(f1,DEL);
while(--mark1) take(f1,&len);
after(f1);
take(f1,&len);
wrtctl(f1,END);
}
take(p,len)
register struct Info *p;
int *len;
{
register struct Ln *lp;
if(oldrec) free(oldrec);
lp = &p->Ilines[p->Itop];
oldrec = lp->Ltext;
oldlnno = lp->Llnno;
*len = lp->Llen;
if(rdline(p,&lp->Ltext,&lp->Llen,&lp->Llnno) == -1) p->Ilast--;
if(++p->Itop == p->Ilookahead) p->Itop = 0;
return(oldrec);
}
numsame(f1,f2,start1,start2)
register struct Info *f1, *f2;
int start1, start2;
{
register int i1;
int i2, num;
num = 0;
for(i1=start1+1; i1<=f1->Ilast; i1++)
for(i2=start2+1; i2<=f2->Ilast; i2++) if (equal(
f1->Ilines[(f1->Itop+i1-1)%(f1->Ilookahead)].Ltext,
f2->Ilines[(f2->Itop+i2-1)%(f2->Ilookahead)].Ltext)) {
num =+ f1->Ilines[(f1->Itop+i1-1)%
(f1->Ilookahead)].Llen;
break;
}
return(num);
}
double reldiff(a,b)
double a, b;
{
double x;
if((1e-37 > -b) && (b < 1e-37)) return(1e37);
x = (a - b)/b;
if(x < 0) return(-x);
return(x);
}
double W 1.;
double X 2.;
double Y 4.;
double Z 8.;
choose(ainfo,binfo,a,b,deg)
register struct Info *ainfo, *binfo;
int a[2], b[2], deg[2];
{
register int i;
int k, chars, avgline, nsame;
double val[2];
if(heur == 0) return(ask(ainfo,binfo,a,b));
for(k=0; k<=1; k++) {
chars = 0;
for(i=a[k]; i>0 && i<a[k]+deg[k]; i++)
chars =+ ainfo->Ilines[(ainfo->Itop+i-1)%
(ainfo->Ilookahead)].Llen;
avgline = (a[k]+b[k])/2;
nsame=numsame(ainfo,binfo,a[k],b[k]);
val[k] = -W*avgline + X*deg[k] + Y*chars + Z*nsame;
}
if(reldiff(val[0],val[1]) < .2) return(ask(ainfo,binfo,a,b));
if(heur==1 && b[0]!=1 && ((a[1]-a[0])>3 || (b[0]-b[1])>3))
return(ask(ainfo,binfo,a,b));
if(val[0] > val[1]) return(0);
if(val[0] < val[1]) return(1);
}
/* If you've made it to here, congratulations!
* The rest is easy.
*
* Ask() returns zero if lines were inserted,
* non-zero if lines were deleted.
*/
ask(f1,f2,a,b)
struct Info *f1, *f2;
int a[2], b[2];
{
if(heur == 3) return(0);
while(1) {
printf("%d inserted:\n",b[0]-(b[1]?b[1]:1));
if(answer(f2,b[1]) == 'y') return(0);
printf("%d deleted:\n",a[1]-(a[0]?a[0]:1));
if(answer(f1,a[0]) == 'y') return(1);
printf("try again...\n");
}
}
/* Answer() solicits a response from the user
* and prints the next line in the buffer if the response
* was only a new-line.
* It returns the response (which must be either `y' or `n').
*/
answer(p,ln)
struct Info *p;
int ln;
{
char resp;
if(!ln) ln++;
printf(" %s\n",
p->Ilines[(p->Itop+(ln++)-1)%(p->Ilookahead)].Ltext);
if(!(noteach++)) printf("(y,n,s,CR)");
printf("? ");
while((resp=getresp("(y,n,s,CR)?")) == '\n') {
if(ln > p->Ilast) printf("end of buffer\n?");
else printf(" %s\n?",
p->Ilines[(p->Itop+(ln++)-1)%(p->Ilookahead)].Ltext);
}
return(resp);
}
/* Getresp() gets a response from the user about `lesson'.
* It processes all responses other than `y', `n', and
* new-line.
*/
getresp(lesson)
char lesson[];
{
char resp, nextc;
while(1) {
resp = getchr();
if(resp == '\n') return('\n');
if((nextc=getchr()) != '\n') while(getchr() != '\n');
if(resp == 's' && nextc == '\n') {
status();
printf("? ");
continue;
}
if((resp != 'y' && resp != 'n') || nextc != '\n') {
printf(lesson);
continue;
}
return(resp);
}
}
/* Update() solicits a `y'/`n' response from the user.
*/
update()
{
char resp;
printf("update");
if(!(noteach++)) printf("(y,n)");
do {
printf("? ");
resp=getresp("(y,n)?");
} while(resp == '\n');
return(resp);
}