653 lines
13 KiB
C
653 lines
13 KiB
C
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
#ident "@(#)bdiff:bdiff.c 5.11"
|
|
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#define ONSIG 16
|
|
|
|
#if sgi
|
|
/*
|
|
* Taken stright from <fatal.h>
|
|
* Since <fatal.h> is in the SCCS directory this was
|
|
* easier and cleaner.
|
|
*/
|
|
# define FTLMSG 0100000
|
|
# define FTLCLN 040000
|
|
# define FTLEXIT 01
|
|
#endif
|
|
|
|
char *satoi();
|
|
char *Mesg[ONSIG]={
|
|
0,
|
|
0, /* Hangup */
|
|
0, /* Interrupt */
|
|
0, /* Quit */
|
|
"Illegal instruction",
|
|
"Trace/BPT trap",
|
|
"IOT trap",
|
|
"EMT trap",
|
|
"Floating exception",
|
|
"Killed",
|
|
"Bus error",
|
|
"Memory fault",
|
|
"Bad system call",
|
|
"Broken pipe",
|
|
"Alarm clock"
|
|
};
|
|
|
|
/*
|
|
This program segments two files into pieces of <= seglim lines
|
|
(which is passed as a third argument or defaulted to some number)
|
|
and then executes diff upon the pieces. The output of
|
|
'diff' is then processed to make it look as if 'diff' had
|
|
processed the files whole. The reason for all this is that seglim
|
|
is a reasonable upper limit on the size of files that diff can
|
|
process.
|
|
NOTE -- by segmenting the files in this manner, it cannot be
|
|
guaranteed that the 'diffing' of the segments will generate
|
|
a minimal set of differences.
|
|
This process is most definitely not equivalent to 'diffing'
|
|
the files whole, assuming 'diff' could handle such large files.
|
|
|
|
'diff' is executed by a child process, generated by forking,
|
|
and communicates with this program through pipes.
|
|
*/
|
|
|
|
char Error[128];
|
|
|
|
int seglim; /* limit of size of file segment to be generated */
|
|
|
|
FILE *fdopen();
|
|
char diff[] = "diff";
|
|
char tempskel[] = "/tmp/bdXXXXX"; /* used to generate temp file names */
|
|
char tempfile[32];
|
|
char otmp[32], ntmp[32];
|
|
char *Ffile;
|
|
int Fflags, olinenum, nlinenum;
|
|
|
|
char *prognam;
|
|
|
|
main(argc,argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
FILE *poldfile, *pnewfile, *maket(), *carryon();
|
|
char oline[BUFSIZ], nline[BUFSIZ], diffline[BUFSIZ];
|
|
char *olp, *nlp, *dp;
|
|
int otcnt, ntcnt;
|
|
pid_t i;
|
|
int pfd[2];
|
|
FILE *poldtemp, *pnewtemp, *pipeinp;
|
|
int status;
|
|
char type;
|
|
|
|
prognam = argv[0];
|
|
/*
|
|
Set flags for 'fatal' so that it will clean up,
|
|
produce a message, and terminate.
|
|
*/
|
|
Fflags = FTLMSG | FTLCLN | FTLEXIT;
|
|
|
|
setsig();
|
|
|
|
if (argc < 3 || argc > 5)
|
|
fatal("arg count");
|
|
|
|
if (strcmp(argv[1],"-") == 0 && strcmp(argv[2],"-") == 0)
|
|
fatal("both files standard input");
|
|
if (strcmp(argv[1],"-") == 0)
|
|
poldfile = stdin;
|
|
else
|
|
if((poldfile = fopen(argv[1],"r")) == NULL) {
|
|
sprintf(Error, "Can not open '%s'", argv[1]);
|
|
fatal(Error);
|
|
}
|
|
if (strcmp(argv[2],"-") == 0)
|
|
pnewfile = stdin;
|
|
else
|
|
if((pnewfile = fopen(argv[2], "r")) == NULL) {
|
|
sprintf(Error, "Can not open '%s'", argv[2]);
|
|
fatal(Error);
|
|
}
|
|
|
|
seglim = 3500;
|
|
|
|
if (argc > 3) {
|
|
if (argv[3][0] == '-' && argv[3][1] == 's')
|
|
Fflags &= ~FTLMSG;
|
|
else {
|
|
if ((seglim = atoi(argv[3])) == 0)
|
|
fatal("non-numeric limit");
|
|
if (argc == 5 && argv[4][0] == '-' &&
|
|
argv[4][1] == 's')
|
|
Fflags &= ~FTLMSG;
|
|
}
|
|
}
|
|
|
|
olinenum = nlinenum = 0;
|
|
|
|
/*
|
|
The following while-loop will prevent any lines
|
|
common to the beginning of both files from being
|
|
sent to 'diff'. Since the running time of 'diff' is
|
|
non-linear, this will help improve performance.
|
|
If, during this process, both files reach EOF, then
|
|
the files are equal and the program will terminate.
|
|
If either file reaches EOF before the other, the
|
|
program will generate the appropriate 'diff' output
|
|
itself, since this can be easily determined and will
|
|
avoid executing 'diff' completely.
|
|
*/
|
|
while (1) {
|
|
olp = fgets(oline,BUFSIZ,poldfile);
|
|
nlp = fgets(nline,BUFSIZ,pnewfile);
|
|
|
|
if (!olp && !nlp) /* files are e == 0qual */
|
|
exit(0);
|
|
|
|
if (!olp) {
|
|
/*
|
|
The entire old file is a prefix of the
|
|
new file. Generate the appropriate "append"
|
|
'diff'-like output, which is of the form:
|
|
nan,n
|
|
where 'n' represents a line-number.
|
|
*/
|
|
addgen(nline,pnewfile, 0, NULL, NULL);
|
|
}
|
|
|
|
if (!nlp) {
|
|
/*
|
|
The entire new file is a prefix of the
|
|
old file. Generate the appropriate "delete"
|
|
'diff'-like output, which is of the form:
|
|
n,ndn
|
|
where 'n' represents a line-number.
|
|
*/
|
|
delgen(oline,poldfile, 0, NULL, NULL);
|
|
}
|
|
|
|
if (strcmp(olp,nlp) == 0) {
|
|
olinenum++;
|
|
nlinenum++;
|
|
} else
|
|
break;
|
|
}
|
|
|
|
/*
|
|
Here, first 'olinenum' lines are equal.
|
|
The following while-loop segments both files into
|
|
seglim segments, forks and executes 'diff' on the
|
|
segments, and processes the resulting output of
|
|
'diff', which is read from a pipe.
|
|
*/
|
|
otcnt = ntcnt = 0;
|
|
while (1) {
|
|
/*
|
|
If both files are at EOF, everything is done.
|
|
*/
|
|
if (!olp && !otcnt && !nlp && !ntcnt) /* finished */
|
|
exit(0);
|
|
|
|
if (!olp && !otcnt) {
|
|
/*
|
|
Generate appropriate "append"
|
|
output without executing 'diff'.
|
|
*/
|
|
addgen(nline,pnewfile, ntcnt, pnewtemp, ntmp);
|
|
}
|
|
|
|
if (!nlp && !ntcnt) {
|
|
/*
|
|
Generate appropriate "delete"
|
|
output without executing 'diff'.
|
|
*/
|
|
delgen(oline,poldfile, otcnt, poldtemp, otmp);
|
|
}
|
|
|
|
/*
|
|
If no line is carried on from the previous segment,
|
|
create a temporary file to hold a segment
|
|
from the old file, and write it.
|
|
*/
|
|
if (otcnt == 0) {
|
|
poldtemp = maket(otmp);
|
|
otcnt = 0;
|
|
}
|
|
while(olp && otcnt < seglim) {
|
|
fputs(oline,poldtemp);
|
|
if (ferror(poldtemp) != 0) {
|
|
Fflags |= FTLMSG;
|
|
fatal("Can not write to temporary file");
|
|
}
|
|
olp = fgets(oline,BUFSIZ,poldfile);
|
|
otcnt++;
|
|
}
|
|
fclose(poldtemp);
|
|
|
|
/*
|
|
If no line is carried on from the previous segment,
|
|
create a temporary file to hold a segment
|
|
from the new file, and write it.
|
|
*/
|
|
if (ntcnt == 0) {
|
|
pnewtemp = maket(ntmp);
|
|
ntcnt = 0;
|
|
}
|
|
while(nlp && ntcnt < seglim) {
|
|
fputs(nline,pnewtemp);
|
|
if (ferror(pnewtemp) != 0) {
|
|
Fflags |= FTLMSG;
|
|
fatal("Can not write to temporary file");
|
|
}
|
|
nlp = fgets(nline,BUFSIZ,pnewfile);
|
|
ntcnt++;
|
|
}
|
|
fclose(pnewtemp);
|
|
|
|
/*
|
|
Create pipes and fork.
|
|
*/
|
|
if((pipe(pfd)) == -1)
|
|
fatal("Can not create pipe");
|
|
if ((i = fork()) < (pid_t)0) {
|
|
close(pfd[0]);
|
|
close(pfd[1]);
|
|
fatal("Can not fork, try again");
|
|
}
|
|
else if (i == (pid_t)0) { /* child process */
|
|
close(pfd[0]);
|
|
close(1);
|
|
dup(pfd[1]);
|
|
close(pfd[1]);
|
|
|
|
/*
|
|
Execute 'diff' on the segment files.
|
|
*/
|
|
execlp(diff,diff,otmp,ntmp,0);
|
|
close(1);
|
|
sprintf(Error,"Can not execute '%s'",diff);
|
|
fatal(Error);
|
|
}
|
|
else { /* parent process */
|
|
close(pfd[1]);
|
|
pipeinp = fdopen(pfd[0], "r");
|
|
|
|
/*
|
|
Process 'diff' output.
|
|
*/
|
|
while ((dp = fgets(diffline,BUFSIZ,pipeinp))) {
|
|
if (isdigit(*dp)) {
|
|
if (fixnum(diffline,&type))
|
|
break;
|
|
} else
|
|
printf("%s",diffline);
|
|
}
|
|
|
|
/*
|
|
Remove temporary files.
|
|
*/
|
|
unlink(otmp);
|
|
unlink(ntmp);
|
|
|
|
olinenum += otcnt;
|
|
nlinenum += ntcnt;
|
|
otcnt = ntcnt = 0;
|
|
|
|
if (dp) {
|
|
switch (type) {
|
|
case 'a':
|
|
pnewtemp = carryon(pipeinp,ntmp,
|
|
&ntcnt,&nlinenum,0);
|
|
break;
|
|
case 'd':
|
|
poldtemp = carryon(pipeinp,otmp,
|
|
&otcnt,&olinenum,0);
|
|
break;
|
|
case 'c':
|
|
poldtemp = carryon(pipeinp,otmp,
|
|
&otcnt,&olinenum,1);
|
|
pnewtemp = carryon(pipeinp,ntmp,
|
|
&ntcnt,&nlinenum,0);
|
|
}
|
|
}
|
|
|
|
fclose(pipeinp);
|
|
|
|
/*
|
|
EOF on pipe.
|
|
*/
|
|
wait(&status);
|
|
if (status&~0x100) {
|
|
sprintf(Error,"'%s' failed",diff);
|
|
fatal(Error);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/*
|
|
Routine to save remainder of a file.
|
|
*/
|
|
saverest(line,iptr,linenum, tcnt, tfp, tfn)
|
|
char *line, *tfn;
|
|
FILE *iptr, *tfp;
|
|
int *linenum, tcnt;
|
|
{
|
|
register char *lp;
|
|
FILE *temptr, *maket();
|
|
|
|
if (tcnt) {
|
|
temptr = tfp;
|
|
strcpy(tempfile, tfn);
|
|
*linenum += tcnt;
|
|
} else {
|
|
temptr = maket(tempfile);
|
|
}
|
|
|
|
lp = line;
|
|
|
|
while (lp) {
|
|
fputs(line,temptr);
|
|
(*linenum)++;
|
|
lp = fgets(line,BUFSIZ,iptr);
|
|
}
|
|
fclose(temptr);
|
|
}
|
|
|
|
/*
|
|
Routine to write out data saved by
|
|
'saverest' routine and to remove the file.
|
|
*/
|
|
putsave(line,type)
|
|
char *line;
|
|
char type;
|
|
{
|
|
FILE *temptr;
|
|
|
|
if((temptr = fopen(tempfile, "r")) == NULL) {
|
|
sprintf(Error, "Can not open tempfile ('%s')", tempfile);
|
|
fatal(Error);
|
|
}
|
|
|
|
while (fgets(line,BUFSIZ,temptr))
|
|
printf("%c %s",type,line);
|
|
|
|
fclose(temptr);
|
|
|
|
unlink(tempfile);
|
|
}
|
|
|
|
fixnum(lp,type)
|
|
char *lp, *type;
|
|
{
|
|
int nums[4], *num, comma;
|
|
|
|
comma = nums[2] = nums[3] = 0;
|
|
*type = ' ';
|
|
num = nums;
|
|
while (*lp) {
|
|
switch (*lp) {
|
|
|
|
case 'a':
|
|
case 'c':
|
|
case 'd':
|
|
*type = *lp;
|
|
lp++;
|
|
num++;
|
|
break;
|
|
case ',':
|
|
if (*type == ' ')
|
|
comma = 1;
|
|
else if (comma == 0)
|
|
comma = 2;
|
|
else
|
|
comma = 3;
|
|
lp++;
|
|
num++;
|
|
break;
|
|
case '\n':
|
|
lp++;
|
|
break;
|
|
default:
|
|
lp = satoi(lp,num);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Check the diff output line numbers to see if they reach the
|
|
segment boundary.
|
|
*/
|
|
if (nums[0] == seglim || nums[1] == seglim || nums[2] == seglim ||
|
|
nums[3] == seglim) {
|
|
/*
|
|
Check if the whole segment is different from the other segment.
|
|
*/
|
|
if (!((nums[0] == 1 && nums[1] == seglim) ||
|
|
(nums[1] == 1 && nums[2] == seglim) ||
|
|
(nums[2] == 1 && nums[3] == seglim)))
|
|
/*
|
|
Need to carry the last diff output to next segment
|
|
to avoid redundant diff output due to insert/delete
|
|
lines.
|
|
*/
|
|
return(1);
|
|
}
|
|
|
|
/*
|
|
Print out fixed line numbers
|
|
*/
|
|
switch (comma) {
|
|
case 0:
|
|
printf("%d%c%d\n",nums[0]+olinenum,*type,nums[1]+nlinenum);
|
|
break;
|
|
case 1:
|
|
printf("%d,%d%c%d\n",nums[0]+olinenum,nums[1]+olinenum,*type,
|
|
nums[2]+nlinenum);
|
|
break;
|
|
case 2:
|
|
printf("%d%c%d,%d\n",nums[0]+olinenum,*type,nums[1]+nlinenum,
|
|
nums[2]+nlinenum);
|
|
break;
|
|
case 3:
|
|
printf("%d,%d%c%d,%d\n",nums[0]+olinenum,nums[1]+olinenum,
|
|
*type,nums[2]+nlinenum,nums[3]+nlinenum);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
FILE *
|
|
carryon(pipeinp,fn,cnt,linenum,check)
|
|
FILE *pipeinp;
|
|
char *fn;
|
|
int *cnt, *linenum, check;
|
|
/*
|
|
Carry the diff output which reaches the segment boundary.
|
|
*/
|
|
{
|
|
FILE *fp, *maket();
|
|
char *dp, linebuf[BUFSIZ];
|
|
|
|
fp = maket(fn);
|
|
while ((dp = fgets(linebuf,BUFSIZ,pipeinp))) {
|
|
if (check)
|
|
if (*dp == '-')
|
|
break;
|
|
fputs(linebuf+2,fp);
|
|
if (ferror(fp) != 0) {
|
|
Fflags |= FTLMSG;
|
|
fatal("Can not write to temporary file");
|
|
}
|
|
(*cnt)++;
|
|
}
|
|
*linenum -= *cnt;
|
|
return(fp);
|
|
}
|
|
|
|
addgen(lp,fp, tcnt, tfp, tfn)
|
|
char *lp, *tfn;
|
|
int tcnt;
|
|
FILE *fp, *tfp;
|
|
{
|
|
int savenum;
|
|
|
|
savenum = nlinenum + 1;
|
|
printf("%da%d", olinenum, savenum);
|
|
|
|
/*
|
|
Save lines of new file.
|
|
*/
|
|
saverest(lp,fp,&nlinenum, tcnt, tfp, tfn);
|
|
|
|
if (savenum < nlinenum)
|
|
printf(",%d\n",nlinenum);
|
|
else
|
|
printf("\n");
|
|
|
|
/*
|
|
Output saved lines, as 'diff' would.
|
|
*/
|
|
putsave(lp,'>');
|
|
|
|
exit(0);
|
|
}
|
|
|
|
delgen(lp,fp, tcnt, tfp, tfn)
|
|
char *lp, *tfn;
|
|
int tcnt;
|
|
FILE *fp, *tfp;
|
|
{
|
|
int savenum;
|
|
|
|
savenum = olinenum + 1;
|
|
printf("%d", savenum);
|
|
|
|
/*
|
|
Save lines of old file.
|
|
*/
|
|
saverest(lp,fp,&olinenum, tcnt, tfp, tfn);
|
|
|
|
if (savenum < olinenum)
|
|
printf(",%dd%d\n", olinenum, nlinenum);
|
|
else
|
|
printf("d%d\n", nlinenum);
|
|
|
|
/*
|
|
Output saved lines, as 'diff' would.
|
|
*/
|
|
putsave(lp,'<');
|
|
|
|
exit(0);
|
|
}
|
|
|
|
clean_up()
|
|
{
|
|
unlink(tempfile);
|
|
unlink(otmp);
|
|
unlink(ntmp);
|
|
}
|
|
|
|
FILE *
|
|
maket(file)
|
|
char *file;
|
|
{
|
|
FILE *iop;
|
|
register char *cp;
|
|
char *mktemp();
|
|
|
|
strcpy(file, tempskel);
|
|
cp = mktemp(file);
|
|
if ((iop = fopen(cp, "w+")) == NULL) {
|
|
sprintf(Error, "Can not open/create temp file ('%s')", cp);
|
|
fatal(Error);
|
|
}
|
|
return(iop);
|
|
}
|
|
|
|
fatal(msg)
|
|
char *msg;
|
|
/*
|
|
General purpose error handler.
|
|
|
|
The argument to fatal is a pointer to an error message string.
|
|
The action of this routine is driven completely from
|
|
the "Fflags" global word (see <fatal.h>).
|
|
|
|
The FTLMSG bit controls the writing of the error
|
|
message on file descriptor 2. The message is preceded
|
|
by the string "ERROR: ", unless the global character pointer
|
|
"Ffile" is non-zero, in which case the message is preceded
|
|
by the string "ERROR [<Ffile>]: ". A newline is written
|
|
after the user supplied message.
|
|
|
|
If the FTLCLN bit is on, clean_up is called with an
|
|
argument of 0.
|
|
*/
|
|
{
|
|
if (Fflags & FTLMSG)
|
|
fprintf(stderr, "%s: %s\n", prognam, msg);
|
|
if (Fflags & FTLCLN)
|
|
clean_up();
|
|
if (Fflags & FTLEXIT)
|
|
exit(1);
|
|
}
|
|
|
|
setsig()
|
|
/*
|
|
General-purpose signal setting routine.
|
|
All non-ignored, non-caught signals are caught.
|
|
If a signal other than hangup, interrupt, or quit is caught,
|
|
a "user-oriented" message is printed on file descriptor 2.
|
|
If hangup, interrupt or quit is caught, that signal
|
|
is set to ignore.
|
|
Termination is like that of "fatal",
|
|
via "clean_up(sig)" (sig is the signal number).
|
|
*/
|
|
{
|
|
void setsig1();
|
|
register int j, n;
|
|
|
|
for (j=1; j < ONSIG; j++) {
|
|
n = (int) signal(j, setsig1);
|
|
if ((void(*)())n == SIG_ERR)
|
|
continue;
|
|
if ((void(*)())n == SIG_DFL)
|
|
continue;
|
|
signal(j, (void(*)())n);
|
|
}
|
|
}
|
|
|
|
void
|
|
setsig1(sig)
|
|
int sig;
|
|
{
|
|
|
|
signal(sig, SIG_IGN);
|
|
clean_up();
|
|
exit(1);
|
|
}
|
|
|
|
char *satoi(p,ip)
|
|
register char *p;
|
|
register int *ip;
|
|
{
|
|
register int sum;
|
|
|
|
sum = 0;
|
|
while (isdigit(*p))
|
|
sum = sum * 10 + (*p++ - '0');
|
|
*ip = sum;
|
|
return(p);
|
|
}
|