home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 5 Edit / 05-Edit.zip / epmmac2.zip / EPMCOMP.E < prev    next >
Text File  |  1995-09-21  |  17KB  |  444 lines

  1. /****************************************************************************
  2.  
  3.                         ┌───────────────────────┐
  4.                         │  E P M C O M P . E    │
  5.                         └───────────────────────┘
  6.  
  7.                            By Larry Margolis
  8.                            Modelled after E3COMP (by Dave Burton & Bryan Lewis)
  9.  
  10.   This is  a very useful  file-compare  utility  written  in the E language.
  11.   Searches two files for differences, centers  the lines  so you  can easily
  12.   flicker back and forth to see what's different.  Fast!
  13.  
  14.   Enhancements over E3COMP are:  the comparison is done via an internal opcode,
  15.   for greatly increased speed.  The comparands are kept track of by fileid, so
  16.   no need to modify the prevfile / nextfile keys.  In fact, once a comparison
  17.   is started, other files can be added to the ring between the two being
  18.   compared and EPMCOMP will continue to work.  Can handle case insensitive
  19.   comparisons as well as ones ignoring leading / trailing spaces.
  20.  
  21.   The remainder of the comments are from E3COMP.E:
  22.  
  23.   Special features are: ability to get back in  sync after  being thrown off
  24.   by an added or deleted line; and a LOOSEMATCH  command to  tell it  not to
  25.   consider leading / trailing spaces in the comparison.
  26.  
  27.  
  28.   Defines functions:
  29.  
  30.   COMPARE.  Will scan through a pair  of files  looking for  the first point
  31.       where the two files differ.  If they differ on the current lines (that
  32.       is, after you've found a difference), pressing COMPARE repeatedly will
  33.       "flicker"   between the   two files   (do alternating   next-file  and
  34.       previous-file operations).  The current line in the two  files will
  35.   be centered in mid-screen so differences will be readily apparent.
  36.  
  37.   SYNC.  Will attempt to resynchronize the two files, by looking ahead up to
  38.       11 lines in each file for a place where  the two  files are identical.
  39.       Repeatedly pressing SYNC will "flicker" between the two files.
  40.  
  41.   FLICKER.  Alternate between the compared files
  42.  
  43.   The two files to be  compared should  be the  Current file  and the "next"
  44.   file in the ring (the "next" file is the one made active  by a  single F10
  45.   keypress).  Put the cursor on identical lines in the two files, then press
  46.   the COMPARE key to skip to the first difference in each file.
  47.  
  48.   Press COMPARE a few times more to see the differences.  You  can then move
  49.   the two cursors down to  identical lines  again "re-synchronize"  with the
  50.   SYNC key, and repeat the process.
  51.  
  52.   Note that COMPARE "remembers" which  of the  two files  is currently being
  53.   displayed; after you re-synchronize and compare again,  you will  again be
  54.   looking at the  "first" of  the two  files, no  matter which  one you were
  55.   looking at when you started the compare.  However, using  the  "NEXT-FILE"
  56.   (F10) or  the "LAST-FILE" (Alt-F10)  key  causes this  recollection  to be
  57.   "forgotten".
  58.  
  59.   A faster way to re-synchronize (if you are  within 11  lines of  a pair of
  60.   matching lines) is to simply press the SYNC key.  Then  press it  again to
  61.   see the other file (flicker).
  62.  
  63.   Defines:
  64.      Ctrl-C
  65.         As the COMPARE key
  66.      Ctrl-S
  67.         As the SYNC key
  68.  
  69.       LOOSEMATCH - Causes EPMCOMP to  consider lines  to be  matching even if
  70.           indented differently, or if one of them has trailing blanks.
  71.  
  72.       EXACTMATCH - Causes EPMCOMP to  consider lines  to be  matching only if
  73.           the lines are identical.
  74.  
  75.      Modifies the NEXT-FILE (F10) definition        (adjust to suit)
  76.      Modifies the LAST-FILE (Alt-F10) definition    (adjust to suit)
  77.  
  78. *****************************************************************************/
  79.  
  80. ; Updated to optionally add Compare and Sync to the action bar, and not use keys.
  81.  
  82. compile if EVERSION < '6.01a'
  83.   *** EPMCOMP requires EPM version 6.01a or above.
  84. compile endif
  85.  
  86. compile if not defined(EPM)
  87.    include 'stdconst.e'
  88.    tryinclude 'mycnf.e'
  89. compile endif
  90.  
  91. compile if not defined(USE_MENU_FOR_EPMCOMP)
  92.  compile if defined(USE_MENU_FOR_E3COMP)
  93. const USE_MENU_FOR_EPMCOMP = USE_MENU_FOR_E3COMP
  94.  compile else
  95. const USE_MENU_FOR_EPMCOMP = 1
  96.  compile endif
  97. compile endif
  98.  
  99. #define IGNORE_SPACES 1
  100. #define IGNORE_CASE   2
  101. #define SINGLE_LINE   4
  102.  
  103. #define STATUS_INIT      0
  104. #define STATUS_DIFFERENT 1
  105. #define STATUS_SYNCHED   2
  106. #define STATUS_NOMATCH   3
  107. #define STATUS_DONE      4
  108.  
  109.    define MENUADD_ID = 0
  110. ;compile if USE_MENU_FOR_EPMCOMP
  111.  compile if defined(STD_MENU_NAME)
  112.   compile if STD_MENU_NAME = 'OVSHMENU.E'  -- This is one we know about...
  113.    define MENUADD_ID = 2      -- Under View
  114.   compile endif
  115.   compile if STD_MENU_NAME = 'FEVSHMNU.E'  -- This is the only other one we know about...
  116.    define MENUADD_ID = 3      -- Under View
  117.   compile endif
  118.  compile else  -- STD_MENU_NAME not defined; we're using STDMENU.E:
  119.    define MENUADD_ID = 3      -- Under Search
  120.  compile endif  -- defined(STD_MENU_NAME)
  121. ;compile endif  -- USE_MENU_FOR_EPMCOMP
  122.  
  123. definit                     /* 1=LOOSE matching as default. 0 for EXACT  */
  124.    universal epmcomp_flags, epmcomp_status
  125.    universal defaultmenu, activemenu
  126.  
  127. compile if defined(my_EPMCOMP_FLAGS)
  128.    epmcomp_flags = my_EPMCOMP_FLAGS
  129. compile else
  130.    epmcomp_flags = IGNORE_SPACES
  131. compile endif
  132.    epmcomp_status = STATUS_INIT
  133. compile if USE_MENU_FOR_EPMCOMP
  134.    deletemenu defaultmenu, 6, 0, 0  -- delete the existing Help menu (we want it to stay at the right)
  135.    buildsubmenu defaultmenu, 32, 'Compare!', 'epmcomp',  0, 0
  136.    buildsubmenu defaultmenu, 33, 'Sync!',    'sync',     0, 0
  137. compile endif
  138.  
  139. compile if MENUADD_ID
  140.    buildmenuitem defaultmenu, MENUADD_ID, 3100, 'EPMcomp',     \1 || 'EPMCOMP options', 17, 0
  141.    buildmenuitem defaultmenu, MENUADD_ID, 3101, '~Strip spaces', 'epmcompflags ts' || \1 || 'Toggle loose or exact comparison for EPMCOMP.', 0, 0
  142.    buildmenuitem defaultmenu, MENUADD_ID, 3102, '~Ignore case', 'epmcompflags tc' || \1 || 'Toggle respect or ignore case for EPMCOMP.', 0, 0
  143.    buildmenuitem defaultmenu, MENUADD_ID, 3103, \0, '', 4, 0
  144.    buildmenuitem defaultmenu, MENUADD_ID, 3104, '~Reset comparands', 'epmcompflags reset' || \1 || 'Tell EPMCOMP to forget the files being compared.', 32769, 0
  145. compile endif
  146.  
  147. compile if USE_MENU_FOR_EPMCOMP
  148.    call readd_help_menu()
  149. compile elseif MENUADD_ID
  150.    call maybe_show_menu()
  151. compile endif
  152.  
  153. compile if not USE_MENU_FOR_EPMCOMP
  154. def c_c='epmcomp'        /*     define C-C to be the "compare" key   */
  155. compile endif
  156.  
  157. defc epmcomp
  158.    universal epmcomp_status, epmcomp_flags, epmcomp_fid1, epmcomp_fid2
  159.  
  160. ;  getfileid start_fid
  161.    start_fid = .currentview_of_file
  162.    call ppini_comp()
  163.    if wordpos(epmcomp_status, STATUS_DIFFERENT STATUS_NOMATCH STATUS_DONE) then
  164. ;     getfileid this_fid
  165.       this_fid = .currentview_of_file
  166.       if this_fid = start_fid then
  167.          call ppflicker()  /* switch to other file */
  168.       endif
  169.    else
  170.       sayerror 'Comparing...'
  171.       res = filecompare(epmcomp_fid1, epmcomp_fid2, epmcomp_flags)
  172.       if res = 0 then
  173.          sayerror 'No differences.'
  174.          epmcomp_status = STATUS_DONE
  175.       elseif res = 2 then
  176.          sayerror 'Reached end of only one file.'
  177.          epmcomp_status = STATUS_DIFFERENT
  178.       else
  179.          sayerror 0
  180.          epmcomp_status = STATUS_DIFFERENT
  181.       endif
  182.       call ppend_comp()
  183.    endif
  184. /* end of compare key definition */
  185.  
  186.  
  187.  
  188. /****************************************************************************
  189. We have 7 private variables in this def, four of which have double uses:
  190.      c_count1 & c_count2  =  saved positions after a "no match" compare,
  191.                              line counter while scanning
  192.      c_limit1 & c_limit2  =  saved file sizes after a "no match" compare,
  193.                              limit to line counter while scanning
  194.      c_got1 & c_got2      =  "got a match" positions while scanning
  195.      c_success               "got one" flag while scanning
  196. ****************************************************************************/
  197. compile if not defined(SYNC_LIMIT)
  198.    const SYNC_LIMIT = 21
  199. compile endif
  200.  
  201. compile if not USE_MENU_FOR_EPMCOMP
  202. def c_s='sync'               /*  Define C-S to be the sync key     */
  203. compile endif
  204.  
  205. defc sync
  206.    universal epmcomp_status, epmcomp_flags, epmcomp_fid1, epmcomp_fid2
  207.  
  208. ;  getfileid start_fid
  209.    start_fid = .currentview_of_file
  210.    call ppini_comp()
  211.    if wordpos(epmcomp_status, STATUS_NOMATCH STATUS_SYNCHED STATUS_DONE) then
  212. ;     getfileid this_fid
  213.       this_fid = .currentview_of_file
  214.       if this_fid = start_fid then
  215.          call ppflicker()  /* switch to other file */
  216.       endif
  217.    else  -- status = STATUS_DIFFERENT or STATUS_INIT
  218.       startline1 = epmcomp_fid1.line
  219.       startline2 = epmcomp_fid2.line
  220.       c_limit1 = min(.last-.line, SYNC_LIMIT) /* we look ahead only a few lines (lest, it be too slow) */
  221.       c_got1=0
  222.       c_got2=0
  223.       c_success=0
  224.       flags = epmcomp_flags + SINGLE_LINE
  225.       for c_count1 = 0 to c_limit1
  226.          epmcomp_fid1.lineg = startline1 + c_count1
  227.          if c_success then
  228.             c_limit2 = min((c_got1+c_got2)-c_count1-1, min(epmcomp_fid2.last-startline2, SYNC_LIMIT))
  229.          else
  230.             c_limit2 = min(epmcomp_fid2.last-startline2, SYNC_LIMIT)
  231.          endif
  232.          /* we've carefully calculated the limits so that we'll only find */
  233.          /* "better" matches than those we've already found.  A "better"  */
  234.          /* match is one which is nearer to the current lines (not as     */
  235.          /* far down).  More precisely, the best match is the one with a  */
  236.          /* minimum sum of the two lines numbers.                         */
  237.          for c_count2 = 0 to c_limit2
  238.             epmcomp_fid2.lineg = startline2 + c_count2
  239.             if not filecompare(epmcomp_fid1, epmcomp_fid2, flags) then
  240.                if length(textline(.line)) then
  241.                    c_got1=c_count1
  242.                    c_got2=c_count2
  243.                    c_success=1
  244.                    leave  /* break out of the inner for loop */
  245.                endif
  246.             endif
  247.          endfor
  248.       endfor
  249.       if c_success then
  250.          epmcomp_fid1.lineg = startline1 + c_got1
  251.          epmcomp_fid2.lineg = startline2 + c_got2
  252.          epmcomp_status = STATUS_SYNCHED
  253.       else
  254.          epmcomp_fid1.lineg = startline1
  255.          epmcomp_fid2.lineg = startline2
  256.          epmcomp_status = STATUS_NOMATCH
  257.          sayerror 'No match within' SYNC_LIMIT 'lines'
  258.       endif
  259.       call ppend_comp()
  260.    endif
  261. /* end of synchronize key definition */
  262.  
  263.  
  264. ;def c_f10=            /*     define Ctrl-F10 to be the "flicker" key  */
  265. ;def a_f10=            /*     define Alt-F10 to be the "flicker" key   */
  266. ;def c_w=             /*     define Ctrl-W to be the "flicker" key    */
  267. ;   universal epmcomp_status, epmcomp_flags, epmcomp_fid1, epmcomp_fid2
  268. ;   call ppflicker()
  269.  
  270.  
  271.      /* Common stuff for compare and synchronize keys */
  272.  
  273. defproc ppini_comp
  274.    universal epmcomp_status, epmcomp_flags, epmcomp_fid1, epmcomp_fid2
  275.    universal epmcomp_line1, epmcomp_line2, epmcomp_linetext1, epmcomp_linetext2
  276.  
  277.     /* If in second file, switch to 1st file */
  278.  
  279.    if epmcomp_status <> STATUS_INIT then  -- check if we're on one of the files of interest.
  280. ;     getfileid fileid
  281.       fileid = .currentview_of_file
  282.       if fileid = epmcomp_fid2 then
  283.          rc = 0
  284.          activatefile epmcomp_fid1
  285.          if rc = -260 then  -- Invalid fileid
  286.             epmcomp_status = STATUS_INIT
  287.          endif
  288.       elseif fileid<>epmcomp_fid1 then  -- Neither of the original files; reset.
  289.          epmcomp_status = STATUS_INIT
  290.       else  -- We're on first file, just verify that second is still valid.
  291.          if not validatefileid(epmcomp_fid2) then
  292.             epmcomp_status = STATUS_INIT
  293.          endif
  294.       endif
  295.    endif
  296.  
  297.      /* get initial line numbers & file ids to compare */
  298.  
  299.    if epmcomp_status = STATUS_INIT then
  300. ;     getfileid epmcomp_fid1
  301.       epmcomp_fid1 = .currentview_of_file
  302.       nextfile
  303. ;     getfileid epmcomp_fid2
  304.       epmcomp_fid2 = .currentview_of_file
  305.       prevfile
  306.    endif
  307.    if (not epmcomp_fid1.line) and epmcomp_fid1.last then epmcomp_fid1.lineg=1; endif
  308.    if (not epmcomp_fid2.line) and epmcomp_fid2.last then epmcomp_fid2.lineg=1; endif
  309.  
  310.    if epmcomp_status <> STATUS_INIT then
  311.       status_has_changed = 0
  312.       if epmcomp_fid1.line <> epmcomp_line1 then  -- If user manually moved around,
  313.          status_has_changed = 1                   -- we don't know the current state
  314.       elseif epmcomp_fid2.line <> epmcomp_line2 then
  315.          status_has_changed = 1
  316.       elseif textline(.line) /== epmcomp_linetext1 then  -- If user modified the current
  317.          status_has_changed = 1                          -- line, we also have to recheck.
  318.       else
  319.          getline line, epmcomp_line2, epmcomp_fid2
  320.          if line /== epmcomp_linetext2 then
  321.             status_has_changed = 1
  322.          endif
  323.       endif
  324.       if status_has_changed then
  325.          if filecompare(epmcomp_fid1, epmcomp_fid2, epmcomp_flags+SINGLE_LINE) then
  326.             epmcomp_status = STATUS_DIFFERENT
  327.          else
  328.             epmcomp_status = STATUS_SYNCHED  -- or STATUS_DONE; do we need to differentiate?
  329.          endif
  330.          -- Note:  epmcomp_line1 and/or epmcomp_line2 are wrong now, but we don't care.
  331.       endif
  332.    endif
  333.  
  334. ;  if epmcomp_status = STATUS_INIT then
  335. ;     epmcomp_line1 = epmcomp_fid1.line
  336. ;     epmcomp_line2 = epmcomp_fid2.line
  337. ;  endif
  338. ;  call ppcenter_screen()
  339.  
  340.  
  341. /* called after a Compare or Sync, to center the 2 files on the screen */
  342.  
  343. defproc ppend_comp
  344.    universal epmcomp_status, epmcomp_flags, epmcomp_fid1, epmcomp_fid2
  345.    universal epmcomp_line1, epmcomp_line2, epmcomp_linetext1, epmcomp_linetext2
  346.  
  347.    activatefile epmcomp_fid2
  348.    call ppcenter_screen()
  349.    activatefile epmcomp_fid1
  350.    call ppcenter_screen()
  351.  
  352.    epmcomp_line1 = epmcomp_fid1.line
  353.    epmcomp_line2 = epmcomp_fid2.line
  354.    getline epmcomp_linetext1, epmcomp_line1, epmcomp_fid1
  355.    getline epmcomp_linetext2, epmcomp_line2, epmcomp_fid2
  356.  
  357.  
  358. /* flicker procedure -- call to switch to other file */
  359.  
  360. defproc ppflicker
  361.    universal epmcomp_status, epmcomp_flags, epmcomp_fid1, epmcomp_fid2
  362.  
  363. ;  getfileid fid
  364.    fid = .currentview_of_file
  365.    if fid = epmcomp_fid2 then
  366.       activatefile epmcomp_fid1
  367.    else
  368.       activatefile epmcomp_fid2
  369.    endif
  370.    call ppcenter_screen()
  371. /* end of flicker procedure definition */
  372.  
  373.  
  374. /***********************************************************************
  375. Procedure to center current line on screen, except that if either
  376.    file is currently "at" a line number less than 1/2 of a screen,
  377.    then the current position is set at that line (so they'll line up
  378.    properly when you "flicker" with ctrl-F10).  Note that I haven't
  379.    been able to make this work for small files.  Sigh.
  380.  
  381. ************************************************************************/
  382. defproc ppcenter_screen
  383.    universal epmcomp_status, epmcomp_flags, epmcomp_fid1, epmcomp_fid2
  384.  
  385.    temp=.line
  386.    .cursory=.windowheight%2   /* % for integer division */
  387.    if (epmcomp_fid1.line<.cursory) then
  388.       .cursory=epmcomp_fid1.line+1
  389.    endif
  390.    if (epmcomp_fid2.line<.cursory) then
  391.       .cursory=epmcomp_fid2.line+1
  392.    endif
  393.    if (.cursory <= 1) then
  394.       .cursory=2
  395.    endif
  396.    .cursorx=1
  397.    temp
  398.  
  399.  
  400. defc loose, loosematch=
  401.    universal epmcomp_flags
  402.    if not (epmcomp_flags//2) then
  403.       epmcomp_flags = epmcomp_flags + IGNORE_SPACES
  404.    endif
  405.    sayerror 'COMPARE will use loose matching now.'
  406.  
  407. defc exact, exactmatch=
  408.    universal epmcomp_flags
  409.    if epmcomp_flags//2 then
  410.       epmcomp_flags = epmcomp_flags - IGNORE_SPACES
  411.    endif
  412.    sayerror 'COMPARE will use exact matching now.'
  413.  
  414.  
  415. compile if MENUADD_ID
  416. defc epmcompflags
  417.    universal epmcomp_status, epmcomp_flags
  418.    if arg(1) = 'ts' then  -- toggle Strip Spaces
  419.       flag = epmcomp_flags // 2
  420.       if flag then
  421.          epmcomp_flags = epmcomp_flags - IGNORE_SPACES
  422.       else
  423.          epmcomp_flags = epmcomp_flags + IGNORE_SPACES
  424.       endif
  425.    elseif arg(1) = 'tc' then  -- toggle Strip Spaces
  426.       flag = epmcomp_flags % 2 // 2
  427.       if flag then
  428.          epmcomp_flags = epmcomp_flags - IGNORE_CASE
  429.       else
  430.          epmcomp_flags = epmcomp_flags + IGNORE_CASE
  431.       endif
  432.    elseif arg(1) = 'reset' then
  433.       epmcomp_status = STATUS_INIT
  434.    endif
  435.  
  436. defc menuinit_3100
  437.    universal epmcomp_status, epmcomp_flags
  438.    flag = epmcomp_flags // 2
  439.    SetMenuAttribute( 3101, 8192, not flag)
  440.    flag = epmcomp_flags % 2 // 2
  441.    SetMenuAttribute( 3102, 8192, not flag)
  442.    SetMenuAttribute( 3104, 16384, epmcomp_status<>STATUS_INIT)
  443. compile endif
  444.