home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 133_01 / textcom < prev    next >
Text File  |  1985-03-10  |  12KB  |  496 lines

  1. /*
  2. CUG-HEADER:
  3.    TITLE: "Text comparison utility"
  4.    VER: 1.7
  5.    AUTHOR: G.N. GILBERT
  6.    O.S.: CP/M 2.2
  7.    FNAME: TEXTCOM
  8.    DESC: compares two text files, printing differences
  9.         attempts to re-synchronise after finding differences
  10.    KEYWORDS: text, differences
  11. */
  12.  
  13. /*
  14.     TEXTCOM
  15.  
  16.     compares two text files, printing differences
  17.         attempts to synchronise after finding differences.
  18.  
  19.     G. Nigel Gilbert / MICROLOGY  (tel 04868-22521)  16.10.81
  20.  
  21.     vers 1.3:  tabs expanded on list output      30.8.82
  22.     vers 1.4:  control chars suppressed on output (except ^I,cr/lf)
  23.     vers 1.5:  option to ignore parity bit
  24.     vers 1.6:  findline() substituted for fgets(), which got screwed
  25.             up when \n had its parity bit set
  26.     vers 1.7:  option to match white space in comparison
  27.  
  28.     compile with -e3300 -o
  29. */
  30. #define VERSION 17
  31.  
  32. #include <bdscio.h>
  33.  
  34. #define MAXINT 65535
  35. #define MAXSYNC 100    /*max distance to attempt to restore synchronism*/
  36. #define DEFLSYNC 3    /*default no. of bytes which must match to
  37. establish synchronity*/
  38. #define MAXLEN 512    /*max length of input line*/
  39.  
  40. #define NO 0
  41. #define YES 1
  42.  
  43. #define bool char
  44.  
  45. bool putboth;        /*print diffs on both screen and printer*/
  46. bool pause;        /*stop at the end of each screen of output*/
  47. bool nosync;        /*don't attempt to synchronise*/
  48. int sync;        /*distance over which files must be matched */
  49. int line;        /*output line count*/
  50. int col;        /*output column no for printer tabs*/
  51. bool report;        /*write differences to file*/
  52. bool parity;        /*ignore parity bit*/
  53. bool white;        /*match white space*/
  54. char repbuf[BUFSIZ];    /*file buffer for report*/
  55. char name1[15], name2[15];    /*names of files to compare*/
  56. unsigned line1, line2;    /*line counts for input files*/
  57. unsigned start, stop;    /*start and stop comparing at lines..*/
  58. bool different, listing;/*found a difference; list diffs.*/
  59. char *c1,*c2;        /*pointers to current lines being compared*/
  60. char fbuf1[BUFSIZ], fbuf2[BUFSIZ];    /*input file buffers*/
  61. char repname[15];    /*report file name*/
  62.  
  63. struct aring {
  64.     char *buf[MAXSYNC];
  65.     int ringp;
  66.     bool eof;
  67. ring1, ring2;        /*file input ring buffer*/
  68.  
  69. char *findline(line,iobuf)    /*read a line from an  input file, stripping
  70.                  '\n', and returning NULL if at Eof */
  71. char *line, *iobuf;
  72. {
  73.     int cint;
  74.     char i, cnopar;
  75.  
  76.     for (i=0; i < (MAXLEN-1) && (cint=getc(iobuf)) != ERROR; i++) {
  77.         if ((cnopar=cint & 0x7f) == CPMEOF) return NULL;
  78.         if (cnopar == '\n') break;
  79.         if (cnopar == '\r') continue;
  80.         *line++= (parity ? cnopar : cint);
  81.         }
  82.     *line='\0';
  83.     return line;
  84. }
  85.  
  86. char *readline(iobuf)    /*read a line and store it away, returning pointer
  87.                 to it, or NULL if end of file*/
  88. char *iobuf;
  89. {
  90.     char temp[MAXLEN], *t, *p, *alloc(), *findline(), *strcpy();
  91.  
  92.     if (findline(temp,iobuf) == NULL) return NULL;
  93.     if ((p=alloc(strlen(temp)+1)) == NULL) {
  94.         puts("Out of memory\n"); 
  95.         exit();
  96.         }
  97.     return strcpy(p,temp);
  98. }
  99.  
  100. char *getline(ring,iobuf)    /*get a line of text from ring buffer
  101.                     returning pointer to it;
  102.                     replace with one from input file*/
  103. struct aring *ring;
  104. char *iobuf;
  105. {
  106.     char *c, **p, *readline();
  107.  
  108.     if (ring -> ringp == MAXSYNC) ring -> ringp = 0;
  109.     c = *( p=&(ring -> buf[ring -> ringp++]) );
  110.     if (ring -> eof || (*p=readline(iobuf)) == NULL) {
  111.         *p=EOF; 
  112.         ring -> eof=YES;
  113.         }
  114.     return c;
  115. }
  116.  
  117. initring(ring,iobuf)    /*initialise by filling ring buffer*/
  118. struct aring *ring;
  119. char *iobuf;
  120. {
  121.     int i;
  122.     char *c, *readline();
  123.  
  124.     ring -> eof=NO;
  125.     for (i=0; i<MAXSYNC; i++) {
  126.         if (ring -> eof || (c=readline(iobuf)) == NULL)
  127.         {
  128.             c=EOF; 
  129.             ring -> eof=YES;
  130.             }
  131.         ring -> buf[i]=c;
  132.         }
  133.     ring -> ringp=MAXSYNC;
  134. }
  135.  
  136. char *peep(ring,p)        /*return pointer to p'th next line*/
  137. struct aring *ring;
  138. int p;
  139. {
  140.     return ring -> buf[ (p - 1 + ring -> ringp) % MAXSYNC];
  141. }
  142.  
  143. pline(c)        /*print a line and free its storage space*/
  144. char *c;
  145. {
  146.     lineincr();
  147.     if (c != EOF) {
  148.         puts(c);
  149.         free(c);
  150.         }
  151.     else puts("--End Of File--");
  152.     putchar('\n');
  153. }
  154.  
  155. putchar(c)        /*print a printable character,
  156.               on printer too if putboth
  157.               or into report file if reporting*/
  158. int c;
  159. {
  160.     if (c < ' ' && (c != '\n' && c != '\r' && c != '\t')) return c;
  161.     if (c == '\n')putchar('\r');
  162.     if (putboth) {
  163.         if (c == '\t') while (++col%8) bdos(5,' ');
  164.         else {
  165.             if (c == '\n') col=0;
  166.             else col++;
  167.             bdos(5,c);
  168.             }
  169.         }
  170.     if (report) {
  171.         if (putc(c,repbuf) == ERROR) {
  172.             report=NO; 
  173.             puts("Error on writing report file\n");
  174.             exit();
  175.             }
  176.         }
  177.     else bdos(2,c);
  178.     return c;
  179. }
  180. lineincr()    /*increment line count; pause, print header
  181.                 if at end of screen*/
  182. {
  183.     if (line >= 22 && pause) {
  184.         puts("\t\t\t\t\t[Return]/Quit > ");
  185.         if (tolower(getchar()) == 'q') exit();
  186.         putchar('\n');
  187.         line=0;
  188.         }
  189.     if (!line++)printf("\t<<<<< Comparison of %s with %s >>>>>\n",
  190.     name1,name2);
  191. }
  192.  
  193. scompare(s1,s2)        /*compare strings for equality (0=same)*/
  194. char *s1,*s2;
  195. {
  196.     if (s1 == s2) return 0; /*both EOF*/
  197.     if (s1 == EOF || s2 == EOF) return 1;
  198.     return (white ? txtcmp(s1,s2) : strcmp(s1,s2));
  199. }
  200.  
  201. txtcmp(s1,s2)        /*compare strings, matching white space with
  202.             any white space chars*/
  203. char *s1,*s2;
  204. {
  205.     while (*s1 && *s2) {
  206.         if ((*s1 == ' ' || *s1 == '\t') && (*s2 == ' ' || *s2 == '\t')) {
  207.             while (*s1 == ' ' || *s1 == '\t') s1++;
  208.             while (*s2 == ' ' || *s2 == '\t') s2++;
  209.             if (!*s1 && !*s2) return 0;
  210.             }
  211.         if (*s1++ != *s2++) return 1;
  212.         }
  213.     while (*s1 == ' ' || *s1 == '\t') *s1++;
  214.     while (*s2 == ' ' || *s2 == '\t') *s2++;
  215.     return *s1-*s2;
  216. }
  217.         
  218. search(ringa,ringb,c)    /*try to get files back into sync. when mismatch
  219.                 has been found.  If successful, returns
  220.                 number of lines to skip to get back to
  221.                 synchronity, else 0*/
  222. struct aring *ringa, *ringb;
  223. char *c;
  224. {
  225.     int outofsync, match, i;
  226.  
  227.     if (nosync) return 0;
  228.     for (outofsync=1; outofsync < MAXSYNC; outofsync++)
  229.         /*look forward for a match*/
  230.         if (!scompare(peep(ringb,outofsync),c)) break;
  231.     /*break when found*/
  232.  
  233.     if (outofsync == MAXSYNC) return 0;
  234.     else   /* match found, look for sync more */
  235.     for (match=1; match < sync; match++)
  236.         if ( (i=outofsync+match) >= MAXSYNC ||
  237.         scompare(peep(ringa,match),peep(ringb,i)))
  238.             break;    /*break if no match*/
  239.  
  240.     if (match == sync) return outofsync;
  241.     /*return pos of start of sync matches*/
  242.     else return 0;
  243. }
  244.  
  245. blksrch(ringa,ringb,n)    /*try to get files back into sync. when mismatch
  246.                 at n'th character after current
  247.                 has been found.  If successful, returns
  248.                 number of lines to skip to get back to
  249.                 synchronity, else 0*/
  250. struct aring *ringa, *ringb;
  251. int n;
  252. {
  253.     int outofsync, match, i;
  254.  
  255.     if (nosync) return 0;
  256.     for (outofsync=1; outofsync+n < MAXSYNC; outofsync++)
  257.         /*look forward for a match*/
  258.         if (!scompare(peep(ringb,outofsync+n),peep(ringa,n)))
  259.             break; /*break when found*/
  260.  
  261.     if (outofsync == MAXSYNC) return 0;
  262.     else   /* match found, look for sync more */
  263.     for (match=1; match < sync; match++)
  264.         if ( (i=outofsync+match+n) >= MAXSYNC ||
  265.         scompare(peep(ringa,match+n),peep(ringb,i)))
  266.             break;    /*break if no match*/
  267.     if (match == sync) return outofsync;
  268.     /*return pos of start of sync matches*/
  269.     else return 0;
  270. }
  271.  
  272. addr(p)        /*gets a line number from a command line parameter;
  273.             returns it*/
  274. char **p;
  275. {
  276.     int n;
  277.  
  278.     n=0;
  279.     while (*(++*p))    n=10*n+**p-'0';
  280.     return n;
  281. }
  282.  
  283. args(argc,argv)        /*read args from command line*/
  284.  
  285. int argc;
  286. char *argv[];
  287. {
  288.     int i;
  289.     char *p;
  290.  
  291.     putboth=listing=nosync=report=NO;
  292.     pause=parity=white=YES;
  293.     start=sync=0; 
  294.     stop=MAXINT;
  295.     printf("[Textcom v%d.%d]\n",VERSION/10,VERSION%10);
  296.     if (argc < 3) {
  297.         puts("Usage: TEXTCOM  oldfile  newfile [options]\n");
  298.         puts("\t(compares an old version of a text file with a new)\n");
  299.         puts("\n   Options:\n");
  300.         puts("       -Sdd  to syncronise up to dd lines\n");
  301.         printf("           (use -S for strict compare). Default = %d\n",
  302.         DEFLSYNC);
  303.         puts("       -L  to list differences on printer and screen\n");
  304.         puts("       -Rname to create a file, 'name', reporting differences\n");
  305.         puts("       -C  to continue without pauses at the end of each screen\n");
  306.         puts("       -Fxxxx to display differences only from ");
  307.         puts("line xxxx\n");
  308.         puts("       -Txxxx to stop comparison at line xxxx\n");
  309.         puts("       -P  to include parity bit in comparison\n");
  310.         puts("       -W  to match a white space char only with the same one\n");
  311.         exit();
  312.         }
  313.  
  314.     for (i=3; i < argc; i++) {
  315.         switch( *(p=++argv[i]) ) {
  316.         case 'L': 
  317.             listing=YES; 
  318.             break;
  319.         case 'C': 
  320.             pause=NO; 
  321.             break;
  322.         case 'S': 
  323.             sync=addr(&p); 
  324.             nosync=!sync;
  325.             if (sync >= MAXSYNC) {
  326.                 printf("Max. sync is %u",MAXSYNC); 
  327.                 exit();
  328.                 }
  329.             break;
  330.         case 'R': 
  331.             report=YES; 
  332.             strcpy(repname,++p); 
  333.             break;
  334.         case 'F': 
  335.             start=addr(&p); 
  336.             break;
  337.         case 'T': 
  338.             stop=addr(&p); 
  339.             break;
  340.         case 'P': 
  341.             parity=NO; 
  342.             break;
  343.         case 'W':
  344.             white=NO;
  345.             break;
  346.         default : 
  347.             puts("Unknown option -"); 
  348.             puts(p); 
  349.             exit();
  350.             }
  351.         }
  352.     if (stop < start) {
  353.         printf("-From (%d) is not less than -To (%d)\n",start,stop);
  354.         exit();
  355.         }
  356.     if(listing || report) pause=NO;
  357.     if(sync == 0) sync=DEFLSYNC;
  358.  
  359.     strcpy(name1,argv[1]);
  360.     if (fopen(name1,fbuf1) == ERROR) {
  361.         puts("Can't find "); 
  362.         puts(name1); 
  363.         exit();
  364.         }
  365.     initring(&ring1,fbuf1);
  366.  
  367.     strcpy(name2,argv[2]);
  368.     if (fopen(name2,fbuf2) == ERROR) {
  369.         puts("Can't find "); 
  370.         puts(name2); 
  371.         exit();
  372.         }
  373.     initring(&ring2,fbuf2);
  374.  
  375.     if (report) {
  376.         if (fcreat(repname,repbuf) == ERROR) {
  377.             puts("Can't open "); 
  378.             puts(repname); 
  379.             exit();
  380.             }
  381.         }
  382.     putboth=listing;
  383. }
  384.  
  385. printdif()    /*resynchronise and print differences */
  386. {
  387.     int unsync, i;
  388.  
  389.     if ( (unsync=search(&ring2,&ring1,c2))) { /*note deletion*/
  390.         lineincr();
  391.         puts(">>>>>> ");
  392.         if (unsync == 1) printf("Line %u",line1+1);
  393.         else printf("Lines %u to %u",line1,line1+unsync);
  394.         printf(" of %s deleted from %s\n",name1,name2);
  395.         for (i=0; i<unsync; i++) {
  396.             pline(c1);
  397.             c1=getline(&ring1,fbuf1);
  398.             }
  399.         line1+=unsync;
  400.         }
  401.     else {
  402.         if ((unsync=search(&ring1,&ring2,c1))) {
  403.             /*note insertion*/
  404.             lineincr();
  405.             puts(">>>>>> ");
  406.             if (unsync == 1) printf("Line %u",line2+1);
  407.             else printf("Lines %u to %u",line2,
  408.                 line2+unsync);
  409.             printf(" of %s inserted\n",name2);
  410.             for (i=0; i<unsync; i++) {
  411.                 pline(c2);
  412.                 c2=getline(&ring2,fbuf2);
  413.                 }
  414.             line2+=unsync;
  415.             }
  416.         else { /*look for a block of changed lines*/
  417.  
  418.             for (unsync=1; unsync < MAXSYNC &&
  419.         scompare(peep(&ring1,unsync),peep(&ring2,unsync)) &&
  420.         !blksrch(&ring1,&ring2,unsync) &&
  421.         !blksrch(&ring2,&ring1,unsync);
  422.         unsync++);
  423.                 if (unsync == 1) {
  424.                 lineincr();
  425.                 printf(">>>>>> Line %u of %s changed to line %u of %s\n",
  426.                 line1,name1,line2,name2);
  427.                 pline(c1,line1);
  428.                 pline(c2,line2);
  429.                 }
  430.             else {
  431.                 lineincr();
  432.                 printf(">>>>>> Lines %u to %u of %s ....\n",
  433.                 line1,line1+unsync-1,name1);
  434.                 for (i=1; i<=unsync; i++) {
  435.                     pline(c1);
  436.                     if (i < unsync)c1=getline(&ring1,fbuf1);
  437.                     }
  438.                 lineincr();
  439.                 printf(">>>>>> .... changed to lines %u to %u of %s\n",
  440.                 line2,line2+unsync-1,name2);
  441.                 for (i=1; i<=unsync; i++) {
  442.                     pline(c2);
  443.                     if(i < unsync)c2=getline(&ring2,fbuf2);
  444.                     }
  445.                 line1+=--unsync; 
  446.                 line2+=unsync;
  447.                 }
  448.             }
  449.         }
  450. }
  451.  
  452. main(argc,argv)
  453.  
  454. int argc;
  455. char *argv[];
  456.  
  457. {
  458.     _allocp=NULL;
  459.     args(argc,argv);
  460.  
  461.     line1=line2=1;
  462.     line=col=0;
  463.     different=NO;
  464.  
  465.     do {
  466.         c1=getline(&ring1,fbuf1);
  467.         c2=getline(&ring2,fbuf2);
  468.  
  469.         if (scompare(c1,c2)) { /*lines are different*/
  470.             different=YES;
  471.             if (line1 >= start && line2 >= start) printdif();
  472.             }
  473.         else {
  474.             if (c1 != EOF) free(c1);
  475.             if (c2 != EOF) free(c2);
  476.             }
  477.         line1++; 
  478.         line2++;
  479.         } 
  480.     while ((c1 != EOF || c2 != EOF) && (line1 < stop || line2 < stop));
  481.  
  482.     if (different) puts("Files differ");
  483.     else printf("Files %s and %s are identical",name1,name2);
  484.     if (start != 0 || stop != MAXINT)
  485.         printf(" between lines %u and %u\n",start,min(line1,line2));
  486.     else putchar('\n');
  487.  
  488.     if (report) { /*flush and close report file*/
  489.         putc(CPMEOF,repbuf); 
  490.         fflush(repbuf); 
  491.         fclose(repbuf);
  492.         }
  493. }
  494.     puts(">>>>>> ");
  495.             if (unsync == 1) printf("Line %u",line2+1);