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