home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / MADTRB18.ZIP / DIFF.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1985-06-01  |  11.3 KB  |  452 lines

  1.  
  2. {
  3.     HDIF : a file difference finder
  4. }
  5.  
  6. program diff;
  7.  
  8. const
  9.     VERSION = '1.0';
  10.     MAXFILE = 1000;         { lines in largest old/new file }
  11.     MAXOVER = 1001;         { Allow for a sentinal Value }
  12.     MAXCHAR = 127;          { Max chars/line }
  13.     MAXSYM  = 2053;         { Symbol Table Size - Prime > 2*MAXFILE - }
  14.     TOPSYM  = 2052;         { Max Index of SYM array }
  15.  
  16. {
  17.     CP/M storage = 6 * MAXOVER + 8 * MAXSYM + (128 * UNIQUE LINES)
  18. }
  19.  
  20. type
  21.     symnum  = 0..TOPSYM;        { Type of symbol table indices }
  22.     linenum = 1..MAXOVER;        {  indices into OA and NA }
  23.     linecnt = 0..MAXOVER;
  24.  
  25. { Compiler dependent - }
  26.  
  27.     ltext   = string[MAXCHAR];
  28.  
  29.     linerec = record
  30.         matched : boolean;        { True when matched with other file }
  31.         index   : integer;  { index to symtab of other file }
  32.     end;
  33.  
  34.     symrec = record                { one symbol table entry }
  35.         hashval : integer;        { hash value of this entry }
  36.         lineval : ^ltext;        { address of text string }
  37.         Oline   : linenum;        { index to line in Old File OA }
  38.         Ocount  : 0..2;        { how many occurances in old file 0,1,many }
  39.         Ncount  : 0..2;     { " for new file }
  40.     end;
  41.  
  42. var
  43.     oldmax, newmax : linecnt;        { linecount of each file }
  44.     oldfile,newfile,
  45.     diffile        : text;                { files used }
  46.     OA,NA          : array[linenum] of linerec;
  47.     ST             : array[symnum ] of symrec;
  48.     kill_line_1    : boolean;        { used by levee driver }
  49.  
  50. {
  51.     Compute a 15 bit signature based on all the characters
  52.     in the line.  Pick a symtab entry based on the hash value
  53.     and return the symbol index.  If there is a collision
  54.     (2 lines hash to same entry), just overflow by 1 till
  55.     no more collision.  The full hash value is saved in the
  56.     entry for quick discovery of unequal lines.
  57. }
  58.  
  59.     function store(var t : ltext ) : symnum;
  60.     label try_entry, empty_ent, done;
  61.     var s : symnum;
  62.         h : integer;
  63.  
  64.         function hash(var t : ltext) : integer;
  65.         const hashlim = 16256;    { Maxint div 4 - 127 }
  66.         var q   : integer;
  67.             len : 0..MAXCHAR;
  68.         begin
  69.         q := 0;
  70.         for len := length(t) downto 1 do begin
  71.             q := q + ord(t[len]);
  72.             { shift left to 14 bits then wrap }
  73.             if q < hashlim then
  74.             q := q + q    { << 1 bit pos }
  75.             else
  76.             q := 1 + q div 2;
  77.         end;
  78.         hash := q;
  79.         end;  { hash }
  80.  
  81.     begin { store }
  82.         h := hash(t);
  83.         s := h mod maxsym;        { initial probe of symtab }
  84.         {*
  85.          *    find duplicate line or a vacant entry starting at ST[s].
  86.          *}
  87. try_entry:  if (ST[s].hashval < 0) then goto empty_ent;
  88.         {*
  89.          *    entry is used - is it a duplicate?
  90.          *}
  91.         if (ST[s].hashval = h) then { fast test }
  92.         if (ST[s].lineval^ = t) then goto done;
  93.         s := (s+1) mod MAXSYM;  { try next entry }
  94.         goto try_entry;
  95. empty_ent:  with st[s] do begin
  96.         hashval := h;
  97.         new(lineval);
  98.         lineval^ := t;
  99.         end;
  100. done:       store := s;
  101.     end;
  102.  
  103.     {*
  104.      *        PASS 1                read old file,
  105.      *                        store its lines in the symtab
  106.      *                        count their occurances in Ocount for each entry
  107.      *}
  108.  
  109.      procedure pass1;
  110.      var o : linecnt;
  111.          s : symnum;
  112.          t : ltext;
  113.      begin
  114.      o := 0;
  115.      repeat
  116.          readln(oldfile,t);
  117.          o := o + 1;
  118.          s := store(t);
  119.          with ST[s] do begin
  120.          Oline := o;
  121.          if Ocount <> 2 then Ocount := succ(Ocount);
  122.          end;
  123.          with OA[o] do begin
  124.          matched := false;
  125.          index := s;
  126.          end;
  127.      until (eof(oldfile) or (o = maxfile));
  128.      with OA[o+1] do begin { create sentinal saying END }
  129.          matched := true;
  130.          index := MAXOVER;
  131.      end;
  132.      oldmax := o;
  133.      end;
  134.  
  135.  
  136.     {*
  137.      *        PASS 2                read new file,
  138.      *                        store its lines in the symtab
  139.      *                        count their occurances in Ncount for each entry
  140.      *}
  141.  
  142.      procedure pass2;
  143.      var n : linecnt;
  144.          s : symnum;
  145.          t : ltext;
  146.      begin
  147.      n := 0;
  148.      repeat
  149.          readln(newfile,t);
  150.          n := n + 1;
  151.          s := store(t);
  152.          with ST[s] do
  153.          if (Ncount <> 2) then Ncount := succ(Ncount);
  154.          with NA[n] do begin
  155.          matched := false;
  156.          index := s;
  157.          end;
  158.      until (eof(newfile) or (n = MAXFILE) );
  159.      with NA[n+1] do begin { create sentinal saying END }
  160.          matched := true;
  161.          index := MAXOVER;
  162.      end;
  163.      newmax := n;
  164.      end;
  165.  
  166.     {*
  167.      *        MATCHUP                record the fact that lines match
  168.      *                        by placing reciprocal indices
  169.      *                        in their array indices.
  170.      *}
  171.  
  172.      procedure matchup(o,n : linenum);
  173.      begin
  174.      with OA[o] do begin
  175.          matched := true;
  176.          index := n;
  177.      end;
  178.      with NA[n] do begin
  179.          matched := true;
  180.          index := o;
  181.      end;
  182.      end;
  183.      
  184.     {*
  185.      *        PASS 3                 rule 1 - a line appears 1 time in
  186.      *                        a file is the same line, but possably
  187.      *                        moved
  188.      *}
  189.  
  190.      procedure pass3;
  191.      var s   : symnum;
  192.          o,n : linenum;
  193.      begin
  194.      for n := 1 to newmax do begin
  195.          s := NA[n].index;
  196.          with ST[s] do
  197.          if (Ocount = 1) and (Ncount = 1) then 
  198.              matchup(Oline,n)
  199.      end;
  200.      end;
  201.      
  202.     {*
  203.      *        PASS 4a                rule 2 - sweeping down to end of file
  204.      *                        to spread the rule-1 matches towards end of file
  205.      *}
  206.  
  207.      procedure pass4a;
  208.      var o, o1, n, n1 : linenum;
  209.      begin
  210.      for n := 1 to newmax - 1 do
  211.          if (NA[n].matched) then begin
  212.          n1 := n + 1;
  213.          if not NA[n1].matched then begin
  214.              o := NA[n].index;
  215.              if o < oldmax then begin
  216.              o1 := o + 1;
  217.              {*
  218.               * The 'matched' flags are used to ensure that symbol
  219.               * indices are compared.
  220.               *}
  221.              if (OA[o1].matched = NA[n1].matched) AND
  222.                 (OA[o1].index   = NA[n1].index) then
  223.                  matchup(o1,n1);
  224.              end;
  225.          end;
  226.          end;
  227.      end;
  228.  
  229.     {*
  230.      *        PASS 4b                rule 2 - sweeping up to begining of file
  231.      *                        to spread the rule-1 matches.
  232.      *}
  233.  
  234.      procedure pass4b;
  235.      var o, o1, n, n1 : linenum;
  236.      begin
  237.      for n := newmax downto 2 do
  238.          if (NA[n].matched) then begin
  239.          n1 := n - 1;
  240.          if not NA[n1].matched then begin
  241.              o := NA[n].index;
  242.              if o > 1 then begin
  243.              o1 := o - 1;
  244.              if (OA[o1].matched = NA[n1].matched) AND
  245.                 (OA[o1].index   = NA[n1].index) then
  246.                  matchup(o1,n1);
  247.              end;
  248.          end;
  249.          end;
  250.      end;
  251.  
  252.  
  253.     {*
  254.      *        PASS 5                 KLUGE - Change block moves into delete/insert pairs
  255.      *}
  256.  
  257.      procedure pass5;
  258.      var o, n : linenum;
  259.  
  260.      procedure resolve( var o,n : linenum);
  261.      {*
  262.       *        a discontinuity starts at OA[o] and NA[n].
  263.       *        figure out which block has fewer lines.
  264.       *        convert smaller group to inserts/deletes
  265.       *}
  266.           var xo, xn : linenum;
  267.           t, ospan, nspan : integer;
  268.           s : symnum;
  269.       begin
  270.           xo := o;        { measure block starting at OA[o] }
  271.           repeat
  272.           t := 1 + OA[xo].index;
  273.           xo := xo + 1;
  274.           until t <> OA[xo].index;
  275.           ospan := xo - o;
  276.           
  277.           xn := n;        { measure block starting at NA[n] }
  278.           repeat
  279.           t := 1 + NA[xn].index;
  280.           xn := xn + 1;
  281.           until t <> NA[xn].index;
  282.           nspan := xn - n;
  283.  
  284.           if ospan < nspan then begin        { block move DOWN }
  285.           xo := o;
  286.           xn := OA[o].index;
  287.           t  := ospan;
  288.           o  := o + ospan;
  289.           end
  290.           else begin
  291.           xn := n;
  292.           xo := NA[n].index;
  293.           t  := nspan;
  294.           n  := n + nspan;
  295.           end;
  296.           
  297.           {*
  298.            *        unmatch a block of matched lines
  299.            *        we need symbol table entry here - find by
  300.            *        scanning table.  Could be sped up.
  301.            *}
  302.           while t > 0 do begin
  303.           s := 0;
  304.           while ST[s].Oline <> xo do
  305.               s := s + 1;
  306.           with OA[xo] do begin
  307.               matched := false;
  308.               index := s;
  309.           end;
  310.           with NA[xn] do begin
  311.               matched := false;
  312.               index := s;
  313.           end;
  314.           xo := xo + 1;
  315.           xn := xn + 1;
  316.           t  := t - 1;
  317.           end;
  318.       end;   { resolve }
  319.  
  320.       begin { pass 5 }
  321.       o := 1;
  322.       n := 1;
  323.       while OA[o].index <> maxover do begin
  324.           while not OA[o].matched do {skip deletes }
  325.           o := o + 1;
  326.           while not NA[n].matched do {skip deletes }
  327.           n := n + 1;
  328.           if OA[o].index = n then begin { matching lines }
  329.           o := o + 1;
  330.           n := n + 1;
  331.           end
  332.           else { discontinuity ? }
  333.           if OA[o].index <> maxover then  {yes}
  334.               resolve(o,n)
  335.           else {no - just the sentinal at end.} ;
  336.       end {while}
  337.       end; {pass 5 }
  338.  
  339.     {*
  340.      *        PASS 6                 write the difference file as an editor script
  341.      *}
  342.  
  343. {$I DIFF-ENG.DRV }        { driver for ENGLISH text }
  344.             { There is a driver for CPM's ED, also }
  345.  
  346.     procedure pass6;
  347.     var o, n, xn : linenum;
  348.         t : integer;
  349.  
  350.     begin { pass 6 }
  351.     putfirst;
  352.     o := 1;
  353.     n := 1;
  354.     repeat
  355.         if not OA[o].matched then begin { deleting }
  356.         t := 0;
  357.         repeat
  358.             t := t + 1;
  359.             o := o + 1;
  360.         until OA[o].matched;
  361.         putdel(t);
  362.         end;
  363.         if not NA[n].matched then begin { inserting }
  364.         if n = 1 then puttop;       { Put text above line 1 }
  365.         if o > oldmax then putbot;  { insert at bottom }
  366.         t := 0;
  367.         xn := n;
  368.         repeat
  369.             t := t + 1;
  370.             n := n + 1;
  371.         until NA[n].matched;
  372.         putins(xn,t);
  373.         end;
  374.         t := 0;
  375.         while OA[o].matched AND NA[n].matched AND (o <= oldmax) do begin
  376.         { skip unchanged lines }
  377.         t := t + 1;
  378.         o := o + 1;
  379.         n := n + 1;
  380.         end;
  381.         if t > 0 then putmov(t);
  382.     until (OA[o].index = maxover) AND (NA[n].index = maxover);
  383.     putlast;
  384.     end; { pass 6 }
  385.  
  386.     procedure setup;
  387.     var j : symnum;
  388.         fspec : string[14];
  389.  
  390.     procedure quit(i : integer);
  391.     begin
  392.         writeln;
  393.         write('DIFF: Aborted because of ');
  394.         case i of
  395.         1 : writeln('no OLD filename.');
  396.         2 : writeln('no NEW filename.');
  397.         3 : writeln('no Difference output file.');
  398.         end;
  399.         halt;
  400.     end;
  401.  
  402.     begin
  403.     for j := 0 to TOPSYM do
  404.         with ST[j] do begin
  405.         hashval := -1;  { entry not used }
  406.         Oline := maxover;
  407.         Ocount := 0;
  408.         Ncount := 0;
  409.         end;
  410.     oldmax := 0;
  411.     newmax := 0;
  412.  
  413.     write(chr(27),'(');
  414.     writeln('DIFF - file difference generator for "',ED_NAME,'".');
  415.     write  ('Original (old) file: '); readln(fspec); if fspec = '' then quit(1);
  416.     assign(oldfile, fspec);  reset(oldfile);
  417.     write  ('Modified (new) file: '); readln(fspec); if fspec = '' then quit(2);
  418.     assign(newfile, fspec);  reset(newfile);
  419.     write  ('Diff output file: '); readln(fspec);    if fspec = '' then quit(3);
  420.     assign(DIFFILE, fspec);  rewrite(diffile);
  421.     end;
  422.  
  423. begin { MAIN program - DIFF }
  424.     setup;
  425.     if eof(oldfile) then
  426.     writeln('Old file is empty  -  DIFF is all insert')
  427.     else if eof(newfile) then
  428.     writeln('New file is empty  -  DIFF is all delete')
  429.     else begin
  430.     write('Pass 1',^M);
  431.     pass1;
  432.     write('Pass 2',^M);
  433.     pass2;
  434.     write('Pass 3',^M);
  435.     pass3;
  436.     write('Pass 4a',^M);
  437.     pass4a;
  438.     write('Pass 4b',^M);
  439.     pass4b;
  440.     write('Pass 5 ',^M);
  441.     pass5;
  442.     write('Pass 6',^M);
  443.     pass6;
  444.     close(oldfile);
  445.     close(newfile);
  446.     close(diffile);
  447.     writeln('Done  ');
  448.     end;
  449. end.
  450.  
  451.  
  452.