home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / TURBOPAS / COMP.ZIP / COMP.PAS
Encoding:
Pascal/Delphi Source File  |  1985-12-28  |  10.1 KB  |  547 lines

  1. program comp;
  2.  
  3. { source file comparator:
  4.   after an article in Dr. Dobb's Journal No. 94 by D.E. Cortesi
  5.  
  6.   Turbo 3.0 implementation and changes by
  7.   Paul van der Eijk   (703) 941-0942  }
  8.  
  9. {$p32  allows output redirection }
  10. {$r- disable range checking}
  11. {$k- disable stack checking}
  12.  
  13. const
  14.    trace   = false;
  15.  
  16.    maxfile = 2000;         {largest old/new file}
  17.    maxover = 2001;         {maxfile+1 sentinel value}
  18.    maxchar = 255;          {longest line}
  19.    maxsym  = 4023;         {prime > 2*maxfile}
  20.    topsym  = 4022;         {maxsym-1 max index }
  21.  
  22.  
  23. type
  24.    symnum  = 0..topsym;
  25.    linenum = 1..maxover;
  26.    linecnt = 0..maxover;
  27.  
  28.    ltext   = string[maxchar];
  29.    linerec = record
  30.       matched: boolean;  {true when matched in other file}
  31.       index  : integer;  {index to symbtab or other file}
  32.    end;
  33.  
  34.    symrec  = record
  35.       hashval: integer;   {neg = unused entry}
  36.       lineval: ^ltext;    {address of text string}
  37.       oline  : linenum;   {index to line in old file}
  38.       ocount : 0..2;      {occurrences in old file}
  39.       ncount : 0..2;      {occurrences in new file}
  40.    end;
  41.  
  42. var
  43.    oldmax,
  44.    newmax: linecnt;
  45.    oldfile,
  46.    newfile: text;
  47.    difname,
  48.    oldname,
  49.    newname: string[20];
  50.    oa     ,
  51.    na     : array[linenum] of linerec;
  52.    st     : array[symnum] of symrec;
  53.    supbl  : boolean;  {suppress multiple blanks}
  54.  
  55.  
  56. function store(var t: ltext): symnum;
  57. label
  58.    1,
  59.    2,
  60.    3,
  61.    4;
  62.  
  63. var
  64.    s: symnum;
  65.    h: integer;
  66.    l: 0..maxchar;
  67.  
  68.    procedure removebl;
  69.    var
  70.       xl,
  71.       xr : 0..maxchar;
  72.    begin
  73.    xl := 1;
  74.    for xr := 2 to length(t)
  75.    do if (t[xr] <> ' ') or (t[xl] <> ' ')
  76.       then
  77.          begin
  78.          xl := succ(xl);
  79.          t[xl] := t[xr]
  80.          end;
  81.    t[0] := chr(xl)
  82.    end;
  83.  
  84. begin {store}
  85. {strip trailing blanks}
  86. l := length(t);
  87. while t[l] = ' '
  88. do l := l - 1;
  89. t[0] := chr(l);
  90.  
  91. {optional suppress multiple blanks}
  92. if supbl and (l > 0)
  93. then
  94.    removebl;
  95.  
  96. {get hash value}
  97. h := 0;
  98. for l := length(t) downto 1
  99. do h := h + h + ord(t[l]);  {ignores overflow}
  100. h := h and $7FFF;
  101.  
  102. s := h mod maxsym;
  103.  
  104. {find duplicate line or vacant symbol starting at st[s] }
  105. 1: if st[s].hashval < 0
  106.    then
  107.       goto 3;   {free entry}
  108.  
  109.    if st[s].hashval <> h
  110.    then
  111.       goto 2;   {fast not equal}
  112.  
  113.    if st[s].lineval^ = t
  114.    then
  115.       goto 4;   {expensive equal}
  116.  
  117. 2: {next entry}
  118.    s := (s + 1) mod maxsym;
  119.    goto 1;
  120.  
  121. 3: {install new line}
  122.    with st[s]
  123.    do begin
  124.       hashval := h;
  125.       getmem(lineval,length(t) + 1);
  126.       lineval^ := t;
  127.       end;
  128. 4: {line exists}
  129.    store := s
  130. end;
  131.  
  132. procedure pass1;
  133. {read old file}
  134. var
  135.    o: linecnt;
  136.    s: symnum;
  137.    t: ltext;
  138. begin
  139. o := 0;
  140. repeat
  141.    readln(oldfile,t);
  142.    o := o + 1;
  143.    s := store(t);
  144.    with st[s]
  145.    do begin
  146.       oline := o;
  147.       if ocount < 2
  148.       then
  149.          ocount := ocount + 1
  150.       end;
  151.    with oa[o]
  152.    do begin
  153.       matched := false;
  154.       index := s
  155.       end;
  156. until eof(oldfile) or (o >= maxfile);
  157. {create stopper}
  158. with oa[o + 1]
  159. do begin
  160.    matched := true;
  161.    index := maxover
  162.    end;
  163. oldmax := o
  164. end;
  165.  
  166. procedure pass2;
  167. {read the new file}
  168. var
  169.    n: linecnt;
  170.    s: symnum;
  171.    t: ltext;
  172. begin
  173. n := 0;
  174. repeat
  175.    readln(newfile,t);
  176.    n := n + 1;
  177.    s := store(t);
  178.    with st[s]
  179.    do if ncount < 2
  180.       then
  181.          ncount := ncount + 1;
  182.    with na[n]
  183.    do begin
  184.       matched := false;
  185.       index   := s
  186.       end
  187. until eof(newfile) or (n >= maxfile);
  188. {create stopper}
  189. with na[n + 1]
  190. do begin
  191.    matched := true;
  192.    index   := maxover
  193.    end;
  194. newmax := n
  195. end;
  196.  
  197.  
  198. procedure matchup(o,n: linenum);
  199. {store indices for matching lines}
  200. begin
  201. with oa[o]
  202. do begin
  203.    matched := true;
  204.    index   := n
  205.    end;
  206. with na[n]
  207. do begin
  208.    matched := true;
  209.    index := o
  210.    end
  211. end;
  212.  
  213. procedure pass3;
  214. {when a line appears exactly once in each file, it
  215.  is the same line in a possible different position}
  216. var
  217.    o,
  218.    n: linenum;
  219. begin
  220. for n := 1 to newmax
  221. do begin
  222.    with st[na[n].index]
  223.    do if (ocount = 1) and (ncount = 1)
  224.       then
  225.          matchup(oline,n)
  226.    end
  227. end;
  228.  
  229. procedure pass4a;
  230. {if two lines are equal and directly follow two matched lines
  231.  then they match}
  232. var
  233.    o,
  234.    o1,
  235.    n,
  236.    n1: linenum;
  237. begin
  238. for n := 1 to newmax - 1
  239. do if na[n].matched
  240.    then
  241.       begin
  242.       n1 := n + 1;
  243.       if not na[n1].matched
  244.       then
  245.          begin
  246.          o := na[n].index;
  247.          if o < oldmax
  248.          then
  249.             begin
  250.             o1 := o + 1;
  251.             if not oa[o1].matched
  252.             then
  253.                if oa[o1].index = na[n1].index
  254.                then
  255.                   matchup(o1,n1)
  256.             end
  257.          end
  258.       end
  259. end;
  260.  
  261. procedure pass4b;
  262. {if two lines are equal and preceed two matched lines
  263.  then they match}
  264. var
  265.    o ,
  266.    o1,
  267.    n ,
  268.    n1: linenum;
  269. begin
  270. for n := newmax downto 2
  271. do if na[n].matched
  272.    then
  273.       begin
  274.       n1 := n - 1;
  275.       if not na[n1].matched
  276.       then
  277.          begin
  278.          o := na[n].index;
  279.          if o > 1
  280.          then
  281.             begin
  282.             o1 := o - 1;
  283.             if not oa[o1].matched
  284.             then
  285.                if oa[o1].index = na[n1].index
  286.                then
  287.                   matchup(o1,n1)
  288.             end
  289.          end
  290.       end
  291. end;
  292.  
  293.  
  294. procedure pass5;
  295. {translate block moves into deletes and inserts}
  296. var
  297.    o,
  298.    n: linenum;
  299.    done: boolean;
  300.  
  301.    procedure resolve(var o,n: linenum);
  302.    var
  303.       xo,
  304.       xn,
  305.       first,
  306.       last : linenum;
  307.       t    : integer;
  308.       s: symnum;
  309.    begin
  310.    xo := o;
  311.    repeat
  312.       t := 1 + oa[xo].index;
  313.       xo := xo + 1
  314.    until (t <> oa[xo].index) or not oa[xo].matched;
  315.    xn := n;
  316.    repeat
  317.       t := 1 + na[xn].index;
  318.       xn := xn + 1
  319.    until (t <> na[xn].index) or not na[xn].matched;
  320.    if xo - o < xn - n
  321.    then
  322.       begin
  323.       first := o;
  324.       last := xo - 1;
  325.       o := xo
  326.       end
  327.    else
  328.       begin
  329.       first := na[n].index;
  330.       last := first + xn - n - 1;
  331.       n := xn
  332.       end;
  333.  
  334.    s := 0;
  335.    for t := first to last
  336.    do begin
  337.       while (st[s].oline < first) or (st[s].oline > last)
  338.       do s := s + 1;
  339.       xo := st[s].oline;
  340.       xn := oa[xo].index;
  341.       with oa[xo]
  342.       do begin
  343.          matched := false;
  344.          index := s
  345.          end;
  346.       with na[xn]
  347.       do begin
  348.          matched := false;
  349.          index := s
  350.          end;
  351.       s := s + 1
  352.       end
  353.    end;
  354.  
  355. begin {pass5}
  356. o := 1;  n := 1;  done := false;
  357. repeat
  358.    while not oa[o].matched
  359.    do o := o + 1;
  360.    while not na[n].matched
  361.    do n := n + 1;
  362.  
  363.    if (n > newmax) or (o > oldmax)
  364.    then
  365.       done := true
  366.    else
  367.       if oa[o].index = n
  368.       then
  369.          begin
  370.          o := o + 1;
  371.          n := n + 1
  372.          end
  373.       else
  374.          resolve(o,n)
  375. until done
  376. end;
  377.  
  378.  
  379. procedure pass6;
  380. var
  381.    xo,
  382.    xn,
  383.    o,
  384.    n,
  385.    i: linenum;
  386.    delcnt,
  387.    inscnt: integer;
  388.  
  389. begin {pass6}
  390. o := 1;  n := 1;
  391. repeat
  392.  
  393.    delcnt := 0;
  394.    if not oa[o].matched
  395.    then
  396.       begin {deleting}
  397.       xo := o;
  398.       repeat
  399.          delcnt := delcnt + 1;
  400.          o := o + 1
  401.       until oa[o].matched
  402.       end;
  403.  
  404.    inscnt := 0;
  405.    if not na[n].matched
  406.    then
  407.       begin
  408.       xn := n;
  409.       repeat
  410.          inscnt := inscnt + 1;
  411.          n := n + 1
  412.       until na[n].matched;
  413.       end;
  414.  
  415.  
  416.    if (delcnt > 0) and (inscnt > 0)
  417.    then
  418.       begin
  419.       writeln('-----Replace at ',xo:0);
  420.       for i := xo to xo + delcnt - 1
  421.       do writeln(st[oa[i].index].lineval^);
  422.       writeln('-----with');
  423.       for i := xn to xn + inscnt - 1
  424.       do writeln(st[na[i].index].lineval^)
  425.       end
  426.    else
  427.       if delcnt > 0
  428.       then
  429.          begin
  430.          writeln('-----Delete at ',xo:0);
  431.          for i := xo to xo + delcnt - 1
  432.          do writeln(st[oa[i].index].lineval^)
  433.          end
  434.       else
  435.          if inscnt > 0
  436.          then
  437.             begin
  438.             writeln('-----Insert at ',xn:0);
  439.             for i := xn to xn + inscnt - 1
  440.             do writeln(st[na[i].index].lineval^)
  441.             end;
  442.  
  443.    while oa[o].matched and na[n].matched and (o <= oldmax)
  444.    do begin
  445.       o := o + 1;
  446.       n := n + 1
  447.       end;
  448.  
  449.  
  450. until (oa[o].index = maxover) and (na[n].index = maxover);
  451. end;
  452.  
  453.  
  454. procedure setup;
  455. var
  456.    j: symnum;
  457. begin
  458. for j := 0 to topsym
  459. do with st[j]
  460.    do begin
  461.       hashval := -1;
  462.       oline := maxover;
  463.       ocount := 0;
  464.       ncount := 0
  465.       end;
  466. oldmax := 0;
  467. newmax := 0;
  468.  
  469. end;
  470.  
  471.  
  472. procedure dump(t: ltext);
  473. var
  474.    i: linenum;
  475. begin
  476. if trace
  477. then
  478.    begin
  479.    writeln('OLD FILE ',t);
  480.    for i := 1 to oldmax
  481.    do with oa[i],st[index]
  482.       do begin
  483.          write(i:5,index:8,matched:7,oline:8,ocount:3,ncount:3);
  484.          if not matched
  485.          then
  486.             write('   ',lineval^);
  487.          writeln
  488.          end;
  489.    writeln('NEW FILE ',t);
  490.    for i := 1 to newmax
  491.    do with na[i],st[index]
  492.       do begin
  493.          write(i:5,index:8,matched:7,oline:8,ocount:3,ncount:3);
  494.          if not matched
  495.          then
  496.             write('    ',lineval^);
  497.          writeln
  498.          end
  499.    end
  500. end;
  501.  
  502.  
  503.  
  504. begin
  505. if paramcount < 2
  506. then
  507.    begin
  508.    writeln('usage: comp oldfile newfile');
  509.    writeln('       optional switches /s : to suppress multiple blanks');
  510.    writeln('       diffile is written to output')
  511.    end
  512. else
  513.    begin
  514.    assign(oldfile,paramstr(1));
  515.    reset(oldfile);
  516.    assign(newfile,paramstr(2));
  517.    reset(newfile);
  518.    supbl := false;
  519.    if paramcount > 2
  520.    then
  521.       begin
  522.       if (paramstr(3) = '/s') or (paramstr(3) = '/S')
  523.       then
  524.          supbl := true
  525.       end;
  526.  
  527.    if eof(oldfile)
  528.    then
  529.       writeln('OLD FILE IS EMPTY')
  530.    else
  531.       if eof(newfile)
  532.       then
  533.          writeln('NEW FILE IS EMPTY')
  534.       else
  535.          begin
  536.          setup;
  537.          pass1;
  538.          pass2;  dump('AFTER PASS2');
  539.          pass3;  dump('AFTER PASS3');
  540.          pass5;  dump('AFTER PASS5');
  541.          pass4a;
  542.          pass4b; dump('AFTER PASS4');
  543.          pass6;
  544.          end
  545.    end
  546. end.
  547.