home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / epmsmp.zip / E3COMP.E < prev    next >
Text File  |  1995-01-16  |  19KB  |  528 lines

  1. /****************************************************************************
  2.  
  3.                         ┌───────────────────────┐
  4.                         │  E 3 C O M P . E      │
  5.                         └───────────────────────┘
  6.  
  7.                                     By Dave Burton
  8.                                     Modified by Bryan Lewis
  9.  
  10.   Modified  to work  in messy-desk  mode as  well as  tiled, by calling
  11.   pnextfile() instead of doing 'nextfile' for F10.
  12.  
  13.   This  is  a  very  useful  file-compare  utility  written  in E3 language.
  14.   Searches two files for differences, centers  the lines  so you  can easily
  15.   flicker back and forth to see what's different.  Fast!  Dave  Burton put a
  16.   lot of effort into speed optimization, simplifying loops and the like.  He
  17.   is unable to spend any more time on this, but was happy to let  me convert
  18.   it to E3 (I  needed it  for myself!)  and put  it on  PROCS.  Don't expect
  19.   support.
  20.  
  21.   Special features are: ability to get back in  sync after  being thrown off
  22.   be an added or deleted line; and a LOOSEMATCH  command to  tell it  not to
  23.   consider leading / trailing spaces in the comparison.
  24.  
  25.   Modified by Larry Margolis to be slightly more efficient, and to work with
  26.   EPM.
  27.  
  28.  
  29.   Defines functions:
  30.  
  31.   COMPARE.  Will scan through a pair  of files  looking for  the first point
  32.       where the two files differ.  If they differ on the current lines (that
  33.       is, after you've found a difference), pressing COMPARE repeatedly will
  34.       "flicker"   between the   two files   (do alternating   next-file  and
  35.       previous-file operations).  The current line in the two  files will
  36.   be centered in mid-screen so differences will be readily apparent.
  37.  
  38.   SYNC.  Will attempt to resynchronize the two files, by looking ahead up to
  39.       11 lines in each file for a place where  the two  files are identical.
  40.       Repeatedly pressing SYNC will "flicker" between the two files.
  41.  
  42.   FLICKER.  Alternate between the compared files
  43.  
  44.   The two files to be  compared should  be the  Current file  and the "next"
  45.   file in the ring (the "next" file is the one made active  by a  single F10
  46.   keypress).  Put the cursor on identical lines in the two files, then press
  47.   the COMPARE key to skip to the first difference in each file.
  48.  
  49.   Press COMPARE a few times more to see the differences.  You  can then move
  50.   the two cursors down to  identical lines  again "re-synchronize"  with the
  51.   SYNC key, and repeat the process.
  52.  
  53.   Note that COMPARE "remembers" which  of the  two files  is currently being
  54.   displayed; after you re-synchronize and compare again,  you will  again be
  55.   looking at the  "first" of  the two  files, no  matter which  one you were
  56.   looking at when you started the compare.  However, using  the  "NEXT-FILE"
  57.   (F10) or  the "LAST-FILE" (Alt-F10)  key  causes this  recollection  to be
  58.   "forgotten".
  59.  
  60.   A faster way to re-synchronize (if you are  within 11  lines of  a pair of
  61.   matching lines) is to simply press the SYNC key.  Then  press it  again to
  62.   see the other file (flicker).
  63.  
  64.   Defines:
  65.      Ctrl-C
  66.         As the COMPARE key
  67.      Ctrl-S
  68.         As the SYNC key
  69.  
  70.       LOOSEMATCH - Causes E3COMP to  consider lines  to be  matching even if
  71.           indented differently, or if one of them has trailing blanks.
  72.  
  73.       EXACTMATCH - Causes E3COMP to  consider lines  to be  matching only if
  74.           the lines are identical.
  75.  
  76.      Modifies the NEXT-FILE (F10) definition        (adjust to suit)
  77.      Modifies the LAST-FILE (Alt-F10) definition    (adjust to suit)
  78.  
  79. *****************************************************************************/
  80.  
  81. ; Updated to optionally add Compare and Sync to the action bar, and not use keys.
  82.  
  83. compile if not defined(EPM)
  84.    include 'stdconst.e'
  85.    tryinclude 'mycnf.e'
  86. compile endif
  87.  
  88. compile if not defined(USE_MENU_FOR_E3COMP)
  89. const USE_MENU_FOR_E3COMP = 1
  90. compile endif
  91.  
  92. definit                     /* 1=LOOSE matching as default. 0 for EXACT  */
  93.    universal c_inexact
  94. compile if defined(my_C_INEXACT)
  95.    c_inexact=my_C_INEXACT
  96. compile else
  97.    c_inexact=1
  98. compile endif
  99. compile if USE_MENU_FOR_E3COMP
  100.    menuname='default'
  101.    buildsubmenu menuname, 30, 'Compare!', 'e3comp',     0, 0
  102.    buildsubmenu menuname, 31, 'Sync!', 'sync',       0, 0
  103.    showmenu menuname
  104. compile endif
  105.  
  106. compile if not USE_MENU_FOR_E3COMP
  107. def c_c='e3comp'        /*     define C-C to be the "compare" key   */
  108. compile endif
  109.  
  110. defc e3comp
  111.    universal c_flicker_flag, c_line1, c_line2
  112.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  113.    universal c_limit1, c_limit2, c_got1, c_got2
  114.    universal c_success, c_inexact, fileid1, fileid2
  115.  
  116.   c_saved_flicker_flag=c_flicker_flag
  117.   call ppini_comp()
  118.   if (c_line1<>c_line2) or ((not c_inexact)and(c_line1/==c_line2)) or (c_lineno1=.last) or (c_lineno2=c_last2) then
  119.       if c_saved_flicker_flag=c_flicker_flag then
  120.           call ppflicker()  /* switch to other file */
  121.       endif
  122.   else
  123.       sayerror 'comparing...'
  124.       /* loop, looking for first difference */
  125.       if (.last-c_lineno1) < (c_last2-c_lineno2) then
  126.           /* trick to make loop faster by removing "c_lineno1<.last" test */
  127.           c_last2=c_lineno2+.last-c_lineno1
  128.       endif
  129.       /*
  130.          This function is an "unwound" version of the next loop; by adding
  131.          this, we sped up the search substantially.  You could, however, remove
  132.          fast_compare_loop completely without affecting anything except speed.
  133.       */
  134.       call fast_compare_loop()
  135.       /* here is the slow version of the loop, to finish up: */
  136.       while (c_lineno2<c_last2) and ((c_line1==c_line2)or(c_inexact and(c_line1=c_line2))) do
  137.          /* "and (c_lineno1<.last)" deleted due to trick, above */
  138.          c_lineno1=c_lineno1+1
  139.          c_lineno2=c_lineno2+1
  140.          getline c_line1,c_lineno1,fileid1
  141.          getline c_line2,c_lineno2,fileid2
  142.       endwhile
  143.       c_last2=fileid2.last /* done with loop, so restore to normal value */
  144.       if (c_lineno1=.last) and (c_lineno2=c_last2) and ((c_line1==c_line2)or(c_inexact and(c_line1=c_line2))) then
  145.          sayerror 'no differences'
  146.       else
  147.          sayerror 1
  148.       endif
  149.       call ppend_comp()
  150.   endif
  151.   call ppcenter_screen()
  152. /* end of compare key definition */
  153.  
  154.  
  155. /* inner loop for COMPARE definition -- unwound for maximum speed */
  156.  
  157. defproc fast_compare_loop
  158.    universal c_flicker_flag, c_line1, c_line2
  159.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  160.    universal c_limit1, c_limit2, c_got1, c_got2
  161.    universal c_success, c_inexact, fileid1, fileid2
  162.  
  163.    if c_inexact then
  164.       while ((c_last2-c_lineno2)>=4) and (c_line1=c_line2) do
  165.         getline c_line1,c_lineno1+1,fileid1
  166.         getline c_line2,c_lineno2+1,fileid2
  167.         if c_line1<>c_line2 then
  168.             c_lineno1=c_lineno1+1
  169.             c_lineno2=c_lineno2+1
  170.         else
  171.             getline c_line1,c_lineno1+2,fileid1
  172.             getline c_line2,c_lineno2+2,fileid2
  173.             if c_line1<>c_line2 then
  174.                 c_lineno1=c_lineno1+2
  175.                 c_lineno2=c_lineno2+2
  176.             else
  177.                 getline c_line1,c_lineno1+3,fileid1
  178.                 getline c_line2,c_lineno2+3,fileid2
  179.                 if c_line1<>c_line2 then
  180.                     c_lineno1=c_lineno1+3
  181.                     c_lineno2=c_lineno2+3
  182.                 else
  183.                     c_lineno1=c_lineno1+4
  184.                     c_lineno2=c_lineno2+4
  185.                     getline c_line1,c_lineno1,fileid1
  186.                     getline c_line2,c_lineno2,fileid2
  187.                 endif
  188.             endif
  189.         endif
  190.         /* end of the unwound inexact-match while loop */
  191.       endwhile
  192.    else
  193.       while ((c_last2-c_lineno2)>=4) and (c_line1==c_line2) do
  194.         getline c_line1,c_lineno1+1,fileid1
  195.         getline c_line2,c_lineno2+1,fileid2
  196.         if c_line1/==c_line2 then
  197.             c_lineno1=c_lineno1+1
  198.             c_lineno2=c_lineno2+1
  199.         else
  200.             getline c_line1,c_lineno1+2,fileid1
  201.             getline c_line2,c_lineno2+2,fileid2
  202.             if c_line1/==c_line2 then
  203.                 c_lineno1=c_lineno1+2
  204.                 c_lineno2=c_lineno2+2
  205.             else
  206.                 getline c_line1,c_lineno1+3,fileid1
  207.                 getline c_line2,c_lineno2+3,fileid2
  208.                 if c_line1/==c_line2 then
  209.                     c_lineno1=c_lineno1+3
  210.                     c_lineno2=c_lineno2+3
  211.                 else
  212.                     c_lineno1=c_lineno1+4
  213.                     c_lineno2=c_lineno2+4
  214.                     getline c_line1,c_lineno1,fileid1
  215.                     getline c_line2,c_lineno2,fileid2
  216.                 endif
  217.             endif
  218.         endif
  219.         /* end of the unwound exact-match while loop */
  220.       endwhile
  221.    endif
  222.  
  223.  
  224.  
  225. /****************************************************************************
  226. We have 7 private variables in this def, four of which have double uses:
  227.      c_count1 & c_count2  =  saved positions after a "no match" compare,
  228.                              line counter while scanning
  229.      c_limit1 & c_limit2  =  saved file sizes after a "no match" compare,
  230.                              limit to line counter while scanning
  231.      c_got1 & c_got2      =  "got a match" positions while scanning
  232.      c_success               "got one" flag while scanning
  233. ****************************************************************************/
  234.  
  235. compile if not USE_MENU_FOR_E3COMP
  236. def c_s='sync'               /*  Define C-S to be the sync key     */
  237. compile endif
  238.  
  239. defc sync
  240.    universal c_flicker_flag, c_line1, c_line2
  241.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  242.    universal c_limit1, c_limit2, c_got1, c_got2
  243.    universal c_success, c_inexact, fileid1, fileid2
  244.  
  245.   c_saved_flicker_flag=c_flicker_flag
  246.   call ppini_comp()
  247.   if (c_count1=c_lineno1)and(c_count2=c_lineno2)and(c_limit1=.last)and(c_limit2=c_last2) then
  248.       /* repeated "no match within 11 lines" flickers */
  249.       if c_saved_flicker_flag=c_flicker_flag then
  250.           call ppflicker()  /* switch to other file */
  251.       endif
  252.   elseif (c_line1==c_line2) or (c_inexact and(c_line1=c_line2)) then
  253.       /* if we are already synchronized, then flicker */
  254.       c_count1=0  /* we have a match, so clear saved "not found" position */
  255.       if c_saved_flicker_flag=c_flicker_flag then
  256.           call ppflicker()  /* switch to other file */
  257.       endif
  258.   else
  259.       c_limit1=11 /* we look ahead only 11 lines (lest, it be too slow) */
  260.       if (.last-c_lineno1) < c_limit1 then
  261.           c_limit1=(.last-c_lineno1)
  262.       endif
  263.       c_got1=0
  264.       c_got2=0
  265.       c_count1=0
  266.       c_success=0
  267.       while c_count1 <= c_limit1 do
  268.           c_count2=0
  269.           if c_success then
  270.               c_limit2=(c_got1+c_got2)-c_count1-1
  271.               if c_limit2 > 11 then
  272.                   c_limit2=11
  273.               endif
  274.           else
  275.               c_limit2=11
  276.           endif
  277.           if (c_last2-c_lineno2) < c_limit2 then
  278.               c_limit2=(c_last2-c_lineno2)
  279.           endif
  280.           /* we've carefully calculated the limits so that we'll only find */
  281.           /* "better" matches than those we've already found.  A "better"  */
  282.           /* match is one which is nearer to the current lines (not as     */
  283.           /* far down).  More precisely, the best match is the one with a  */
  284.           /* minimum sum of the two lines numbers.                         */
  285.           if c_inexact then
  286.            /* note: we move the "c_inexact" test outside the loop, for speed */
  287.               while c_count2 <= c_limit2 do
  288.                   getline c_line1,c_lineno1+c_count1,fileid1
  289.                   getline c_line2,c_lineno2+c_count2,fileid2
  290.                   if c_line1=c_line2 then /*inexact*/
  291.                       if length(c_line1) > 0 then
  292.                           c_got1=c_count1
  293.                           c_got2=c_count2
  294.                           c_success=1
  295.                           c_limit2=c_count2 /* break out of the inner while */
  296.                       endif
  297.                   endif
  298.                   c_count2=(c_count2+1)
  299.               endwhile
  300.           else
  301.               while c_count2 <= c_limit2 do
  302.                   getline c_line1,c_lineno1+c_count1,fileid1
  303.                   getline c_line2,c_lineno2+c_count2,fileid2
  304.                   if c_line1==c_line2 then /*exact*/
  305.                       if length(c_line1) > 0 then
  306.                           c_got1=c_count1
  307.                           c_got2=c_count2
  308.                           c_success=1
  309.                           c_limit2=c_count2 /* break out of the inner while */
  310.                       endif
  311.                   endif
  312.                   c_count2=(c_count2+1)
  313.               endwhile
  314.           endif
  315.           c_count1=(c_count1+1)
  316.       endwhile
  317.       if c_success then
  318.           c_lineno1=c_lineno1+c_got1
  319.           c_lineno2=c_lineno2+c_got2
  320.           c_count1=0 /* we found a match, so clear saved "no match" position */
  321.       else
  322.           /* save state (for flicker next time he presses c-F5 flickers) */
  323.           c_count1=c_lineno1
  324.           c_count2=c_lineno2
  325.           c_limit1=.last
  326.           c_limit2=c_last2
  327.           sayerror 'No match within 11 lines'
  328.       endif
  329.       call ppend_comp()
  330.   endif
  331.   call ppcenter_screen()
  332. /* end of synchronize key definition */
  333.  
  334.  
  335. ;def c_f10=            /*     define Ctrl-F10 to be the "flicker" key  */
  336. ;def a_f10=            /*     define Alt-F10 to be the "flicker" key   */
  337. ;def c_w=             /*     define Ctrl-W to be the "flicker" key    */
  338. ;   universal c_flicker_flag, c_line1, c_line2
  339. ;   universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  340. ;   universal c_limit1, c_limit2, c_got1, c_got2
  341. ;   universal c_success, c_inexact, fileid1, fileid2
  342. ;   call ppflicker()
  343. ;   c_count1=0  /* discard the saved "no match" state */
  344.  
  345.  
  346. /* modify the "NEXT-FILE" definition to reset the flicker-counter */
  347. compile if EVERSION < '4.10'          -- Early E doesn't support enh. kbd.
  348. def f10,c_N
  349. compile else
  350. def f10,f12,c_N
  351. compile endif
  352.    universal c_flicker_flag, c_line1, c_line2
  353.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  354.    universal c_limit1, c_limit2, c_got1, c_got2
  355.    universal c_success, c_inexact, fileid1, fileid2
  356.  
  357. compile if EPM
  358.    nextfile
  359. compile else
  360.    call pnextfile() -- Do this instead of nextfile, so will work with messy-desk or tiled.
  361. compile endif
  362.    c_flicker_flag=0  /* 1st file is current file */
  363.    c_count1=0  /* discard the saved "no match" state */
  364.  
  365.  
  366. /* modify "LAST-FILE" definition to reset the flicker-counter */
  367. compile if EVERSION < '4.10'          -- Early E doesn't support enh. kbd.
  368. def a_f10,c_P=
  369.  compile else
  370. def a_f10,F11,c_P=  -- a_F10 is usual E default; F11 for enh. kbd, c_P for EPM.
  371.  compile endif
  372.    universal c_flicker_flag, c_line1, c_line2
  373.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  374.    universal c_limit1, c_limit2, c_got1, c_got2
  375.    universal c_success, c_inexact, fileid1, fileid2
  376. compile if EPM
  377.    prevfile
  378. compile else
  379.    call pprevfile()
  380. compile endif
  381.    c_flicker_flag=0  /* 1st file is current file */
  382.    c_count1=0  /* discard the saved "no match" state */
  383.  
  384.      /* Common stuff for compare and synchronize keys */
  385.  
  386. defproc ppini_comp
  387.    universal c_flicker_flag, c_line1, c_line2
  388.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  389.    universal c_limit1, c_limit2, c_got1, c_got2
  390.    universal c_success, c_inexact, fileid1, fileid2
  391.  
  392.     /* if Ctrl-F10 was pressed an odd number of times, switch to 1st file */
  393.  
  394.    getfileid fileid
  395.    if fileid<>fileid1 & fileid <> fileid2 then  -- Neither of the original files; reset.
  396.       c_flicker_flag=0  /* 1st file is current file */
  397.       c_count1=0  /* discard the saved "no match" state */
  398.    endif
  399.    if c_flicker_flag then
  400. compile if EPM
  401.       prevfile
  402. compile else
  403.       call pprevfile()
  404. compile endif
  405.    endif
  406.    c_flicker_flag=0  /* 1st file is current file */
  407.  
  408.      /* get initial lines & file lengths to compare */
  409.  
  410.    getfileid fileid1
  411.    if (not .line) and .last then 1; endif
  412.    c_lineno1=.line
  413.    getline c_line1
  414. compile if EPM
  415.       nextfile
  416. compile else
  417.       call pnextfile('Q')
  418. compile endif
  419.    getfileid fileid2
  420.    if (not .line) and .last then 1; endif
  421.    c_lineno2=.line
  422.    c_last2=.last
  423.    getline c_line2
  424. compile if EPM
  425.       prevfile
  426. compile else
  427.       call pprevfile('Q')
  428. compile endif
  429.    call ppcenter_screen()
  430.  
  431.  
  432. /* called after a compare, to set the current lines of the two files
  433.    to c_lineno1 and c_lineno2, and center them on the screen */
  434.  
  435. defproc ppend_comp
  436.    universal c_flicker_flag, c_line1, c_line2
  437.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  438.    universal c_limit1, c_limit2, c_got1, c_got2
  439.    universal c_success, c_inexact, fileid1, fileid2
  440.  
  441.    .line=c_lineno1
  442. compile if EPM
  443.    nextfile
  444. compile else
  445.    call pnextfile('Q')
  446. compile endif
  447.    .line=c_lineno2
  448.    call ppcenter_screen()
  449. compile if EPM
  450.    prevfile
  451. compile else
  452.    call pprevfile('Q')
  453. compile endif
  454.    call ppcenter_screen()
  455.  
  456.  
  457. /* flicker procedure -- call to switch to other file */
  458.  
  459. defproc ppflicker
  460.    universal c_flicker_flag, c_line1, c_line2
  461.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  462.    universal c_limit1, c_limit2, c_got1, c_got2
  463.    universal c_success, c_inexact, fileid1, fileid2
  464.  
  465.    if c_flicker_flag then
  466.       c_lineno2=.line
  467. compile if EPM
  468.       prevfile
  469. compile else
  470.       call pprevfile()
  471. compile endif
  472.       c_lineno1=.line
  473.       call ppcenter_screen()
  474.       c_flicker_flag=0  /* 1st file is current file */
  475.    else
  476.       c_lineno1=.line
  477. compile if EPM
  478.       nextfile
  479. compile else
  480.       call pnextfile()
  481. compile endif
  482.       c_lineno2=.line
  483.       call ppcenter_screen()
  484.       c_flicker_flag=1  /* 2nd file is current file */
  485.    endif
  486. /* end of flicker procedure definition */
  487.  
  488.  
  489. /***********************************************************************
  490. Procedure to center current line on screen, except that if either
  491.    file is currently "at" a line number less than 1/2 of a screen,
  492.    then the current position is set at that line (so they'll line up
  493.    properly when you "flicker" with ctrl-F10).  Note that I haven't
  494.    been able to make this work for small files.  Sigh.
  495.  
  496. ************************************************************************/
  497. defproc ppcenter_screen
  498.    universal c_flicker_flag, c_line1, c_line2
  499.    universal c_lineno1, c_lineno2, c_last2, c_count1, c_count2
  500.    universal c_limit1, c_limit2, c_got1, c_got2
  501.    universal c_success, c_inexact, fileid1, fileid2
  502.  
  503.    c_temp=.line
  504.    .cursory=.windowheight%2   /* % for integer division */
  505.    if (c_lineno1<.cursory) then
  506.       .cursory=c_lineno1+1
  507.    endif
  508.    if (c_lineno2<.cursory) then
  509.       .cursory=c_lineno2+1
  510.    endif
  511.    if (.cursory <= 1) then
  512.       .cursory=2
  513.    endif
  514.    .cursorx=1
  515.    c_temp
  516.  
  517.  
  518. defc loose, loosematch=
  519.    universal c_inexact
  520.    c_inexact=1
  521.    sayerror 'COMPARE will use loose matching now.'
  522.  
  523. defc exact, exactmatch=
  524.    universal c_inexact
  525.    c_inexact=0
  526.    sayerror 'COMPARE will use exact matching now.'
  527.  
  528.