// Reverse a unidiff. // Bruno Haible 26.1.1999 /* * Copyright (C) 1995, 1996, 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. */ // My personal preferences. #define var #define loop while (1) #define unused (void) // The `bool' type. #if !(defined(__GNUC__) || defined(__KCC) || defined(__ICC) || defined(__ECC)) typedef int bool; #endif #define true 1 #define false 0 // Debugging hooks. #define DEBUG_MALLOC(statement) //statement #define DEBUG_ABORT(statement) //statement #define DEBUG_READLINE(statement) //statement #define DEBUG_LINES(statement) //statement #define DEBUG_MALLOC_STATISTICS(statement) //statement // Finally inline, only when not debugging any more. #define finline inline // Include files. #include #include #include /* for sprintf() only */ #include #undef offsetof #define offsetof(type,ident) ((unsigned long)&(((type*)0)->ident)) // Global variables. struct { const char * name; DEBUG_MALLOC_STATISTICS(unsigned long allocated_chunks;) DEBUG_MALLOC_STATISTICS(unsigned long allocated_bytes;) DEBUG_MALLOC_STATISTICS( void statistics (const char * where) { fprintf(stderr, "%s: Allocated bytes: %lu, in %lu chunks.\n", where, allocated_bytes, allocated_chunks); } ) } program; // Memory allocation. extern void* xmalloc (size_t size); extern void xfree (void* ptr); // Implementation. #ifndef sun // SunOS 4 #ifndef malloc extern "C" void* malloc (size_t size); #endif #ifndef free extern "C" void free (void* ptr); #endif #endif finline void oom () { cerr << program.name << ": Error: Out of virtual memory.\n"; exit(1); } // Just like malloc() but never return NULL pointers. finline void* xmalloc (size_t size) { DEBUG_MALLOC_STATISTICS(size += sizeof(long);) DEBUG_MALLOC(fprintf(stderr,">>xmalloc(%ld)\n",(long)size);) var void* ptr = malloc(size); DEBUG_MALLOC(fprintf(stderr,"<%p\n",(long)size,ptr);) if (ptr) { DEBUG_MALLOC_STATISTICS(size -= sizeof(long);) DEBUG_MALLOC_STATISTICS(*(long*)ptr = size;) DEBUG_MALLOC_STATISTICS(ptr = (long*)ptr + 1;) DEBUG_MALLOC_STATISTICS(program.allocated_chunks++;) DEBUG_MALLOC_STATISTICS(program.allocated_bytes += size;) return ptr; } oom(); } // Just like free(). finline void xfree (void* ptr) { DEBUG_MALLOC_STATISTICS(ptr = (long*)ptr - 1;) DEBUG_MALLOC_STATISTICS(program.allocated_bytes -= *(long*)ptr;) DEBUG_MALLOC_STATISTICS(program.allocated_chunks--;) DEBUG_MALLOC(fprintf(stderr,">>free(%p)\n",ptr);) #ifndef sun free(ptr); #else free((char*)ptr); #endif DEBUG_MALLOC(fprintf(stderr,"< 0; count--) *ptr2++ = *ptr1++; *ptr2++ = '\0'; } return string; } // Garbage collected objects. struct heap_object { int refcount; // reference count // Destructor. virtual ~heap_object () {}; protected: // Constructor. heap_object () : refcount(1) {} }; finline void free_heap_object (heap_object* pointer) { // This is invoked when pointer->refcount gets decremented to 0. (*pointer).~heap_object(); xfree(pointer); } // Increment the reference count. inline void inc_pointer_refcount (heap_object* pointer) { pointer->refcount++; } // Decrement the reference count. inline void dec_pointer_refcount (heap_object* pointer) { if (--pointer->refcount == 0) free_heap_object(pointer); } // An "object" is a reference (pointer) to a heap-allocated area. class object { protected: heap_object* pointer; // Normally no constructor accessible. object (heap_object* p) : pointer(p) {}; public: // Copy constructor. object (const object& x) { var heap_object* p = x.pointer; inc_pointer_refcount(p); pointer = p; } // Assignment operator. object& operator= (const object& x) { /* Be careful, we might be assigning x to itself. */ var heap_object* p = x.pointer; inc_pointer_refcount(p); dec_pointer_refcount(pointer); pointer = p; return *this; } // Destructor. ~object () { dec_pointer_refcount(pointer); } }; // General, garbage collected strings. struct heap_string : public heap_object { unsigned long length; // length (in characters) char data[1]; // the characters, plus a '\0' at the end // Destructor. ~heap_string () {} // Allocator. void* operator new (size_t size, unsigned long len) { unused size; return xmalloc(offsetof(heap_string,data[len+1])); } // No default constructor. heap_string (unsigned long len) : heap_object(), length(len) { /* Have to fill data[0..len] yourself. */ } }; finline heap_string* make_heap_string (unsigned long len) { return new (len) heap_string (len); // Have to fill data[0..len] yourself. } finline heap_string* make_heap_string (const char * ptr, unsigned long len) { var heap_string* str = make_heap_string(len); var const char * ptr1 = ptr; var char * ptr2 = &str->data[0]; for (var unsigned long count = len; count > 0; count--) *ptr2++ = *ptr1++; *ptr2++ = '\0'; return str; } struct string : public object { // pointer is actually a heap_string*. // Convert to a `const char *'. Don't call free() on the resulting pointer! operator const char * () const { return &((heap_string*)pointer)->data[0]; } // Return the length (number of characters). unsigned long length () const { return ((heap_string*)pointer)->length; } // Return a specific character. char operator[] (unsigned long i) const { DEBUG_ABORT(fprintf(stderr,"string[]: length=%ld, i=%ld\n",(long)length(),(long)i);) if (!(i < length())) abort(); // Range check. return ((heap_string*)pointer)->data[i]; } // New dpANS C++ compilers also want the following. char operator[] (unsigned int i) const { return operator[]((unsigned long)i); } char operator[] (long i) const { return operator[]((unsigned long)i); } char operator[] (int i) const { return operator[]((unsigned long)i); } // Constructor. string (const char * s) : object(make_heap_string(s,strlen(s))) {} string (heap_string* p) : object(p) {} // Must already have refcount=1. // Copy constructor. string (const string& x) : object(x) {} // Assignment operator. string& operator= (const string& x) { /* Be careful, we might be assigning x to itself. */ var heap_object* p = x.pointer; inc_pointer_refcount(p); dec_pointer_refcount(pointer); pointer = p; return *this; } // Destructor: already inherited from `object'. // ~string () { dec_pointer_refcount(pointer); } // Default constructor. string (); }; // Some default value for uninitialized strings. static string null_string = string(make_heap_string(NULL,0)); // Default constructor. string::string () : object(null_string) {} // Length. finline unsigned long strlen (const string& str) { return str.length(); } // Returns a string. string make_string (const char * ptr, unsigned long len); // Concatenation. string operator+ (const string& str1, const string& str2); string operator+ (sstring str1, const string& str2); string operator+ (const string& str1, sstring str2); // Implementation. finline string make_string (const char * ptr, unsigned long len) { return string(make_heap_string(ptr,len)); } finline string operator+ (const string& str1, const string& str2) { unsigned long len1 = strlen(str1); unsigned long len2 = strlen(str2); var heap_string* str = make_heap_string(len1+len2); var char * ptr = &str->data[0]; { var const char * ptr1 = str1; for (var unsigned long count = len1; count > 0; count--) *ptr++ = *ptr1++; } { var const char * ptr2 = str2; for (var unsigned long count = len2; count > 0; count--) *ptr++ = *ptr2++; } *ptr++ = '\0'; return str; } string operator+ (sstring str1, const string& str2) { unsigned long len1 = strlen(str1); unsigned long len2 = strlen(str2); var heap_string* str = make_heap_string(len1+len2); var char * ptr = &str->data[0]; { var const char * ptr1 = str1; for (var unsigned long count = len1; count > 0; count--) *ptr++ = *ptr1++; } { var const char * ptr2 = str2; for (var unsigned long count = len2; count > 0; count--) *ptr++ = *ptr2++; } *ptr++ = '\0'; return str; } finline string operator+ (const string& str1, sstring str2) { unsigned long len1 = strlen(str1); unsigned long len2 = strlen(str2); var heap_string* str = make_heap_string(len1+len2); var char * ptr = &str->data[0]; { var const char * ptr1 = str1; for (var unsigned long count = len1; count > 0; count--) *ptr++ = *ptr1++; } { var const char * ptr2 = str2; for (var unsigned long count = len2; count > 0; count--) *ptr++ = *ptr2++; } *ptr++ = '\0'; return str; } // Extendable strings. class pushstring { protected: char * buffer; unsigned long alloc; // allocated size of buffer unsigned long index; // index into buffer, 0 <= index <= alloc public: // Constructor. When constructed, the string is empty. pushstring (); // Destructor. ~pushstring (); // Forget the contents. void reset (); // Add a character at the end. void push (char); // Adds several characters at the end at once. void append (const char * ptr, unsigned long len); // Look at a the contents. unsigned long length () const { return index; } char operator[] (unsigned long i) const { DEBUG_ABORT(fprintf(stderr,"pushstring[]: length=%ld, i=%ld\n",(long)index,(long)i);) if (!(i < index)) abort(); return buffer[i]; } // Get the contents as a string. Free it using xfree() when done. string contents (); }; inline pushstring::pushstring () { alloc = 80; // Must be > 0. buffer = (char *) xmalloc(alloc); index = 0; } inline pushstring::~pushstring () { xfree(buffer); } inline void pushstring::reset () { index = 0; } inline string pushstring::contents () { return make_string(buffer,index); } inline void pushstring::push (char c) { if (index >= alloc) { var unsigned long newalloc = 2*alloc; var char* newbuffer = (char *) xmalloc(newalloc); memcpy(newbuffer,buffer,alloc); xfree(buffer); buffer = newbuffer; alloc = newalloc; } // Now index < alloc. buffer[index++] = c; } inline void pushstring::append (const char * ptr, unsigned long len) { if (index + len > alloc) { var unsigned long newalloc = index+2*len; var char* newbuffer = (char *) xmalloc(newalloc); memcpy(newbuffer,buffer,alloc); xfree(buffer); buffer = newbuffer; alloc = newalloc; } // Now index+len <= alloc. for (unsigned long count = len; count > 0; count--) buffer[index++] = *ptr++; } // Concatenation. finline void operator+= (pushstring& buf, const pushstring& more) { var unsigned long limit = more.length(); for (var unsigned long i = 0; i < limit; i++) buf.push(more[i]); } // Function types. // Function getting T and returning void. // Subclass it. Pass it by reference or pointer! template struct void_fn { protected: virtual void fn (const T&) = 0; // { DEBUG_ABORT(fprintf(stderr,"void_fn::fn called\n");) abort(); } public: void operator() (const T& t) { fn(t); } }; // Function getting T and returning T. // Subclass it. Pass it by reference or pointer! template struct id_fn { protected: virtual T fn (const T&) = 0; // { DEBUG_ABORT(fprintf(stderr,"id_fn::fn called\n");) abort(); } public: T operator() (const T& t) { return fn(t); } }; // Simple vectors. template // T must be a class with default constructor. struct svector { T * pointer; svector (T * ptr) : pointer(ptr) {} }; // Returns a vector. template extern svector make_svector (const T * ptr, unsigned long len); // Free a vector. template extern void xfree (const svector&); // Implementation. template svector make_svector (const T * ptr, unsigned long len) { var T * vector = new T[len]; if (!vector) oom(); { var const T * ptr1 = ptr; var T * ptr2 = vector; for (var unsigned long count = len; count > 0; count--) *ptr2++ = *ptr1++; } return vector; } template void xfree (const svector& v) { delete[] v.pointer; } // Extendable vectors. template // T must be a class with default constructor. class pushvector { protected: T * buffer; unsigned long alloc; // allocated size of buffer unsigned long index; // index into buffer, 0 <= index <= alloc public: // Constructor. When constructed, the vector is empty. pushvector (); // Destructor. ~pushvector (); // Forget the contents. void reset (); // Add an element at the end. void push (T); // Look at a the contents. unsigned long length () const { return index; } T operator[] (unsigned long i) const { DEBUG_ABORT(fprintf(stderr,"pushvector[]: length=%ld, i=%ld\n",(long)index,(long)i);) if (!(i < index)) abort(); return buffer[i]; } // Map over the contents. void nmap (id_fn& fn) { var unsigned long limit = index; for (var unsigned long i = 0; i < limit; i++) buffer[i] = fn(buffer[i]); } }; template inline pushvector::pushvector () { alloc = 1; // Must be > 0. buffer = new T[alloc]; if (!buffer) oom(); index = 0; } template inline pushvector::~pushvector () { delete[] buffer; } template inline void pushvector::reset () { T c; for (var unsigned long i = 0; i < index; i++) buffer[i] = c; index = 0; } template void pushvector::push (T c) { if (index >= alloc) { var unsigned long newalloc = 2*alloc; var T* newbuffer = new T[newalloc]; if (!newbuffer) oom(); for (var unsigned long i = 0; i < alloc; i++) newbuffer[i] = buffer[i]; delete[] buffer; buffer = newbuffer; alloc = newalloc; } // Now index < alloc. buffer[index++] = c; } // Concatenation. template void operator+= (pushvector& buf, const pushvector& more) { var unsigned long limit = more.length(); for (var unsigned long i = 0; i < limit; i++) buf.push(more[i]); } // Convert an integer representation to an integer. // Decodes str[start...], returns the value in "value", and returns the new // index, after the end of "value"'s representation. // Returns 0 if no integer representation was seen. finline unsigned long parse_integer (long& value, const char * str, unsigned long start = 0) { unsigned long i = start; bool negative = false; if (str[i] == '-') { i++; negative = true; } unsigned long after_sign = i; unsigned long val = 0; while ((str[i] >= '0') && (str[i] <= '9')) { val = val*10 + (str[i]-'0'); i++; } if (after_sign < i) { value = (negative ? -val : val); return i; } else return 0; } // Read a line from a stream. // Set eofp if EOF was seen before the next newline character. string read_line (istream& stream, bool& eofp) { var pushstring buffer; // Handling of eofp is tricky: EOF is reached when (!stream.good()) || (stream.get()==EOF). // This code is correct. It is just a bit slow... eofp = true; while (stream.good()) { var int c = stream.get(); if (c==EOF) break; if (c=='\n') { eofp = false; break; } buffer.push(c); } DEBUG_READLINE(fprintf(stderr,"Read line, eofp=%d, line=\"%s\"\n",eofp,(sstring)buffer.contents());) return buffer.contents(); } struct context_in_stream { istream& in_stream; unsigned long linenum; // Number of last read line. string line; // If linep true: last read line. bool linep; // Flag whether line is valid. context_in_stream (istream& in) : in_stream(in), linenum(0), linep(false) {} // Read the next line. void next_line () { var bool eofp; var string ln = read_line(in_stream,eofp); linenum++; if (eofp && (strlen(ln)==0)) linep = false; // EOF else { line = ln; linep = true; } } // Read the next line, if the last line has already been digested. void prepare_line () { if (!linep) next_line(); // Now linep is false if and only if EOF has been reached. } }; struct context_out_stream { ostream& out_stream; context_out_stream (ostream& out) : out_stream(out) {} }; // make `nl' a synonym for `endl'. inline ostream& nl (ostream& os) { return os << endl; } struct unidiff_hunk_params { // An unidiff hunk... long old_start_line; long old_line_count; long new_start_line; long new_line_count; }; // ... is introduced by a line of the form "@@ -%d,%d +%d,%d @@%s". finline bool parse_unidiff_hunk_line (const string& line, unidiff_hunk_params& hunk) { var unsigned long i; if (line.length() >= 11) if ((line[0]=='@') && (line[1]=='@') && (line[2]==' ') && (line[3]=='-')) if ((i = parse_integer(hunk.old_start_line,line,4))) if ((i+1 <= line.length()) && (line[i]==',')) if ((i = parse_integer(hunk.old_line_count,line,i+1))) if ((i+2 <= line.length()) && (line[i]==' ') && (line[i+1]=='+')) if ((i = parse_integer(hunk.new_start_line,line,i+2))) if ((i+1 <= line.length()) && (line[i]==',')) if ((i = parse_integer(hunk.new_line_count,line,i+1))) if ((i+3 <= line.length()) && (line[i]==' ') && (line[i+1]=='@') && (line[i+2]=='@')) return true; return false; } struct context : public context_in_stream, public context_out_stream { context (istream& in, ostream& out) : context_in_stream(in), context_out_stream(out) {} void do_hunks () { loop { prepare_line(); if (!linep) break; var unidiff_hunk_params hunk_params; if (!parse_unidiff_hunk_line(line,hunk_params)) break; var const long old_start_line = hunk_params.old_start_line; var const long new_start_line = hunk_params.new_start_line; var const long old_end_line = hunk_params.old_start_line + hunk_params.old_line_count - 1; var const long new_end_line = hunk_params.new_start_line + hunk_params.new_line_count - 1; var long old_line_count = hunk_params.old_line_count; var long new_line_count = hunk_params.new_line_count; var pushvector lines; typedef struct _piece_buffer { pushvector& destination; pushvector buffer; // Constructor. _piece_buffer (pushvector& dest) : destination(dest) {} } piece_buffer; typedef struct _two_piece_buffer { pushvector old_piece; pushvector new_piece; pushvector& destination; void done () // flush the two buffers { struct _prep_plus : public id_fn { string fn (const string& s) { return "+" + s; } } prep_plus; struct _prep_minus : public id_fn { string fn (const string& s) { return "-" + s; } } prep_minus; new_piece.nmap(prep_minus); destination += new_piece; old_piece.nmap(prep_plus); destination += old_piece; old_piece.reset(); new_piece.reset(); } void add_old_line (const char * line) { old_piece.push(line); } void add_new_line (const char * line) { new_piece.push(line); } // Constructor. _two_piece_buffer (pushvector& dest) : destination(dest) {} } two_piece_buffer; var two_piece_buffer buffer (lines); loop { linep = false; if (!((old_line_count > 0) || (new_line_count > 0))) break; prepare_line(); if (!linep) break; if (!(line.length() >= 1)) break; switch (line[0]) { case ' ': { buffer.done(); struct _prep_space : id_fn { string fn (const string& s) { return " " + s; } } prep_space; lines.push(prep_space(&((const char *)line)[1])); old_line_count--; new_line_count--; break; } case '-': { buffer.add_old_line(&((const char *)line)[1]); old_line_count--; break; } case '+': { buffer.add_new_line(&((const char *)line)[1]); new_line_count--; break; } default: goto break_loop; } if (0) break_loop: break; } buffer.done(); out_stream << "@@ -" << new_start_line << "," << (new_end_line-new_start_line+1) << " +" << old_start_line << "," << (old_end_line-old_start_line+1) << " @@" << nl; for (var unsigned long i = 0; i < lines.length(); i++) out_stream << lines[i] << nl; if ((old_line_count > 0) || (new_line_count > 0)) { cerr << program.name << ": Warning: Incomplete hunk ending at line " << linenum - (linep ? 1 : 0) << nl; DEBUG_LINES(fprintf(stderr, "%s: old_line_count=%ld, new_line_count=%ld, linep=%d, line=\"%s\"\n", program.name, old_line_count, new_line_count, (int)linep, (sstring)line);) } else if ((old_line_count != 0) || (new_line_count != 0)) { cerr << program.name << ": Warning: Overly long hunk ending at line " << linenum - (linep ? 1 : 0) << nl; DEBUG_LINES(fprintf(stderr, "%s: old_line_count=%ld, new_line_count=%ld, linep=%d, line=\"%s\"\n", program.name, old_line_count, new_line_count, (int)linep, (sstring)line);) } } } void do_files () { loop { var string headline; var bool have_headline = false; var string oldfile; var bool have_oldfile = false; var string newfile; var bool have_newfile = false; loop { prepare_line(); if (!linep) break; if (line.length() >= 4 && line[0]=='d' && line[1]=='i' && line[2]=='f' && line[3]=='f') { headline = line; have_headline = true; } else if (line.length() >= 4 && line[0]=='-' && line[1]=='-' && line[2]=='-' && line[3]==' ') break; else { // cerr << program.name << ": Warning: Junk at line " << linenum << "." << nl; out_stream << line << nl; } linep = false; } if (!linep) break; if (line.length() >= 4 && line[0]=='-' && line[1]=='-' && line[2]=='-' && line[3]==' ') { oldfile = &((const char *)line)[4]; have_oldfile = true; next_line(); } if (!linep) break; if (line.length() >= 4 && line[0]=='+' && line[1]=='+' && line[2]=='+' && line[3]==' ') { newfile = &((const char *)line)[4]; have_newfile = true; next_line(); } if (!linep) break; if (have_headline) out_stream << headline << nl; if (have_oldfile && have_newfile) { out_stream << "--- " << newfile << nl; out_stream << "+++ " << oldfile << nl; } do_hunks(); } } }; // Main program! main (int argc, char* argv[]) { unused argc; program.name = argv[0]; DEBUG_MALLOC_STATISTICS(program.statistics("Begin");) { var context main_context = context(cin,cout); main_context.do_files(); } DEBUG_MALLOC_STATISTICS(program.statistics("End");) if (cin.bad() || cout.bad()) return 1; return 0; } /* * Overrides for Emacs so that Emacs understands our indentation style. * Emacs will notice this stuff at the end of the file and automatically * adjust the settings for this buffer only. * This must remain at the end of the file. * --------------------------------------------------------------------------- * Local variables: * c-indent-level: 4 * c-brace-imaginary-offset: 0 * c-brace-offset: -4 * c-argdecl-indent: 4 * c-label-offset: -4 * c-continued-statement-offset: 4 * c-continued-brace-offset: 0 * c-tab-always-indent: nil * tab-width: 4 * compile-command: "g++ -O2 -fomit-frame-pointer -finline-functions -Wall ud2cd.cc -o ud2cd" * asm-compile-command: "g++ -O2 -fomit-frame-pointer -finline-functions -S -Wall ud2cd.cc" * asm-debug-compile-command: "g++ -O2 -fomit-frame-pointer -fno-default-inline -S -Wall ud2cd.cc" * End: */