// Convert a context diff to a unidiff. // Bruno Haible 7.5.1996 (Common Lisp), 7.5.1996 (C++) // Dedicated to David S. Miller "context diffs make my head spin" /* * Copyright (C) 1996, 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 // 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; } cerr << program.name << ": Error: Out of virtual memory.\n"; exit(1); } // 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); // Comparison. bool equal (const string& str1, const string& 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; } bool equal (const string& str1, const string& str2) { return (str1.length() == str2.length() && !memcmp((const char *) str1, (const char *) str2, str1.length())); } // 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]; { 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]; 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]; 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 contextdiff_halfhunk_params { // A contextdiff half hunk... long start_line; long end_line; }; // ... is introduced by a line of the form "*** %d,%d ****" or "*** %d ****". finline bool parse_contextdiff_halfhunk_line (const string& line, char fillchar, contextdiff_halfhunk_params& halfhunk) { var unsigned long i; if (line.length() >= 10) if ((line[0]==fillchar) && (line[1]==fillchar) && (line[2]==fillchar) && (line[3]==' ')) if ((i = parse_integer(halfhunk.start_line,line,4))) if ((i+1 <= line.length()) && (line[i]==',') ? (i = parse_integer(halfhunk.end_line,line,i+1)) : (halfhunk.end_line = (halfhunk.start_line > 0 ? halfhunk.start_line : halfhunk.start_line-1), true) ) if ((i+5 == line.length()) && (line[i]==' ') && (line[i+1]==fillchar) && (line[i+2]==fillchar) && (line[i+3]==fillchar) && (line[i+4]==fillchar)) return true; return false; } struct contextdiff_halfhunk : contextdiff_halfhunk_params { pushvector lines; unsigned long changes; // Constructor. contextdiff_halfhunk () : contextdiff_halfhunk_params (), lines (), changes (0) {} }; struct context : public context_in_stream, public context_out_stream { context (istream& in, ostream& out) : context_in_stream(in), context_out_stream(out) {} bool get_half_hunk (char fillchar, char insertchar, contextdiff_halfhunk& halfhunk) { prepare_line(); if (!linep) return false; if (!parse_contextdiff_halfhunk_line(line,fillchar,halfhunk)) return false; var bool in_change = false; loop { next_line(); if (!linep) break; var char indicator = 0; /* shut up g++ */ if ((line.length() >= 2) && (indicator = line[0], (indicator==' ') || (indicator=='!') || (indicator==insertchar)) && (line[1]==' ') ) { if (indicator=='!') { if (!in_change) halfhunk.changes++; in_change = true; } else in_change = false; halfhunk.lines.push(line); } else break; } return true; } void do_hunks () { loop { prepare_line(); if (!linep) break; if (!(line.length()>=15 && !memcmp((const char *)line, "***************", 15))) break; next_line(); var contextdiff_halfhunk old_half; var contextdiff_halfhunk new_half; if (!get_half_hunk('*','-',old_half)) break; if (!get_half_hunk('-','+',new_half)) break; var long& old_start_line = old_half.start_line; var long& old_end_line = old_half.end_line; var long old_lines_count = old_end_line - old_start_line + 1; var long& new_start_line = new_half.start_line; var long& new_end_line = new_half.end_line; var long new_lines_count = new_end_line - new_start_line + 1; var pushvector& old_lines = old_half.lines; var pushvector& new_lines = new_half.lines; // Consistency checks. if (!(old_lines.length()==0 || (long)old_lines.length()==old_lines_count)) { cerr << program.name << ": Warning: Hunk ending at line " << linenum - (linep ? 1 : 0) << " has wrong old line numbers" << nl; break; } if (!(new_lines.length()==0 || (long)new_lines.length()==new_lines_count)) { cerr << program.name << ": Warning: Hunk ending at line " << linenum - (linep ? 1 : 0) << " has wrong new line numbers" << nl; break; } if (old_lines.length()==0 && new_lines.length()==0) { cerr << program.name << ": Warning: Empty hunk ending at line " << linenum - (linep ? 1 : 0) << nl; break; } if (old_lines.length()==0) { if (!(new_half.changes == 0)) { cerr << program.name << ": Warning: Old lines missing in hunk ending at line " << linenum - (linep ? 1 : 0) << nl; break; } } else if (new_lines.length()==0) { if (!(old_half.changes == 0)) { cerr << program.name << ": Warning: New lines missing in hunk ending at line " << linenum - (linep ? 1 : 0) << nl; break; } } else { var const char * mismatchp; if (!(old_half.changes == new_half.changes)) { mismatchp = "changes count"; goto mismatch; } { var unsigned long old_i = 0; var unsigned long new_i = 0; loop { while (old_i < old_lines.length()) { var char indicator = old_lines[old_i][0]; if ((indicator==' ') || (indicator=='!')) break; old_i++; } while (new_i < new_lines.length()) { var char indicator = new_lines[new_i][0]; if ((indicator==' ') || (indicator=='!')) break; new_i++; } if ((old_i == old_lines.length()) && (new_i == new_lines.length())) break; if (old_i == old_lines.length()) { mismatchp = "too few old lines"; goto mismatch; } if (new_i == new_lines.length()) { mismatchp = "too few new lines"; goto mismatch; } var char old_indicator = old_lines[old_i][0]; var char new_indicator = new_lines[new_i][0]; if ((old_indicator==' ') && (new_indicator==' ')) { if (!equal(old_lines[old_i],new_lines[new_i])) { mismatchp = "different indicators"; goto mismatch; } old_i++; new_i++; } else if ((old_indicator=='!') && (new_indicator=='!')) { do { old_i++; } while ((old_i < old_lines.length()) && (old_lines[old_i][0]=='!')); do { new_i++; } while ((new_i < new_lines.length()) && (new_lines[new_i][0]=='!')); } else { mismatchp = "bad indicator"; goto mismatch; } } } if (0) { mismatch: cerr << program.name << ": Warning: Mismatch (" << mismatchp << ") between old and new lines in hunk ending at line " << linenum - (linep ? 1 : 0) << nl; break; } } // Output a unidiff hunk. out_stream << "@@ -" << old_start_line << "," << old_lines_count << " +" << new_start_line << "," << new_lines_count << " @@" << nl; if (old_lines.length()==0) { // All new_lines begin with " " oder "+ ", remove the space. for (var unsigned long new_i = 0; new_i < new_lines.length(); new_i++) { var string l = new_lines[new_i]; out_stream << l[0] << &((const char *)l)[2] << nl; } } else if (new_lines.length()==0) { // All old_lines begin with " " oder "- ", remove the space. for (var unsigned long old_i = 0; old_i < old_lines.length(); old_i++) { var string l = old_lines[old_i]; out_stream << l[0] << &((const char *)l)[2] << nl; } } else { var unsigned long old_i = 0; var unsigned long new_i = 0; loop { while (old_i < old_lines.length()) { var string l = old_lines[old_i]; var char indicator = l[0]; if ((indicator==' ') || (indicator=='!')) break; out_stream << '-' << &((const char *)l)[2] << nl; old_i++; } while (new_i < new_lines.length()) { var string l = new_lines[new_i]; var char indicator = l[0]; if ((indicator==' ') || (indicator=='!')) break; out_stream << '+' << &((const char *)l)[2] << nl; new_i++; } if ((old_i == old_lines.length()) && (new_i == new_lines.length())) break; var char old_indicator = old_lines[old_i][0]; if (old_indicator==' ') { // new_indicator = new_lines[new_i][0] is ' ' as well, // old_lines[old_i] and new_lines[new_i] are equal var string l = old_lines[old_i]; out_stream << ' ' << &((const char *)l)[2] << nl; old_i++; new_i++; } else if (old_indicator=='!') { // new_indicator = new_lines[new_i][0] is '!' as well. do { var string l = old_lines[old_i]; out_stream << '-' << &((const char *)l)[2] << nl; old_i++; } while ((old_i < old_lines.length()) && (old_lines[old_i][0]=='!')); do { var string l = new_lines[new_i]; out_stream << '+' << &((const char *)l)[2] << nl; new_i++; } while ((new_i < new_lines.length()) && (new_lines[new_i][0]=='!')); } } } } } 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 << "--- " << oldfile << nl; out_stream << "+++ " << newfile << 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: */