// Reverse a context diff. // Bruno Haible 26.1.1999 /* * Copyright (C) 1996, 1997, 1999, 2000, 2002 Bruno Haible * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ import java.io.*; class IntRef { int _i; public IntRef (int i) { _i = i; } public int getValue () { return _i; } public void setValue (int i) { _i = i; } } class DiffParse { public static Long parseInteger (String str, IntRef index) { int i = index.getValue(); boolean negative = false; if (str.charAt(i) == '-') { i++; negative = true; } int after_sign = i; long val = 0; for (;;) { char c = str.charAt(i); if (!(c >= '0' && c <= '9')) break; val = val*10 + (c-'0'); i++; } if (after_sign < i) { index.setValue(i); return new Long(negative ? -val : val); } else return null; // was: throw new NumberFormatException(); } } class ContextIn { DataInput _in_stream; int _linenum; // Number of last read line. String _line; // Last read line, or null. public ContextIn (DataInput stream) { _in_stream = stream; _linenum = 0; _line = null; } // Read the next line. public void nextLine () throws IOException { String line = _in_stream.readLine(); _linenum++; _line = line; } // Read the next line, if the last line has already been digested. public void prepareLine () throws IOException { if (_line == null) nextLine(); } } class ContextOut { DataOutput _out_stream; public ContextOut (DataOutput stream) { _out_stream = stream; } } class ContextdiffHalfhunkParams { // A contextdiff half hunk... int start_line; int end_line; } // ... is introduced by a line of the form "*** %d,%d ****" or "*** %d ****". class ContextdiffHalfhunkParse { public static ContextdiffHalfhunkParams parseContextdiffHalfhunkLine (String line, char fillchar) { IntRef index; int i; if (line.length() >= 10) { if ( line.charAt(0) == fillchar && line.charAt(1) == fillchar && line.charAt(2) == fillchar && line.charAt(3) == ' ') { index = new IntRef(4); Long start_line = DiffParse.parseInteger(line,index); i = index.getValue(); Long end_line; if (start_line != null && i+1 <= line.length() && line.charAt(i) == ',') { index = new IntRef(i+1); end_line = DiffParse.parseInteger(line,index); i = index.getValue(); } else { end_line = new Long(start_line.longValue() > 0 ? start_line.longValue() : start_line.longValue() - 1); } if (i+5 == line.length() && line.charAt(i) == ' ' && line.charAt(i+1) == fillchar && line.charAt(i+2) == fillchar && line.charAt(i+3) == fillchar && line.charAt(i+4) == fillchar) { ContextdiffHalfhunkParams result = new ContextdiffHalfhunkParams(); result.start_line = start_line.intValue(); result.end_line = end_line.intValue(); return result; } } } return null; } } class ContextdiffHalfhunk extends ContextdiffHalfhunkParams { StringVector lines; int changes; ContextdiffHalfhunk () { lines = new StringVector(); changes = 0; } ContextdiffHalfhunk (ContextdiffHalfhunkParams params) { start_line = params.start_line; end_line = params.end_line; lines = new StringVector(); changes = 0; } } class cdiffreverse { /* Should have multiple inheritance, really! static class ContextInOut { ContextIn _cin; ContextOut _cout; ContextInOut (DataInput istream, DataOutput ostream) { _cin = new ContextIn(istream); _cout = new ContextOut(ostream); } } */ static class ContextInOut extends ContextIn { DataOutput _out_stream; String _program_name; ContextInOut (DataInput istream, DataOutput ostream) { super (istream); _out_stream = ostream; _program_name = "cdiffreverse"; } ContextdiffHalfhunk getHalfhunk (char fillchar, char insertchar) throws IOException { prepareLine(); if (_line == null) return null; ContextdiffHalfhunkParams halfhunk_params = ContextdiffHalfhunkParse.parseContextdiffHalfhunkLine(_line,fillchar); if (halfhunk_params == null) return null; ContextdiffHalfhunk halfhunk = new ContextdiffHalfhunk(halfhunk_params); boolean in_change = false; for (;;) { nextLine(); if (_line == null) break; if (!(_line.length() >= 2)) break; char indicator = _line.charAt(0); if (!( indicator == ' ' || indicator == '!' || indicator == insertchar)) break; if (!(_line.charAt(1) == ' ')) break; if (indicator == '!') { if (!in_change) halfhunk.changes++; in_change = true; } else in_change = false; halfhunk.lines.addElement(_line); } return halfhunk; } // Compare two half-hunks for consistency. String compareHalfhunks (ContextdiffHalfhunk old_half, ContextdiffHalfhunk new_half) { StringVector old_lines = old_half.lines; StringVector new_lines = new_half.lines; if (!(old_half.changes == new_half.changes)) return "changes count"; int old_i = 0; int new_i = 0; for (;;) { while (old_i < old_lines.size()) { char indicator = old_lines.elementAt(old_i).charAt(0); if (indicator == ' ' || indicator == '!') break; old_i++; } while (new_i < new_lines.size()) { char indicator = new_lines.elementAt(new_i).charAt(0); if (indicator == ' ' || indicator == '!') break; new_i++; } if (old_i == old_lines.size() && new_i == new_lines.size()) break; if (old_i == old_lines.size()) return "too few old lines"; if (new_i == new_lines.size()) return "too few new lines"; String next_old_line = old_lines.elementAt(old_i); String next_new_line = new_lines.elementAt(new_i); char old_indicator = next_old_line.charAt(0); char new_indicator = next_new_line.charAt(0); if (old_indicator == ' ' && new_indicator == ' ') { if (!next_old_line.equals(next_new_line)) return "different indicators"; old_i++; new_i++; } else if (old_indicator == '!' && new_indicator == '!') { do { old_i++; } while (old_i < old_lines.size() && old_lines.elementAt(old_i).charAt(0) == '!'); do { new_i++; } while (new_i < new_lines.size() && new_lines.elementAt(new_i).charAt(0) == '!'); } else { return "bad indicator"; } } return null; } static String two_lines_string (int start_line, int end_line) { if (start_line <= end_line) return String.valueOf(start_line) .concat(",") .concat(String.valueOf(end_line)); else return String.valueOf(start_line); } void doHunks () throws IOException { for (;;) { prepareLine(); if (_line == null) break; if (!(_line.length()>=15 && _line.startsWith("***************"))) break; nextLine(); ContextdiffHalfhunk old_half = getHalfhunk('*','-'); if (old_half == null) break; ContextdiffHalfhunk new_half = getHalfhunk('-','+'); if (new_half == null) break; int old_start_line = old_half.start_line; int old_end_line = old_half.end_line; int old_lines_count = old_end_line - old_start_line + 1; int new_start_line = new_half.start_line; int new_end_line = new_half.end_line; int new_lines_count = new_end_line - new_start_line + 1; StringVector old_lines = old_half.lines; StringVector new_lines = new_half.lines; // Consistency checks. if (!(old_lines.size()==0 || old_lines.size() == old_lines_count)) { System.err.print(_program_name); System.err.print(": Warning: Hunk ending at line "); System.err.print(_linenum - (_line != null ? 1 : 0)); System.err.print(" has wrong old line numbers"); System.err.println(); break; } if (!(new_lines.size()==0 || new_lines.size() == new_lines_count)) { System.err.print(_program_name); System.err.print(": Warning: Hunk ending at line "); System.err.print(_linenum - (_line != null ? 1 : 0)); System.err.print(" has wrong new line numbers"); System.err.println(); break; } if (old_lines.size()==0 && new_lines.size()==0) { System.err.print(_program_name); System.err.print(": Warning: Empty hunk ending at line "); System.err.print(_linenum - (_line != null ? 1 : 0)); System.err.println(); break; } if (old_lines.size()==0) { if (!(new_half.changes == 0)) { System.err.print(_program_name); System.err.print(": Warning: Old lines missing in hunk ending at line "); System.err.print(_linenum - (_line != null ? 1 : 0)); System.err.println(); break; } } else if (new_lines.size()==0) { if (!(old_half.changes == 0)) { System.err.print(_program_name); System.err.print(": Warning: New lines missing in hunk ending at line "); System.err.print(_linenum - (_line != null ? 1 : 0)); System.err.println(); break; } } else { String mismatchp = compareHalfhunks(old_half,new_half); if (mismatchp != null) { System.err.print(_program_name); System.err.print(": Warning: Mismatch ("); System.err.print(mismatchp); System.err.print(") between old and new lines in hunk ending at line "); System.err.print(_linenum - (_line != null ? 1 : 0)); System.err.println(); break; } } // Output a reversed context diff hunk. _out_stream.writeBytes("***************\n"); _out_stream.writeBytes("*** "); _out_stream.writeBytes(two_lines_string(new_start_line,new_end_line)); _out_stream.writeBytes(" ****\n"); for (StringEnumeration sloop = new StringVectorEnumerator(new_lines); sloop.hasMoreElements(); ) { String line = sloop.nextElement(); if (line.charAt(0) == '+') { _out_stream.writeBytes("-"); _out_stream.writeBytes(line.substring(1)); } else if (line.charAt(0) == '-') { _out_stream.writeBytes("+"); _out_stream.writeBytes(line.substring(1)); } else _out_stream.writeBytes(line); _out_stream.writeBytes("\n"); } _out_stream.writeBytes("--- "); _out_stream.writeBytes(two_lines_string(old_start_line,old_end_line)); _out_stream.writeBytes(" ----\n"); for (StringEnumeration sloop = new StringVectorEnumerator(old_lines); sloop.hasMoreElements(); ) { String line = sloop.nextElement(); if (line.charAt(0) == '+') { _out_stream.writeBytes("-"); _out_stream.writeBytes(line.substring(1)); } else if (line.charAt(0) == '-') { _out_stream.writeBytes("+"); _out_stream.writeBytes(line.substring(1)); } else _out_stream.writeBytes(line); _out_stream.writeBytes("\n"); } } } void doFiles () throws IOException { for (;;) { String headline = null; String oldfile = null; String newfile = null; for (;;) { prepareLine(); if (_line == null) break; if (_line.length() >= 4 && _line.charAt(0) == 'd' && _line.charAt(1) == 'i' && _line.charAt(2) == 'f' && _line.charAt(3) == 'f') headline = _line; else if (_line.length() >= 4 && _line.charAt(0) == '*' && _line.charAt(1) == '*' && _line.charAt(2) == '*' && _line.charAt(3) == ' ') break; else { // System.err.print(_program_name); // System.err.print(": Warning: Junk at line "); // System.err.print(linenum); // System.err.print("."); // System.err.println(); _out_stream.writeBytes(_line); _out_stream.writeBytes("\n"); } _line = null; } if (_line == null) break; if (_line.length() >= 4 && _line.charAt(0) == '*' && _line.charAt(1) == '*' && _line.charAt(2) == '*' && _line.charAt(3) == ' ') { oldfile = _line.substring(4); nextLine(); } if (_line == null) break; if (_line.length() >= 4 && _line.charAt(0) == '-' && _line.charAt(1) == '-' && _line.charAt(2) == '-' && _line.charAt(3) == ' ') { newfile = _line.substring(4); nextLine(); } if (_line == null) break; if (headline != null) { _out_stream.writeBytes(headline); _out_stream.writeBytes("\n"); } if (oldfile != null && newfile != null) { _out_stream.writeBytes("*** "); _out_stream.writeBytes(newfile); _out_stream.writeBytes("\n"); _out_stream.writeBytes("--- "); _out_stream.writeBytes(oldfile); _out_stream.writeBytes("\n"); } doHunks(); } } } // Main program! public static void main (String args[]) { ContextInOut context = new ContextInOut(new DataInputStream(System.in), new DataOutputStream(System.out)); try { context.doFiles(); } catch (IOException e) { System.exit(1); } if (System.out.checkError()) System.exit(1); } }