Article 13572 of alt.sources:
Archive-name: compare
Submitted-by: grunes@ssdd.nrl.navy.mil (Mitchell R Grunes)

********compare: Simple Text File Comparator********

The most recent changes: changed the way errors are marked to make
them easier to search for when using certain text editors.

If you like or dislike it, send e-mail.  DON'T POST to alt.sources.
Bug reports must include sample files on which it failed.

CONTENTS:
   compare.for     Fortran language source code.
---compare.for--------------CUT HERE-----------------------------
c-----------------------------------------------------------------------
        program COMPARE                 ! Compare 2 text files.
                                        ! Has a lot more options than
                                        !  MS-DOS FC command, and
                                        !  is much more readable than
                                        !  Unix diff.
c Program by Mitchell R Grunes, for his own use.  Revised 8/25/96.
c I am writing this again from scratch, to simplify the previously
c  partially working mess.
c Note: This doesn't quite count mismatches right--some are counted twice.

c Side by side prints: uses | on good matches, * on bad.
c Vertically aligned prints: uses B| for lines that occur in both files,
c  1| or 2| for those which occur in file 1 or 2.

c This program was written in FORTRAN, for historic reasons.

        implicit none
        character*70  file1,file2       ! Name of files to compare
        character*70  file3             ! Name of output file; blank=none
        logical       ToFile            ! Output is to a file?
        character*1   YN                ! Y/N answers
        logical       UseSpace          ! Pay attention to spaces?
        logical       CaseMatch         ! Upper and lower case match?
        logical       MatchBlank        ! Match all blank lines?
        logical       Match1            ! Single line matches?
        logical       UseNum            ! Use line numbers?
        logical       PrintAll          ! Print all lines?
        integer       nCols             ! Number of cols; 0=not side by side
        logical       EOF1,EOF2         ! Files at end yet?
        integer       ioff1,ioff2       ! Offsets between index and true line
                                        !  numbers
        integer       j1,j2             ! Current line indexes for each file.
        integer       last1,last2       ! Last read indexes
        integer       MaxBuf            ! Size of text buffers
        parameter     (MaxBuf=500)
        character*256 a1(MaxBuf),a2(MaxBuf),line ! Text buffers
        integer       j                 ! Row looping
        integer       i1,i2             ! Distance searching for matches
        integer       nError            ! # of mismatches
        integer       MaxSearch         ! Maximum Search Size
        logical       eq
        external      eq
        character*256 blank
        data          blank/' '/

        print*,'Name of first file to compare:'
        read(*,'(a70)')file1
        print*,file1
        open(1,file=file1,form='formatted',access='sequential',
     &   status='old')
        print*,'Name of second file to compare:'
        read(*,'(a70)')file2
        print*,file2
        open(2,file=file2,form='formatted',access='sequential',
     &   status='old')
        print*,'Name of output file (blank=screen):'
        read(*,'(a70)')file3
        print*,file3
        ToFile=file3.gt.' '
        if(ToFile)
     &   open(3,file=file3,form='formatted',access='sequential',
     &   status='new')

        print*,'Pay attention to spaces (N?):'
        read(*,'(a1)')YN
        UseSpace=YN.eq.'Y'.or.YN.eq.'y'
        print*,UseSpace

        print*,'Should upper and lower case match (N?):'
        read(*,'(a1)')YN
        CaseMatch=YN.eq.'Y'.or.YN.eq.'y'
        print*,CaseMatch

        print*,'Blank lines match even if before/after don''t(N?):'
        read(*,'(a1)')YN
        MatchBlank=YN.eq.'Y'.or.YN.eq.'y'
        print*,MatchBlank

        print*,'Isolated lines match even if before/after don''t(N?):'
        read(*,'(a1)')YN
        Match1=YN.ne.'N'.and.YN.ne.'n'
        print*,Match1

        print*,'Print line numbers (N?):'
        read(*,'(a1)')YN
        UseNum=YN.eq.'Y'.or.YN.eq.'y'
        print*,UseNum

        print*,'Print all lines (Y?):'
        read(*,'(a1)')YN
        PrintAll=YN.ne.'N'.and.YN.ne.'n'
        print*,PrintAll

        print*,'Total columns available (for side by side display)'
        print*,'    or 0 (to allign lines vertically)'
        print*,':'
        read*,nCols
        print*,nCols
        if(nCols.gt.0)nCols=(nCols-1)/2

        print*,'Maximum Search Length (150?):'
        read*,MaxSearch
        if(MaxSearch.le.0)MaxSearch=150
        MaxSearch=max(2,min(MaxBuf,MaxSearch))
        print*,MaxSearch

        ioff1=-2                         ! Initialize
        ioff2=-2
        j1=1
        j2=1
        a1(1)=file1
        a2(1)=file2
        line='....,....1....,....2....,....3....,....4....,....5'//
     &       '....,....6....,....7....,....8....,....9....,....0'
        line(101:200)=line(1:100)
        line(201:256)=line(1:56)
        if(nCols.eq.0)then
          a1(2)=line(1:80)
          a2(2)=line(1:80)
        else
          if(UseNum)then
            a1(2)=line(1:nCols-6)
            a2(2)=line(1:nCols-6)
          else
            a1(2)=line(1:nCols)
            a2(2)=line(1:nCols)
          endif
        endif
        last1=2
        last2=2
        eof1=.false.
        eof2=.false.
        nError=-2
        if(nCols.gt.0)nError=-1

