/* TEXTCOM compares two text files, printing differences attempts to synchronise after finding differences. G. Nigel Gilbert / MICROLOGY (tel 04868-22521) 16.10.81 vers 1.3: tabs expanded on list output 30.8.82 vers 1.4: control chars suppressed on output (except ^I,cr/lf) vers 1.5: option to ignore parity bit vers 1.6: findline() substituted for fgets(), which got screwed up when \n had its parity bit set compile with -e3000 -o */ #define VERSION 16 #include "bdscio.h" #define MAXINT 65535 #define MAXSYNC 100 /*max distance to attempt to restore synchronism*/ #define DEFLSYNC 3 /*default no. of bytes which must match to establish synchronity*/ #define MAXLEN 256 /*max length of input line*/ #define NO 0 #define YES 1 #define bool char bool putboth; /*print diffs on both screen and printer*/ bool pause; /*stop at the end of each screen of output*/ bool nosync; /*don't attempt to synchronise*/ int sync; /*distance over which files must be matched */ int line; /*output line count*/ int col; /*output column no for printer tabs*/ bool report; /*write differences to file*/ bool parity; /*ignore parity bit*/ char repbuf[BUFSIZ]; /*file buffer for report*/ char name1[15], name2[15]; /*names of files to compare*/ unsigned line1, line2; /*line counts for input files*/ unsigned start, stop; /*start and stop comparing at lines..*/ bool different, listing;/*found a difference; list diffs.*/ char *c1,*c2; /*pointers to current lines being compared*/ char fbuf1[BUFSIZ], fbuf2[BUFSIZ]; /*input file buffers*/ char repname[15]; /*report file name*/ struct aring { char *buf[MAXSYNC]; int ringp; bool eof; } ring1, ring2; /*file input ring buffer*/ char *findline(line,iobuf) /*read a line from an input file, returning NULL if at Eof */ char *line, *iobuf; { int cint; char i, cnopar; for (i=0; i < (MAXLEN-2) && (cint=getc(iobuf)) != ERROR; i++) { if ((cnopar=cint & 0x7f) == CPMEOF) return NULL; *line++= (parity ? cnopar : cint); if (cnopar == '\n') break; } if (i == (MAXLEN-2)) cnopar='\n'; if (cnopar != '\n') return NULL; *line='\0'; return line; } char *readline(iobuf) /*read a line and store it away, returning pointer to it, or NULL if end of file*/ char *iobuf; { char temp[MAXLEN], *t, *p, *alloc(), *findline(), *strcpy(); if (findline(temp,iobuf) == NULL) return NULL; if ((p=alloc(strlen(temp)+1)) == NULL) { puts("Out of memory\n"); exit(); } return strcpy(p,temp); } char *getline(ring,iobuf) /*get a line of text from ring buffer returning pointer to it; replace with one from input file*/ struct aring *ring; char *iobuf; { char *c, **p, *readline(); if (ring -> ringp == MAXSYNC) ring -> ringp = 0; c = *( p=&(ring -> buf[ring -> ringp++]) ); if (ring -> eof || (*p=readline(iobuf)) == NULL) { *p=EOF; ring -> eof=YES; } return c; } initring(ring,iobuf) /*initialise by filling ring buffer*/ struct aring *ring; char *iobuf; { int i; char *c, *readline(); ring -> eof=NO; for (i=0; i eof || (c=readline(iobuf)) == NULL) { c=EOF; ring -> eof=YES; } ring -> buf[i]=c; } ring -> ringp=MAXSYNC; } char *peep(ring,p) /*return pointer to p'th next line*/ struct aring *ring; int p; { return ring -> buf[ (p - 1 + ring -> ringp) % MAXSYNC]; } pline(c) /*print a line and free its storage space*/ char *c; { lineincr(); if (c != EOF) { puts(c); free(c); } else puts("--End Of File--\n"); } putchar(c) /*print a printable character, on printer too if putboth or into report file if reporting*/ int c; { if (c < ' ' && (c != '\n' && c != '\r' && c != '\t')) return c; if (c == '\n')putchar('\r'); if (putboth) { if (c == '\t') while (++col%8) bdos(5,' '); else { if (c == '\n') col=0; else col++; bdos(5,c); } } if (report) { if (putc(c,repbuf) == ERROR) { report=NO; puts("Error on writing report file\n"); exit(); } } else bdos(2,c); return c; } lineincr() /*increment line count; pause, print header if at end of screen*/ { if (line >= 22 && pause) { puts("\t\t\t\t\t[Return]/Quit > "); if (tolower(getchar()) == 'q') exit(); putchar('\n'); line=0; } if (!line++)printf("\t<<<<< Comparison of %s with %s >>>>>\n", name1,name2); } scompare(s1,s2) /*compare strings for equality (0=same)*/ char *s1,*s2; { if (s1 == s2) return 0; /*both EOF*/ if (s1 == EOF || s2 == EOF) return 1; return strcmp(s1,s2); } search(ringa,ringb,c) /*try to get files back into sync. when mismatch has been found. If successful, returns number of lines to skip to get back to synchronity, else 0*/ struct aring *ringa, *ringb; char *c; { int outofsync, match, i; if (nosync) return 0; for (outofsync=1; outofsync < MAXSYNC; outofsync++) /*look forward for a match*/ if (!scompare(peep(ringb,outofsync),c)) break; /*break when found*/ if (outofsync == MAXSYNC) return 0; else /* match found, look for sync more */ for (match=1; match < sync; match++) if ( (i=outofsync+match) >= MAXSYNC || scompare(peep(ringa,match),peep(ringb,i))) break; /*break if no match*/ if (match == sync) return outofsync; /*return pos of start of sync matches*/ else return 0; } blksrch(ringa,ringb,n) /*try to get files back into sync. when mismatch at n'th character after current has been found. If successful, returns number of lines to skip to get back to synchronity, else 0*/ struct aring *ringa, *ringb; int n; { int outofsync, match, i; if (nosync) return 0; for (outofsync=1; outofsync+n < MAXSYNC; outofsync++) /*look forward for a match*/ if (!scompare(peep(ringb,outofsync+n),peep(ringa,n))) break; /*break when found*/ if (outofsync == MAXSYNC) return 0; else /* match found, look for sync more */ for (match=1; match < sync; match++) if ( (i=outofsync+match+n) >= MAXSYNC || scompare(peep(ringa,match+n),peep(ringb,i))) break; /*break if no match*/ if (match == sync) return outofsync; /*return pos of start of sync matches*/ else return 0; } addr(p) /*gets a line number from a command line parameter; returns it*/ char **p; { int n; n=0; while (*(++*p)) n=10*n+**p-'0'; return n; } args(argc,argv) /*read args from command line*/ int argc; char *argv[]; { int i; char *p; putboth=listing=nosync=report=NO; pause=parity=YES; start=sync=0; stop=MAXINT; printf("[Textcom v%d.%d]\n",VERSION/10,VERSION%10); if (argc < 3) { puts("Usage: TEXTCOM oldfile newfile [options]\n"); puts("\t(compares an old version of a text file with a new)\n"); puts("\n Options:\n"); puts(" -Sdd to syncronise up to dd lines\n"); printf(" (use -S for strict compare). Default = %d\n", DEFLSYNC); puts(" -L to list differences on printer and screen\n"); puts(" -Rname to create a file, 'name', reporting differences\n"); puts(" -C to continue without pauses at the end of each screen\n"); puts(" -Fxxxx to display differences only from "); puts("line xxxx\n"); puts(" -Txxxx to stop comparison at line xxxx\n"); puts(" -P to include parity bit in comparison\n"); exit(); } for (i=3; i < argc; i++) { switch( *(p=++argv[i]) ) { case 'L': listing=YES; break; case 'C': pause=NO; break; case 'S': sync=addr(&p); nosync=!sync; if (sync >= MAXSYNC) { printf("Max. sync is %u",MAXSYNC); exit(); } break; case 'R': report=YES; strcpy(repname,++p); break; case 'F': start=addr(&p); break; case 'T': stop=addr(&p); break; case 'P': parity=NO; break; default : puts("Unknown option -"); puts(p); exit(); } } if (stop < start) { printf("-From (%d) is not less than -To (%d)\n",start,stop); exit(); } if(listing || report) pause=NO; if(sync == 0) sync=DEFLSYNC; strcpy(name1,argv[1]); if (fopen(name1,fbuf1) == ERROR) { puts("Can't find "); puts(name1); exit(); } initring(&ring1,fbuf1); strcpy(name2,argv[2]); if (fopen(name2,fbuf2) == ERROR) { puts("Can't find "); puts(name2); exit(); } initring(&ring2,fbuf2); if (report) { if (fcreat(repname,repbuf) == ERROR) { puts("Can't open "); puts(repname); exit(); } } putboth=listing; } printdif() /*resynchronise and print differences */ { int unsync, i; if ( (unsync=search(&ring2,&ring1,c2))) { /*note deletion*/ lineincr(); puts(">>>>>> "); if (unsync == 1) printf("Line %u",line1+1); else printf("Lines %u to %u",line1,line1+unsync); printf(" of %s deleted from %s\n",name1,name2); for (i=0; i>>>>> "); if (unsync == 1) printf("Line %u",line2+1); else printf("Lines %u to %u",line2, line2+unsync); printf(" of %s inserted\n",name2); for (i=0; i>>>>> Line %u of %s changed to line %u of %s\n", line1,name1,line2,name2); pline(c1,line1); pline(c2,line2); } else { lineincr(); printf(">>>>>> Lines %u to %u of %s ....\n", line1,line1+unsync-1,name1); for (i=1; i<=unsync; i++) { pline(c1); if (i < unsync)c1=getline(&ring1,fbuf1); } lineincr(); printf(">>>>>> .... changed to lines %u to %u of %s\n", line2,line2+unsync-1,name2); for (i=1; i<=unsync; i++) { pline(c2); if(i < unsync)c2=getline(&ring2,fbuf2); } line1+=--unsync; line2+=unsync; } } } } main(argc,argv) int argc; char *argv[]; { _allocp=NULL; args(argc,argv); line1=line2=1; line=col=0; different=NO; do { c1=getline(&ring1,fbuf1); c2=getline(&ring2,fbuf2); if (scompare(c1,c2)) { /*lines are different*/ different=YES; if (line1 >= start && line2 >= start) printdif(); } else { if (c1 != EOF) free(c1); if (c2 != EOF) free(c2); } line1++; line2++; } while ((c1 != EOF || c2 != EOF) && (line1 < stop || line2 < stop)); if (different) puts("Files differ"); else puts("Files are identical"); if (start != 0 || stop != MAXINT) printf(" between lines %u and %u\n",start,min(line1,line2)); else putchar('\n'); if (report) { /*flush and close report file*/ putc(CPMEOF,repbuf); fflush(repbuf); fclose(repbuf); } }