Annotation of 3BSD/cmd/diff.c, revision 1.1.1.1

1.1       root        1: #
                      2: /*     diff - differential file comparison
                      3: *
                      4: *      Uses an algorithm due to Harold Stone, which finds
                      5: *      a pair of longest identical subsequences in the two
                      6: *      files.
                      7: *
                      8: *      The major goal is to generate the match vector J.
                      9: *      J[i] is the index of the line in file1 corresponding
                     10: *      to line i file0. J[i] = 0 if there is no
                     11: *      such line in file1.
                     12: *
                     13: *      Lines are hashed so as to work in core. All potential
                     14: *      matches are located by sorting the lines of each file
                     15: *      on the hash (called value_____). In particular, this
                     16: *      collects the equivalence classes in file1 together.
                     17: *      Subroutine equiv____  replaces the value of each line in
                     18: *      file0 by the index of the first element of its 
                     19: *      matching equivalence in (the reordered) file1.
                     20: *      To save space equiv_____ squeezes file1 into a single
                     21: *      array member______ in which the equivalence classes
                     22: *      are simply concatenated, except that their first
                     23: *      members are flagged by changing sign.
                     24: *
                     25: *      Next the indices that point into member______ are unsorted_______   into
                     26: *      array class_____ according to the original order of file0.
                     27: *
                     28: *      The cleverness lies in routine stone______. This marches
                     29: *      through the lines of file0, developing a vector klist_____
                     30: *      of "k-candidates". At step i a k-candidate is a matched
                     31: *      pair of lines x,y (x in file0 y in file1) such that
                     32: *      there is a common subsequence of lenght k
                     33: *      between the first i lines of file0 and the first y 
                     34: *      lines of file1, but there is no such subsequence for
                     35: *      any smaller y. x is the earliest possible mate to y
                     36: *      that occurs in such a subsequence.
                     37: *
                     38: *      Whenever any of the members of the equivalence class of
                     39: *      lines in file1 matable to a line in file0 has serial number 
                     40: *      less than the y of some k-candidate, that k-candidate 
                     41: *      with the smallest such y is replaced. The new 
                     42: *      k-candidate is chained (via pred____) to the current
                     43: *      k-1 candidate so that the actual subsequence can
                     44: *      be recovered. When a member has serial number greater
                     45: *      that the y of all k-candidates, the klist is extended.
                     46: *      At the end, the longest subsequence is pulled out
                     47: *      and placed in the array J by unravel_______.
                     48: *
                     49: *      With J in hand, the matches there recorded are
                     50: *      check_____ed against reality to assure that no spurious
                     51: *      matches have crept in due to hashing. If they have,
                     52: *      they are broken, and "jackpot " is recorded--a harmless
                     53: *      matter except that a true match for a spuriously
                     54: *      mated line may now be unnecessarily reported as a change.
                     55: *
                     56: *      Much of the complexity of the program comes simply
                     57: *      from trying to minimize core utilization and
                     58: *      maximize the range of doable problems by dynamically
                     59: *      allocating what is needed and reusing what is not.
                     60: *      The core requirements for problems larger than somewhat
                     61: *      are (in words) 2*length(file0) + length(file1) +
                     62: *      3*(number of k-candidates installed),  typically about
                     63: *      6n words for files of length n. 
                     64: */
                     65: #include <stdio.h>
                     66: #include <ctype.h>
                     67: #include <sys/types.h>
                     68: #include <sys/stat.h>
                     69: #include <signal.h>
                     70: #define        prints(s)       fputs(s,stdout)
                     71: 
                     72: #define HALFLONG 16
                     73: #define low(x) (x&((1L<<HALFLONG)-1))
                     74: #define high(x)        (x>>HALFLONG)
                     75: FILE *input[2];
                     76: FILE *fopen();
                     77: 
                     78: struct cand {
                     79:        int x;
                     80:        int y;
                     81:        int pred;
                     82: } cand;
                     83: struct line {
                     84:        int serial;
                     85:        int value;
                     86: } *file[2], line;
                     87: int len[2];
                     88: struct line *sfile[2]; /*shortened by pruning common prefix and suffix*/
                     89: int slen[2];
                     90: int pref, suff;        /*length of prefix and suffix*/
                     91: int *class;    /*will be overlaid on file[0]*/
                     92: int *member;   /*will be overlaid on file[1]*/
                     93: int *klist;            /*will be overlaid on file[0] after class*/
                     94: struct cand *clist;    /* merely a free storage pot for candidates */
                     95: int clen = 0;
                     96: int *J;                /*will be overlaid on class*/
                     97: long *ixold;   /*will be overlaid on klist*/
                     98: long *ixnew;   /*will be overlaid on file[1]*/
                     99: int opt;       /* -1,0,1 = -e,normal,-f */
                    100: int status = 2;
                    101: int anychange = 0;
                    102: char *empty = "";
                    103: int bflag;
                    104: 
                    105: char *tempfile;        /*used when comparing against std input*/
                    106: char *mktemp();
                    107: char *dummy;   /*used in resetting storage search ptr*/
                    108: 
                    109: done()
                    110: {
                    111:        unlink(tempfile);
                    112:        exit(status);
                    113: }
                    114: 
                    115: char *talloc(n)
                    116: {
                    117:        extern char *malloc();
                    118:        register char *p;
                    119:        p = malloc((unsigned)n);
                    120:        if(p!=NULL)
                    121:                return(p);
                    122:        noroom();
                    123: }
                    124: 
                    125: char *ralloc(p,n)      /*compacting reallocation */
                    126: char *p;
                    127: {
                    128:        register char *q;
                    129:        char *realloc();
                    130:        free(p);
                    131:        free(dummy);
                    132:        dummy = malloc(1);
                    133:        q = realloc(p, (unsigned)n);
                    134:        if(q==NULL)
                    135:                noroom();
                    136:        return(q);
                    137: }
                    138: 
                    139: noroom()
                    140: {
                    141:        mesg("files too big, try -h\n",empty);
                    142:        done();
                    143: }
                    144: 
                    145: sort(a,n)      /*shellsort CACM #201*/
                    146: struct line *a;
                    147: {
                    148:        struct line w;
                    149:        register int j,m;
                    150:        struct line *ai;
                    151:        register struct line *aim;
                    152:        int k;
                    153:        for(j=1;j<=n;j*= 2)
                    154:                m = 2*j - 1;
                    155:        for(m/=2;m!=0;m/=2) {
                    156:                k = n-m;
                    157:                for(j=1;j<=k;j++) {
                    158:                        for(ai = &a[j]; ai > a; ai -= m) {
                    159:                                aim = &ai[m];
                    160:                                if(aim < ai)
                    161:                                        break;  /*wraparound*/
                    162:                                if(aim->value > ai[0].value ||
                    163:                                   aim->value == ai[0].value &&
                    164:                                   aim->serial > ai[0].serial)
                    165:                                        break;
                    166:                                w.value = ai[0].value;
                    167:                                ai[0].value = aim->value;
                    168:                                aim->value = w.value;
                    169:                                w.serial = ai[0].serial;
                    170:                                ai[0].serial = aim->serial;
                    171:                                aim->serial = w.serial;
                    172:                        }
                    173:                }
                    174:        }
                    175: }
                    176: 
                    177: unsort(f, l, b)
                    178: struct line *f;
                    179: int *b;
                    180: {
                    181:        register int *a;
                    182:        register int i;
                    183:        a = (int *)talloc((l+1)*sizeof(int));
                    184:        for(i=1;i<=l;i++)
                    185:                a[f[i].serial] = f[i].value;
                    186:        for(i=1;i<=l;i++)
                    187:                b[i] = a[i];
                    188:        free((char *)a);
                    189: }
                    190: 
                    191: filename(pa1, pa2)
                    192: char **pa1, **pa2;
                    193: {
                    194:        register char *a1, *b1, *a2;
                    195:        char buf[BUFSIZ];
                    196:        struct stat stbuf;
                    197:        int i, f;
                    198:        a1 = *pa1;
                    199:        a2 = *pa2;
                    200:        if(stat(a1,&stbuf)!=-1 && ((stbuf.st_mode&S_IFMT)==S_IFDIR)) {
                    201:                b1 = *pa1 = malloc(100);
                    202:                while(*b1++ = *a1++) ;
                    203:                b1[-1] = '/';
                    204:                a1 = b1;
                    205:                while(*a1++ = *a2++)
                    206:                        if(*a2 && *a2!='/' && a2[-1]=='/')
                    207:                                a1 = b1;
                    208:        }
                    209:        else if(a1[0]=='-'&&a1[1]==0&&tempfile==0) {
                    210:                signal(SIGHUP,done);
                    211:                signal(SIGINT,done);
                    212:                signal(SIGPIPE,done);
                    213:                signal(SIGTERM,done);
                    214:                *pa1 = tempfile = mktemp("/tmp/dXXXXX");
                    215:                if((f=creat(tempfile,0600)) < 0) {
                    216:                        mesg("cannot create ",tempfile);
                    217:                        done();
                    218:                }
                    219:                while((i=read(0,buf,BUFSIZ))>0)
                    220:                        write(f,buf,i);
                    221:                close(f);
                    222:        }
                    223: }
                    224: 
                    225: prepare(i, arg)
                    226: char *arg;
                    227: {
                    228:        register struct line *p;
                    229:        register j,h;
                    230:        if((input[i] = fopen(arg,"r")) == NULL){
                    231:                mesg("cannot open ", arg);
                    232:                done();
                    233:        }
                    234:        p = (struct line *)talloc(3*sizeof(line));
                    235:        for(j=0; h=readhash(input[i]);) {
                    236:                p = (struct line *)ralloc((char *)p,(++j+3)*sizeof(line));
                    237:                p[j].value = h;
                    238:        }
                    239:        len[i] = j;
                    240:        file[i] = p;
                    241:        fclose(input[i]);
                    242: }
                    243: 
                    244: prune()
                    245: {
                    246:        register i,j;
                    247:        for(pref=0;pref<len[0]&&pref<len[1]&&
                    248:                file[0][pref+1].value==file[1][pref+1].value;
                    249:                pref++ ) ;
                    250:        for(suff=0;suff<len[0]-pref&&suff<len[1]-pref&&
                    251:                file[0][len[0]-suff].value==file[1][len[1]-suff].value;
                    252:                suff++) ;
                    253:        for(j=0;j<2;j++) {
                    254:                sfile[j] = file[j]+pref;
                    255:                slen[j] = len[j]-pref-suff;
                    256:                for(i=0;i<=slen[j];i++)
                    257:                        sfile[j][i].serial = i;
                    258:        }
                    259: }
                    260: 
                    261: equiv(a,n,b,m,c)
                    262: struct line *a, *b;
                    263: int *c;
                    264: {
                    265:        register int i, j;
                    266:        i = j = 1;
                    267:        while(i<=n && j<=m) {
                    268:                if(a[i].value <b[j].value)
                    269:                        a[i++].value = 0;
                    270:                else if(a[i].value == b[j].value)
                    271:                        a[i++].value = j;
                    272:                else
                    273:                        j++;
                    274:        }
                    275:        while(i <= n)
                    276:                a[i++].value = 0;
                    277:        b[m+1].value = 0;
                    278:        j = 0;
                    279:        while(++j <= m) {
                    280:                c[j] = -b[j].serial;
                    281:                while(b[j+1].value == b[j].value) {
                    282:                        j++;
                    283:                        c[j] = b[j].serial;
                    284:                }
                    285:        }
                    286:        c[j] = -1;
                    287: }
                    288: 
                    289: main(argc, argv)
                    290: char **argv;
                    291: {
                    292:        register int k;
                    293:        char **args;
                    294: 
                    295:        args = argv;
                    296:        if(argc>3 && *argv[1]=='-') {
                    297:                argc--;
                    298:                argv++;
                    299:                for(k=1;argv[0][k];k++) {
                    300:                        switch(argv[0][k]) {
                    301:                        case 'e':
                    302:                                opt = -1;
                    303:                                break;
                    304:                        case 'f':
                    305:                                opt = 1;
                    306:                                break;
                    307:                        case 'b':
                    308:                                bflag = 1;
                    309:                                break;
                    310:                        case 'h':
                    311:                                execv("/usr/lib/diffh",args);
                    312:                                mesg("cannot find diffh",empty);
                    313:                                done();
                    314:                        }
                    315:                }
                    316:        }
                    317:        if(argc!=3) {
                    318:                mesg("arg count",empty);
                    319:                done();
                    320:        }
                    321:        dummy = malloc(1);
                    322: 
                    323:        filename(&argv[1], &argv[2]);
                    324:        filename(&argv[2], &argv[1]);
                    325:        prepare(0, argv[1]);
                    326:        prepare(1, argv[2]);
                    327:        prune();
                    328:        sort(sfile[0],slen[0]);
                    329:        sort(sfile[1],slen[1]);
                    330: 
                    331:        member = (int *)file[1];
                    332:        equiv(sfile[0], slen[0], sfile[1], slen[1], member);
                    333:        member = (int *)ralloc((char *)member,(slen[1]+2)*sizeof(int));
                    334: 
                    335:        class = (int *)file[0];
                    336:        unsort(sfile[0], slen[0], class);
                    337:        class = (int *)ralloc((char *)class,(slen[0]+2)*sizeof(int));
                    338: 
                    339:        klist = (int *)talloc((slen[0]+2)*sizeof(int));
                    340:        clist = (struct cand *)talloc(sizeof(cand));
                    341:        k = stone(class, slen[0], member, klist);
                    342:        free((char *)member);
                    343:        free((char *)class);
                    344: 
                    345:        J = (int *)talloc((len[0]+2)*sizeof(int));
                    346:        unravel(klist[k]);
                    347:        free((char *)clist);
                    348:        free((char *)klist);
                    349: 
                    350:        ixold = (long *)talloc((len[0]+2)*sizeof(long));
                    351:        ixnew = (long *)talloc((len[1]+2)*sizeof(long));
                    352:        check(argv);
                    353:        output(argv);
                    354:        status = anychange;
                    355:        done();
                    356: }
                    357: 
                    358: stone(a,n,b,c)
                    359: int *a;
                    360: int *b;
                    361: int *c;
                    362: {
                    363:        register int i, k,y;
                    364:        int j, l;
                    365:        int oldc, tc;
                    366:        int oldl;
                    367:        k = 0;
                    368:        c[0] = newcand(0,0,0);
                    369:        for(i=1; i<=n; i++) {
                    370:                j = a[i];
                    371:                if(j==0)
                    372:                        continue;
                    373:                y = -b[j];
                    374:                oldl = 0;
                    375:                oldc = c[0];
                    376:                do {
                    377:                        if(y <= clist[oldc].y)
                    378:                                continue;
                    379:                        l = search(c, k, y);
                    380:                        if(l!=oldl+1)
                    381:                                oldc = c[l-1];
                    382:                        if(l<=k) {
                    383:                                if(clist[c[l]].y <= y)
                    384:                                        continue;
                    385:                                tc = c[l];
                    386:                                c[l] = newcand(i,y,oldc);
                    387:                                oldc = tc;
                    388:                                oldl = l;
                    389:                        } else {
                    390:                                c[l] = newcand(i,y,oldc);
                    391:                                k++;
                    392:                                break;
                    393:                        }
                    394:                } while((y=b[++j]) > 0);
                    395:        }
                    396:        return(k);
                    397: }
                    398: 
                    399: newcand(x,y,pred)
                    400: {
                    401:        register struct cand *q;
                    402:        clist = (struct cand *)ralloc((char *)clist,++clen*sizeof(cand));
                    403:        q = clist + clen -1;
                    404:        q->x = x;
                    405:        q->y = y;
                    406:        q->pred = pred;
                    407:        return(clen-1);
                    408: }
                    409: 
                    410: search(c, k, y)
                    411: int *c;
                    412: {
                    413:        register int i, j, l;
                    414:        int t;
                    415:        if(clist[c[k]].y<y)     /*quick look for typical case*/
                    416:                return(k+1);
                    417:        i = 0;
                    418:        j = k+1;
                    419:        while((l=(i+j)/2) > i) {
                    420:                t = clist[c[l]].y;
                    421:                if(t > y)
                    422:                        j = l;
                    423:                else if(t < y)
                    424:                        i = l;
                    425:                else
                    426:                        return(l);
                    427:        }
                    428:        return(l+1);
                    429: }
                    430: 
                    431: unravel(p)
                    432: {
                    433:        register int i;
                    434:        register struct cand *q;
                    435:        for(i=0; i<=len[0]; i++)
                    436:                J[i] =  i<=pref ? i:
                    437:                        i>len[0]-suff ? i+len[1]-len[0]:
                    438:                        0;
                    439:        for(q=clist+p;q->y!=0;q=clist+q->pred)
                    440:                J[q->x+pref] = q->y+pref;
                    441: }
                    442: 
                    443: /* check does double duty:
                    444: 1.  ferret out any fortuitous correspondences due
                    445: to confounding by hashing (which result in "jackpot")
                    446: 2.  collect random access indexes to the two files */
                    447: 
                    448: check(argv)
                    449: char **argv;
                    450: {
                    451:        register int i, j;
                    452:        int jackpot;
                    453:        long ctold, ctnew;
                    454:        char c,d;
                    455:        input[0] = fopen(argv[1],"r");
                    456:        input[1] = fopen(argv[2],"r");
                    457:        j = 1;
                    458:        ixold[0] = ixnew[0] = 0;
                    459:        jackpot = 0;
                    460:        ctold = ctnew = 0;
                    461:        for(i=1;i<=len[0];i++) {
                    462:                if(J[i]==0) {
                    463:                        ixold[i] = ctold += skipline(0);
                    464:                        continue;
                    465:                }
                    466:                while(j<J[i]) {
                    467:                        ixnew[j] = ctnew += skipline(1);
                    468:                        j++;
                    469:                }
                    470:                for(;;) {
                    471:                        c = getc(input[0]);
                    472:                        d = getc(input[1]);
                    473:                        ctold++;
                    474:                        ctnew++;
                    475:                        if(bflag && isspace(c) && isspace(d)) {
                    476:                                do {
                    477:                                        if(c=='\n') break;
                    478:                                        ctold++;
                    479:                                } while(isspace(c=getc(input[0])));
                    480:                                do {
                    481:                                        if(d=='\n') break;
                    482:                                        ctnew++;
                    483:                                } while(isspace(d=getc(input[1])));
                    484:                        }
                    485:                        if(c!=d) {
                    486:                                jackpot++;
                    487:                                J[i] = 0;
                    488:                                if(c!='\n')
                    489:                                        ctold += skipline(0);
                    490:                                if(d!='\n')
                    491:                                        ctnew += skipline(1);
                    492:                                break;
                    493:                        }
                    494:                        if(c=='\n')
                    495:                                break;
                    496:                }
                    497:                ixold[i] = ctold;
                    498:                ixnew[j] = ctnew;
                    499:                j++;
                    500:        }
                    501:        for(;j<=len[1];j++) {
                    502:                ixnew[j] = ctnew += skipline(1);
                    503:        }
                    504:        fclose(input[0]);
                    505:        fclose(input[1]);
                    506: /*
                    507:        if(jackpot)
                    508:                mesg("jackpot",empty);
                    509: */
                    510: }
                    511: 
                    512: skipline(f)
                    513: {
                    514:        register i;
                    515:        for(i=1;getc(input[f])!='\n';i++) ;
                    516:        return(i);
                    517: }
                    518: 
                    519: output(argv)
                    520: char **argv;
                    521: {
                    522:        int m;
                    523:        register int i0, i1, j1;
                    524:        int j0;
                    525:        input[0] = fopen(argv[1],"r");
                    526:        input[1] = fopen(argv[2],"r");
                    527:        m = len[0];
                    528:        J[0] = 0;
                    529:        J[m+1] = len[1]+1;
                    530:        if(opt!=-1) for(i0=1;i0<=m;i0=i1+1) {
                    531:                while(i0<=m&&J[i0]==J[i0-1]+1) i0++;
                    532:                j0 = J[i0-1]+1;
                    533:                i1 = i0-1;
                    534:                while(i1<m&&J[i1+1]==0) i1++;
                    535:                j1 = J[i1+1]-1;
                    536:                J[i1] = j1;
                    537:                change(i0,i1,j0,j1);
                    538:        } else for(i0=m;i0>=1;i0=i1-1) {
                    539:                while(i0>=1&&J[i0]==J[i0+1]-1&&J[i0]!=0) i0--;
                    540:                j0 = J[i0+1]-1;
                    541:                i1 = i0+1;
                    542:                while(i1>1&&J[i1-1]==0) i1--;
                    543:                j1 = J[i1-1]+1;
                    544:                J[i1] = j1;
                    545:                change(i1,i0,j1,j0);
                    546:        }
                    547:        if(m==0)
                    548:                change(1,0,1,len[1]);
                    549: }
                    550: 
                    551: change(a,b,c,d)
                    552: {
                    553:        if(a>b&&c>d) return;
                    554:        anychange = 1;
                    555:        if(opt!=1) {
                    556:                range(a,b,",");
                    557:                putchar(a>b?'a':c>d?'d':'c');
                    558:                if(opt!=-1) range(c,d,",");
                    559:        } else {
                    560:                putchar(a>b?'a':c>d?'d':'c');
                    561:                range(a,b," ");
                    562:        }
                    563:        putchar('\n');
                    564:        if(opt==0) {
                    565:                fetch(ixold,a,b,input[0],"< ");
                    566:                if(a<=b&&c<=d) prints("---\n");
                    567:        }
                    568:        fetch(ixnew,c,d,input[1],opt==0?"> ":empty);
                    569:        if(opt!=0&&c<=d) prints(".\n");
                    570: }
                    571: 
                    572: range(a,b,separator)
                    573: char *separator;
                    574: {
                    575:        printf("%d", a>b?b:a);
                    576:        if(a<b) {
                    577:                printf("%s%d", separator, b);
                    578:        }
                    579: }
                    580: 
                    581: fetch(f,a,b,lb,s)
                    582: long *f;
                    583: FILE *lb;
                    584: char *s;
                    585: {
                    586:        register int i, j;
                    587:        register int nc;
                    588:        for(i=a;i<=b;i++) {
                    589:                fseek(lb,f[i-1],0);
                    590:                nc = f[i]-f[i-1];
                    591:                prints(s);
                    592:                for(j=0;j<nc;j++)
                    593:                        putchar(getc(lb));
                    594:        }
                    595: }
                    596: 
                    597: /* hashing has the effect of
                    598:  * arranging line in 7-bit bytes and then
                    599:  * summing 1-s complement in 16-bit hunks 
                    600: */
                    601: 
                    602: readhash(f)
                    603: FILE *f;
                    604: {
                    605:        long sum;
                    606:        register unsigned shift;
                    607:        register space;
                    608:        register t;
                    609:        sum = 1;
                    610:        space = 0;
                    611:        if(!bflag) for(shift=0;(t=getc(f))!='\n';shift+=7) {
                    612:                if(t==-1)
                    613:                        return(0);
                    614:                sum += (long)t << (shift%=HALFLONG);
                    615:        }
                    616:        else for(shift=0;;) {
                    617:                switch(t=getc(f)) {
                    618:                case -1:
                    619:                        return(0);
                    620:                case '\t':
                    621:                case ' ':
                    622:                        space++;
                    623:                        continue;
                    624:                default:
                    625:                        if(space) {
                    626:                                shift += 7;
                    627:                                space = 0;
                    628:                        }
                    629:                        sum += (long)t << (shift%=HALFLONG);
                    630:                        shift += 7;
                    631:                        continue;
                    632:                case '\n':
                    633:                        break;
                    634:                }
                    635:                break;
                    636:        }
                    637:        sum = low(sum) + high(sum);
                    638:        return((short)low(sum) + (short)high(sum));
                    639: }
                    640: 
                    641: mesg(s,t)
                    642: char *s, *t;
                    643: {
                    644:        fprintf(stderr,"diff: %s%s\n",s,t);
                    645: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.