1       if(j1.le.last1.and.j2.le.last2)then     ! Handle immediate matches
          if(eq(a1(j1),a2(j2),UseSpace,CaseMatch))then
            call PrintLine(a1(j1),a2(j2),ioff1+j1,ioff2+j2,
     &       UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
            j1=j1+1
            j2=j2+1
            goto 1
          endif
        endif

        if(ToFile.and.j1+ioff1.gt.0.and.j1.le.last1.and.j2.le.last2)
     &   print 100,nError+1,j1+ioff1,j2+ioff2,a1(j1)(1:40),a2(j2)(1:40)
100     format(1x,'Mismatch',i5,' at lines',2i7,(t39,a40))
101     format(1x,'   match',5x,' at lines',2i7,(t39,a40))
        if(j1.gt.1)then                         ! Move lines to start of buffers
          do j=j1,last1
            a1(j-j1+1)=a1(j)
          enddo
          last1=last1-(j1-1)
          ioff1=ioff1+(j1-1)
          j1=1
          if(ToFile.and.EOF1.and.last1.lt.1)
     &     print*,'-----End of File 1 line',ioff1-1,
     &     ' Mismatches=',nError
        endif

        if(j2.gt.1)then
          do j=j2,last2
            a2(j-j2+1)=a2(j)
          enddo
          last2=last2-(j2-1)
          ioff2=ioff2+(j2-1)
          j2=1
          if(ToFile.and.EOF2.and.last2.lt.1)
     &     print*,'-----End of File 2 line',ioff2-1,
     &     ' Mismatches=',nError
        endif

        dowhile(last1.lt.MaxBuf.and..not.EOF1)  ! Fill read buffers
          last1=last1+1
          call RdLine(1,a1(last1),EOF1)
        enddo

        dowhile(last2.lt.MaxBuf.and..not.EOF2)  ! Fill read buffers
          last2=last2+1
          call RdLine(2,a2(last2),EOF2)
        enddo

        if(last1.le.0.and.last2.le.0)then
          print*,'---END at file1 line',ioff1-1,
     &     ' file2 line',ioff2-1,' Mismatches=',nError
          if(ToFile)write(3,*)'---END at file1 line',ioff1-1,
     &     ' file2 line',ioff2-1,' Mismatches=',nError
          stop 'Finished!'

        elseif(last1.le.0)then                  ! If file1 at end,
          do j=1,last2                          !  print rest of file 2
            call PrintLine(blank,a2(j),-99,ioff2+j,
     &       UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
            j2=j2+1
          enddo
          goto 1
        elseif(last2.le.0)then                  ! If file2 at end,
          do j=1,last1                          !  print rest of file 1
            call PrintLine(a1(j),blank,ioff1+j,-99,
     &       UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
            j1=j1+1
          enddo
          goto 1
        else                                    ! Mismatch--look for next
          if(eq(a1(1),a2(1),UseSpace,CaseMatch))then
            if(ToFile.and.j1+ioff1.gt.0)
     &       print 101,j1+ioff1,j2+ioff2,a1(1)(1:40)
            goto 1                              !  match.
          endif
          do i1=2,min(MaxSearch,max(last1,last2))
            if(ToFile.and.i1/25*25.eq.i1)print*,'Searching ahead ',i1
            do i2=1,i1
              if(i1.le.last1.and.i2.le.last2)then ! Try matches with jj2.le.jj1
                if(eq(a1(i1),a2(i2),UseSpace,CaseMatch))then! Match!
                  j1=i1
                  j2=i2
                  if(ToFile.and.j1+ioff1.gt.0)
     &             print 101,j1+ioff1,j2+ioff2,a1(j1)(1:40)
                  if(j1.ge.last1.or.j2.ge.last2.or.
     &             (a1(j1).ne.' '.and.Match1).or.
     &             MatchBlank.or.Match1)goto 150
                  if(eq(a1(j1+1),a2(j2+1),UseSpace,CaseMatch))goto 150
                endif
              endif
              if(i2.le.last1.and.i1.le.last2)then ! Try matches with jj1.le.jj2
                if(eq(a1(i2),a2(i1),UseSpace,CaseMatch))then! Match!
                  j1=i2
                  j2=i1
                  if(ToFile.and.j1+ioff1.gt.0)
     &             print 101,j1+ioff1,j2+ioff2,a1(j1)(1:40)
                  if(j1.ge.last1.or.j2.ge.last2.or.
     &             (a1(j1).ne.' '.and.Match1).or.
     &             MatchBlank.or.Match1)goto 150
                  if(eq(a1(j1+1),a2(j2+1),UseSpace,CaseMatch))goto 150
                endif
              endif
            enddo
          enddo

          j1=min(last1,min(MaxSearch/2,MaxBuf/3)) ! No match exists anywhere
          j2=min(last2,min(MaxSearch/2,MaxBuf/3))
          if(j1.eq.1)j1=2
          if(j2.eq.1)j2=2
          if((j1.eq.last1.and.EOF1).or.(j2.eq.last2.and.EOF2))then
            j1=2
            j2=2
          endif

150       if(nCols.eq.0)then            ! Vertical allign
            do j=1,j1-1                 ! Print mismatched file1 lines
              call PrintLine(a1(j),blank,ioff1+j,-99,
     &         UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
            enddo
            do j=1,j2-1                 ! Print mismatched file2 lines
              call PrintLine(blank,a2(j),-99,ioff2+j,
     &         UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
            enddo
          else                          ! Side by side print
            do j=1,min(j1,j2)-1         ! Print initial mismatches
              call PrintLine(a1(j),a2(j),ioff1+j,ioff2+j,
     &         UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
            enddo
            if(j1.ge.j2)then
              do j=j2,j1-1              ! Print extra file 1 lines
                call PrintLine(a1(j),blank,ioff1+j,-99,
     &           UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
              enddo
            else
              do j=j1,j2-1              ! Print extra file 2 lines
                call PrintLine(blank,a2(j),-99,ioff2+j,
     &           UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
              enddo
            endif
          endif
        endif
        goto 1
        end
c-----------------------------------------------------------------------
        subroutine RdLine(iunit,line,EOF)  ! Read 1 line
        implicit none
c ------------------------------INPUTS-------------------------------
        integer       iunit                     ! Read unit #
c ------------------------------OUTPUTS------------------------------
        character*256 line                      ! Output line
        logical       EOF                       ! EOF flag
c -------------------------------LOCAL-------------------------------
        character*256 TempLine
        character*1   c
        integer       i1,i2

        read(iunit,'(a256)',end=99)line         ! Read line

        TempLine=' '                            ! Expand tabs to spaces
        i2=1
        do i1=1,256
          if(i2.le.256)then
            c=line(i1:i1)
            if(c.eq.char(9))then
              i2=(i2+7)/8*8+1
            elseif(c.ge.' ')then
              TempLine(i2:i2)=c
              i2=i2+1
            endif
          endif
        enddo
        line=TempLine

        return

99      EOF=.true.
        line='-------END OF FILE-------'
        end

c-----------------------------------------------------------------------
                                                ! Print lines from files
        subroutine PrintLine(a1,a2,j1,j2,
     &       UseNum,UseSpace,CaseMatch,ToFile,PrintAll,nCols,nError)
        implicit none
c ------------------------------INPUTS-------------------------------
        character*256 a1,a2                     ! Lines to print
        integer       j1,j2                     ! Corrosponding line #'s
                                                !  -1 =File names
                                                !  -99=None for that file
        logical       UseNum,UseSpace,CaseMatch,ToFile,PrintAll ! See mainline program.
        integer       nCols,nError
c ------------------------------OUTPUTS------------------------------
c       none
c -------------------------------LOCAL-------------------------------
        character*256 aa1,aa2                   ! Local copies of a1,a2
        character*256 line                      ! Will be printed
        character*1   Flag
        integer       Length,i
        logical       eq
        external      eq

        if(ToFile.and.max(j1,j2)/100*100.eq.max(j1,j2)
     &   .and.j1.ne.0)then
          if(j1.gt.-99.and.j2.gt.-99)
     &     print
     &     '(12H File1 line ,i6,12H File2 line ,i6,12H Mismatches=,i5)',
     &     j1,j2,nError
          if(j1.eq.-99.and.j2.gt.-99)
     &     print
     &     '(12H            ,6x,12H File2 line ,i6,12H Mismatches=,i5)',
     &        j2,nError
          if(j1.gt.-99.and.j2.eq.-99)
     &     print
     &     '(12H File1 line ,i6,12H            ,6x,12H Mismatches=,i5)',
     &     j1,   nError
        endif

        if(.not.PrintAll.and.eq(a1,a2,UseSpace,CaseMatch)
     &   .and.j1.gt.-99.and.j2.gt.-99) return

        aa1=a1                                  ! Make local copies for changes
        aa2=a2

        flag='*'                                ! No Match (| for match)
        if(eq(aa1,aa2,UseSpace,CaseMatch).and.j1.gt.-99.and.j2.gt.-99)
     &   flag='|'
        if(flag.eq.'*')nError=nError+1

1       if(UseNum)then                          ! Prepend Line Numbers
          line=' '
          if(j1.gt.0)write(line(1:6),'(i5,1h|)')j1
          if(j1.eq.0)line='Line#'
          line(7:256)=aa1

          aa1=line
          line=' '
          if(j2.gt.0)write(line(1:6),'(i5,1h|)')j2
          if(j2.eq.0)line='Line#'
          line(7:256)=aa2
          aa2=line

          if(nCols.eq.0.and.j1.gt.0.and.j2.gt.0)
     &     aa1(1:5)=' BOTH'
        endif

        if(j2.eq.-99)then                       ! Just print file 1
          if(nCols.eq.0)then                    !   Vertical allignment
            line='1|'//aa1
          else                                  !   Side by side print
            line=aa1(1:nCols)//'*'
          endif
        elseif(j1.eq.-99)then                   ! Just print file 2
          if(nCols.eq.0)then                    !   Vertical allignment
            line='2|'//aa2
          else                                  !   Side by side print
            line=' '
            line(1+nCols:256)='*'//aa2(1:nCols)
          endif
        else                                    ! Print Both Files
          if(nCols.eq.0)then                    !   Vertical allignment
            line='B|'//aa1
            if(flag.ne.'|')then
              print*,'flag=',flag
              print*,'j1,j2=',j1,j2
              print*,'a1=',a1
              print*,'a2=',a2
              print*,'aa1=',aa1
              print*,'aa2=',aa2
              stop 'Mismatch at bad point'
            endif
          else                                  !   Side by side print
            line=aa1(1:nCols)//flag//aa2(1:nCols)
          endif
        endif

        Length=1                                ! Determine printed line length
        do i=1,256
          if(line(i:i).ne.' ')Length=i
        enddo

        if(flag.eq.'*'.or.PrintAll)then         ! Print Line
          if(ToFile)then
            write(3,'(a)')line(1:length)
          else
            print*,line(1:length)
          endif
        endif

        if(nCols.gt.0)then                      ! Some lines are too long
          if(aa1(nCols+1:256).ne.' '.or.aa2(nCols+1:256).ne.' ')then
            line=aa1(nCols+1:256)
            aa1='>'//line
            line=aa2(nCols+1:256)
            aa2='>'//line
            goto 1
          endif
        endif

        end
c-----------------------------------------------------------------------
        logical function eq(a,b,UseSpace,CaseMatch)      ! Is a=b?
        implicit none
        character*256 a,b,aa,bb
        character*1 c
        logical       UseSpace,CaseMatch
        integer       i,j,k
        if(UseSpace)then
          eq=a.eq.b
        else
          j=0
          k=0
          aa=' '
          bb=' '
          do i=1,256
            if(a(i:i).ne.' ')then
              j=j+1
              c=a(i:i)
              if(CaseMatch.and.c.ge.'a'.and.c.le.'z')
     &         c=char(ichar(c)+ichar('A')-ichar('a'))
              aa(j:j)=c
            endif
            if(b(i:i).ne.' ')then
              k=k+1
              c=b(i:i)
              if(CaseMatch.and.c.ge.'a'.and.c.le.'z')
     &         c=char(ichar(c)+ichar('A')-ichar('a'))
              bb(k:k)=c
            endif
          enddo
          eq=aa.eq.bb
        endif
        end
-----------------------------CUT HERE----------------------------
-------------------------------------------------------------------------
Mitchell R Grunes, grunes@nrlvax.nrl.navy.mil.  Opinions are mine alone.



