1
2(********************************************************************)
3(*                                                                  *)
4(*  diff7.sd7     Compare two files line by line.                   *)
5(*  Copyright (C) 2010 - 2017  Thomas Mertes                        *)
6(*                                                                  *)
7(*  This program is free software; you can redistribute it and/or   *)
8(*  modify it under the terms of the GNU General Public License as  *)
9(*  published by the Free Software Foundation; either version 2 of  *)
10(*  the License, or (at your option) any later version.             *)
11(*                                                                  *)
12(*  This program is distributed in the hope that it will be useful, *)
13(*  but WITHOUT ANY WARRANTY; without even the implied warranty of  *)
14(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the   *)
15(*  GNU General Public License for more details.                    *)
16(*                                                                  *)
17(*  You should have received a copy of the GNU General Public       *)
18(*  License along with this program; if not, write to the           *)
19(*  Free Software Foundation, Inc., 51 Franklin Street,             *)
20(*  Fifth Floor, Boston, MA  02110-1301, USA.                       *)
21(*                                                                  *)
22(********************************************************************)
23
24
25$ include "seed7_05.s7i";
26  include "osfiles.s7i";
27  include "getf.s7i";
28
29
30const proc: showLines (in string: marker, in array string: lines,
31    in integer: startIndex, in integer: stopIndex) is func
32  local
33    var integer: index is 0;
34  begin
35    for index range startIndex to stopIndex do
36      writeln(marker <& lines[index]);
37    end for;
38  end func;
39
40
41const proc: changeMessage (in integer: startIdx1, in integer: endIdx1,
42    in integer: startIdx2, in integer: endIdx2) is func
43  begin
44    write(startIdx1);
45    if startIdx1 <> endIdx1 then
46      write("," <& endIdx1);
47    end if;
48    write("c" <& startIdx2);
49    if startIdx2 <> endIdx2 then
50      write("," <& endIdx2);
51    end if;
52    writeln;
53  end func;
54
55
56const proc: diffContent (in string: content1, in string: content2) is func
57  local
58    var array string: lines1 is 0 times "";
59    var array string: lines2 is 0 times "";
60    var integer: index1 is 1;
61    var integer: index2 is 1;
62    var integer: index1b is 1;
63    var integer: index2b is 1;
64    var integer: index1c is 1;
65    var integer: index2c is 1;
66    var boolean: match is FALSE;
67  begin
68    lines1 := split(content1, '\n');
69    lines2 := split(content2, '\n');
70    while index1 <= length(lines1) or index2 <= length(lines2) do
71      while index1 <= length(lines1) and index2 <= length(lines2) and
72          lines1[index1] = lines2[index2] do
73        # writeln("A " <& lines1[index1]);
74        # writeln("B " <& lines2[index2]);
75        incr(index1);
76        incr(index2);
77      end while;
78      # writeln("index1: " <& index1);
79      # writeln("index2: " <& index2);
80      if index1 <= length(lines1) or index2 <= length(lines2) then
81        # writeln("-----");
82        index1b := succ(index1);
83        index2b := succ(index2);
84        match := FALSE;
85        while not match and (index1b <= length(lines1) or index2b <= length(lines2)) do
86          # writeln("index1b: " <& index1b);
87          # writeln("index2b: " <& index2b);
88          if index1b <= length(lines1) and index2 <= length(lines2) and
89              lines1[index1b] = lines2[index2] then
90            if index1 <> pred(index1b) then
91              write(index1 <& ",");
92            end if;
93            writeln(pred(index1b) <&"d" <& pred(index2));
94            showLines("< ", lines1, index1, pred(index1b));
95            index1 := succ(index1b);
96            incr(index2);
97            match := TRUE;
98          elsif index1 <= length(lines1) and index2b <= length(lines2) and
99              lines1[index1] = lines2[index2b] then
100            write(pred(index1) <& "a" <& index2);
101            if index2 <> pred(index2b) then
102              write("," <& pred(index2b));
103            end if;
104            writeln;
105            showLines("> ", lines2, index2, pred(index2b));
106            incr(index1);
107            index2 := succ(index2b);
108            match := TRUE;
109          else
110            if index1b <= length(lines1) and index2b <= length(lines2) then
111              index1c := succ(index1);
112              while not match and index1c <= index1b do
113                if lines1[index1c] = lines2[index2b] then
114                  changeMessage(index1, pred(index1c), index2, pred(index2b));
115                  showLines("< ", lines1, index1, pred(index1c));
116                  writeln("---");
117                  showLines("> ", lines2, index2, pred(index2b));
118                  index1 := succ(index1c);
119                  index2 := succ(index2b);
120                  match := TRUE;
121                else
122                  incr(index1c);
123                end if;
124              end while;
125              if not match then
126                index2c := succ(index2);
127                while not match and index2c <= index2b do
128                  if lines1[index1b] = lines2[index2c] then
129                    changeMessage(index1, pred(index1b), index2, pred(index2c));
130                    showLines("< ", lines1, index1, pred(index1b));
131                    writeln("---");
132                    showLines("> ", lines2, index2, pred(index2c));
133                    index1 := succ(index1b);
134                    index2 := succ(index2c);
135                    match := TRUE;
136                  else
137                    incr(index2c);
138                  end if;
139                end while;
140              end if;
141            end if;
142          end if;
143          if not match then
144            incr(index1b);
145            incr(index2b);
146          end if;
147        end while;
148        if not match then
149          changeMessage(index1, length(lines1), index2, length(lines2));
150          showLines("< ", lines1, index1, length(lines1));
151          writeln("---");
152          showLines("> ", lines2, index2, length(lines2));
153          index1 := succ(length(lines1));
154          index2 := succ(length(lines2));
155        end if;
156      end if;
157    end while;
158  end func;
159
160
161const proc: diff (in string: path1, in string: path2) is func
162  local
163    var fileType: fileType1 is FILE_ABSENT;
164    var fileType: fileType2 is FILE_ABSENT;
165    var string: currName is "";
166    var string: fileContent1 is "";
167    var string: fileContent2 is "";
168  begin
169    fileType1 := fileType(path1);
170    fileType2 := fileType(path2);
171    if fileType1 = FILE_DIR then
172      if fileType2 <> FILE_DIR then
173        writeln(literal(path1) <& " is a directory");
174        writeln(literal(path2) <& " is not a directory");
175      else
176        for currName range readDir(path1) do
177          # writeln(currName);
178          diff(path1 & "/" & currName, path2 & "/" & currName);
179        end for;
180        for currName range readDir(path2) do
181          if fileType(path1 & "/" & currName) = FILE_ABSENT then
182            writeln(literal(path1 & "/" & currName) <& " missing");
183          end if;
184        end for;
185      end if;
186    elsif fileType1 = FILE_REGULAR then
187      if fileType2 = FILE_ABSENT then
188        writeln(literal(path2) <& " missing");
189      elsif fileType2 <> FILE_REGULAR then
190        writeln(literal(path2) <& " is not a regular file");
191      else
192        fileContent1 := getf(path1);
193        fileContent2 := getf(path2);
194        if fileContent1 <> fileContent2 then
195          if replace(fileContent1, "\r\n", "\n") = fileContent2 then
196            writeln(literal(path1) <& " uses CRLF as line ending");
197          elsif fileContent1 = replace(fileContent2, "\r\n", "\n") then
198            writeln(literal(path2) <& " uses CRLF as line ending");
199          else
200            writeln(literal(path1) <& " and " <& literal(path2) <& " differ");
201            if pos(fileContent1, "\r\n") <> 0 and pos(fileContent2, "\r\n") = 0 then
202              writeln(literal(path1) <& " uses CRLF as line ending");
203              fileContent1 := replace(fileContent1, "\r\n", "\n");
204            end if;
205            if pos(fileContent1, "\r\n") = 0 and pos(fileContent2, "\r\n") <> 0 then
206              writeln(literal(path2) <& " uses CRLF as line ending");
207              fileContent2 := replace(fileContent2, "\r\n", "\n");
208            end if;
209            diffContent(fileContent1, fileContent2);
210          end if;
211        end if;
212      end if;
213    else
214      if fileType1 = FILE_ABSENT then
215        writeln(literal(path1) <& " missing");
216      else
217        writeln(literal(path1) <& " is not a regular file");
218      end if;
219      if fileType2 = FILE_ABSENT then
220        writeln(literal(path2) <& " missing");
221      elsif fileType2 <> FILE_REGULAR and fileType2 <> FILE_DIR then
222        writeln(literal(path2) <& " is not a regular file");
223      end if;
224    end if;
225  end func;
226
227
228const proc: main is func
229  local
230    var string: path1 is "";
231    var string: path2 is "";
232    var integer: slashPos is 0;
233    var string: fileName is "";
234  begin
235    if length(argv(PROGRAM)) <> 2 then
236      writeln("Diff7 Version 1.0 - Compare two files line by line.");
237      writeln("Copyright (C) 2010 - 2017 Thomas Mertes");
238      writeln("This is free software; see the source for copying conditions.  There is NO");
239      writeln("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
240      writeln("Diff7 is written in the Seed7 programming language");
241      writeln("Homepage: http://seed7.sourceforge.net");
242      writeln;
243      writeln("usage: diff7 file1 file2");
244    else
245      path1 := convDosPath(argv(PROGRAM)[1]);
246      path2 := convDosPath(argv(PROGRAM)[2]);
247      if fileType(path1) = FILE_REGULAR and fileType(path2) = FILE_DIR then
248        slashPos := rpos(path1, '/');
249        if slashPos <> 0 then
250          fileName := path1[succ(slashPos) ..];
251        else
252          fileName := path1;
253        end if;
254        if path2 = "/" then
255          path2 &:= fileName;
256        else
257          path2 &:= "/" & fileName;
258        end if;
259      end if;
260      diff(path1, path2);
261    end if;
262  end func;
263