new file: bin/f200M.js
[GalaxyCodeBases.git] / tools / vbindiff / vbindiff.cpp
blobd762e777c3c3e56fcf9440e3a0f95d34ae74af13
1 //--------------------------------------------------------------------
2 //
3 // Visual Binary Diff
4 // Copyright 1995-2008 by Christopher J. Madsen
5 //
6 // Visual display of differences in binary files
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of
11 // the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 //--------------------------------------------------------------------
22 #include "config.h"
24 #include <ctype.h>
25 #include <limits.h>
26 #include <stdlib.h>
27 #include <string.h>
29 #include <algorithm>
30 #include <iostream>
31 #include <sstream>
32 #include <map>
33 #include <string>
34 #include <vector>
35 using namespace std;
37 #include "GetOpt/GetOpt.hpp"
39 #include "ConWin.hpp"
40 #include "FileIO.hpp"
42 const char titleString[] =
43 "\nVBinDiff " PACKAGE_VERSION "\nCopyright 1995-2008 Christopher J. Madsen";
45 void exitMsg(int status, const char* message);
46 void usage(bool showHelp=true, int exitStatus=0);
48 //====================================================================
49 // Type definitions:
51 typedef unsigned char Byte;
52 typedef unsigned short Word;
54 typedef Byte Command;
56 enum LockState { lockNeither = 0, lockTop, lockBottom };
58 //--------------------------------------------------------------------
59 // Strings:
61 typedef string String;
63 typedef String::size_type StrIdx;
64 typedef String::iterator StrItr;
65 typedef String::const_iterator StrConstItr;
67 //--------------------------------------------------------------------
68 // Vectors:
70 typedef vector<String> StrVec;
71 typedef StrVec::iterator SVItr;
72 typedef StrVec::const_iterator SVConstItr;
74 typedef StrVec::size_type VecSize;
76 //--------------------------------------------------------------------
77 // Map:
79 typedef map<VecSize, String> StrMap;
80 typedef StrMap::value_type SMVal;
81 typedef StrMap::iterator SMItr;
82 typedef StrMap::const_iterator SMConstItr;
84 //====================================================================
85 // Constants:
87 const Command cmmMove = 0x80;
89 const Command cmmMoveSize = 0x03;
90 const Command cmmMoveForward = 0x04;
91 const Command cmmMoveTop = 0x08;
92 const Command cmmMoveBottom = 0x10;
94 const Command cmmMoveByte = 0x00; // Move 1 byte
95 const Command cmmMoveLine = 0x01; // Move 1 line
96 const Command cmmMovePage = 0x02; // Move 1 page
97 const Command cmmMoveAll = 0x03; // Move to beginning or end
99 const Command cmmMoveBoth = cmmMoveTop|cmmMoveBottom;
101 const Command cmgGoto = 0x04; // Commands 4-7
102 const Command cmgGotoTop = 0x01;
103 const Command cmgGotoBottom = 0x02;
104 const Command cmgGotoBoth = cmgGotoTop|cmgGotoBottom;
105 const Command cmgGotoMask = ~cmgGotoBoth;
107 const Command cmNothing = 0;
108 const Command cmNextDiff = 1;
109 const Command cmQuit = 2;
110 const Command cmEditTop = 8;
111 const Command cmEditBottom = 9;
112 const Command cmUseTop = 10;
113 const Command cmUseBottom = 11;
114 const Command cmToggleASCII = 12;
115 const Command cmFind = 16; // Commands 16-19
117 const short leftMar = 11; // Starting column of hex display
118 const short leftMar2 = 61; // Starting column of ASCII display
120 const int lineWidth = 16; // Number of bytes displayed per line
122 const int promptHeight = 4; // Height of prompt window
123 const int inWidth = 10; // Width of input window (excluding border)
124 const int screenWidth = 80;
126 const int maxPath = 260;
128 const VecSize maxHistory = 2000;
130 const char hexDigits[] = "0123456789ABCDEF";
132 #include "tables.h" // ASCII and EBCDIC tables
134 //====================================================================
135 // Class Declarations:
137 void showEditPrompt();
138 void showPrompt();
140 class Difference;
142 union FileBuffer
144 Byte line[1][lineWidth];
145 Byte buffer[lineWidth];
146 }; // end FileBuffer
148 class FileDisplay
150 friend class Difference;
152 protected:
153 int bufContents;
154 FileBuffer* data;
155 const Difference* diffs;
156 File file;
157 char fileName[maxPath];
158 FPos offset;
159 ConWindow win;
160 bool writable;
161 int yPos;
162 public:
163 FileDisplay();
164 ~FileDisplay();
165 void init(int y, const Difference* aDiff=NULL,
166 const char* aFileName=NULL);
167 void resize();
168 void shutDown();
169 void display();
170 bool edit(const FileDisplay* other);
171 const Byte* getBuffer() const { return data->buffer; };
172 void move(int step) { moveTo(offset + step); };
173 void moveTo(FPos newOffset);
174 bool moveTo(const Byte* searchFor, int searchLen);
175 void moveToEnd(FileDisplay* other);
176 bool setFile(const char* aFileName);
177 protected:
178 void setByte(short x, short y, Byte b);
179 }; // end FileDisplay
181 class Difference
183 friend void FileDisplay::display();
185 protected:
186 FileBuffer* data;
187 const FileDisplay* file1;
188 const FileDisplay* file2;
189 int numDiffs;
190 public:
191 Difference(const FileDisplay* aFile1, const FileDisplay* aFile2);
192 ~Difference();
193 int compute();
194 int getNumDiffs() const { return numDiffs; };
195 void resize();
196 }; // end Difference
198 class InputManager
200 private:
201 char* buf; // The editing buffer
202 const char* restrict; // If non-NULL, only allow these chars
203 StrVec& history; // The history vector to use
204 StrMap historyOverlay; // Overlay of modified history entries
205 VecSize historyPos; // The current offset into history[]
206 int maxLen; // The size of buf (not including NUL)
207 int len; // The current length of the string
208 int i; // The current cursor position
209 bool upcase; // Force all characters to uppercase?
210 bool splitHex; // Entering space-separated hex bytes?
211 bool insert; // False for overstrike mode
213 public:
214 InputManager(char* aBuf, int aMaxLen, StrVec& aHistory);
215 bool run();
216 void setCharacters(const char* aRestriction) { restrict = aRestriction; };
217 void setSplitHex(bool val) { splitHex = val; };
218 void setUpcase(bool val) { upcase = val; };
220 private:
221 bool normalize(int pos);
222 void useHistory(int delta);
223 }; // end InputManager
225 //====================================================================
226 // Global Variables:
228 String lastSearch;
229 StrVec hexSearchHistory, textSearchHistory, positionHistory;
230 ConWindow promptWin,inWin;
231 FileDisplay file1, file2;
232 Difference diffs(&file1, &file2);
233 const char* displayTable = asciiDisplayTable;
234 const char* program_name; // Name under which this program was invoked
235 LockState lockState = lockNeither;
236 bool singleFile = false;
238 int numLines = 9; // Number of lines of each file to display
239 int bufSize = numLines * lineWidth;
240 int linesBetween = 1; // Number of lines of padding between files
242 // The number of bytes to move for each possible step size:
243 // See cmmMoveByte, cmmMoveLine, cmmMovePage
244 int steps[4] = {1, lineWidth, bufSize-lineWidth, 0};
247 //====================================================================
248 // Miscellaneous Functions:
249 //--------------------------------------------------------------------
250 // Beep the speaker:
252 #ifdef WIN32_CONSOLE // beep() is defined by ncurses
253 void beep()
255 MessageBeep(-1);
256 } // end beep
257 #endif // WIN32_CONSOLE
259 //--------------------------------------------------------------------
260 // Convert a character to uppercase:
262 // The standard toupper(c) isn't guaranteed for arbitrary integers.
264 int safeUC(int c)
266 return (c >= 0 && c <= UCHAR_MAX) ? toupper(c) : c;
267 } // end safeUC
269 //====================================================================
270 // Class Difference:
272 // Member Variables:
273 // file1, file2:
274 // The FileDisplay objects being compared
275 // numDiffs:
276 // The number of differences between the two FileDisplay buffers
277 // line/table:
278 // An array of bools for each byte in the FileDisplay buffers
279 // True marks differences
281 //--------------------------------------------------------------------
282 // Constructor:
284 // Input:
285 // aFile1, aFile2:
286 // Pointers to the FileDisplay objects to compare
288 Difference::Difference(const FileDisplay* aFile1, const FileDisplay* aFile2)
289 : data(NULL),
290 file1(aFile1),
291 file2(aFile2)
293 } // end Difference::Difference
295 //--------------------------------------------------------------------
296 Difference::~Difference()
298 delete [] reinterpret_cast<Byte*>(data);
299 } // end Difference::~Difference
301 //--------------------------------------------------------------------
302 // Compute differences:
304 // Input Variables:
305 // file1, file2: The files to compare
307 // Returns:
308 // The number of differences between the buffers
309 // -1 if both buffers are empty
311 // Output Variables:
312 // numDiffs: The number of differences between the buffers
314 int Difference::compute()
316 if (singleFile)
317 // We return 1 so that cmNextDiff won't keep searching:
318 return (file1->bufContents ? 1 : -1);
320 memset(data->buffer, 0, bufSize); // Clear the difference table
322 int different = 0;
324 const Byte* buf1 = file1->data->buffer;
325 const Byte* buf2 = file2->data->buffer;
327 int size = min(file1->bufContents, file2->bufContents);
329 int i;
330 for (i = 0; i < size; i++)
331 if (*(buf1++) != *(buf2++)) {
332 data->buffer[i] = true;
333 ++different;
336 size = max(file1->bufContents, file2->bufContents);
338 if (i < size) {
339 // One buffer has more data than the other:
340 different += size - i;
341 for (; i < size; i++)
342 data->buffer[i] = true; // These bytes are only in 1 buffer
343 } else if (!size)
344 return -1; // Both buffers are empty
346 numDiffs = different;
348 return different;
349 } // end Difference::compute
351 //--------------------------------------------------------------------
352 void Difference::resize()
354 if (singleFile) return;
356 if (data)
357 delete [] reinterpret_cast<Byte*>(data);
359 data = reinterpret_cast<FileBuffer*>(new Byte[bufSize]);
360 } // end Difference::resize
362 //====================================================================
363 // Class FileDisplay:
365 // Member Variables:
366 // bufContents:
367 // The number of bytes in the file buffer
368 // diffs:
369 // A pointer to the Difference object related to this file
370 // file:
371 // The file being displayed
372 // fileName:
373 // The relative pathname of the file being displayed
374 // offset:
375 // The position in the file of the first byte in the buffer
376 // win:
377 // The handle of the window used for display
378 // yPos:
379 // The vertical position of the display window
380 // buffer/line:
381 // The currently displayed portion of the file
383 //--------------------------------------------------------------------
384 // Constructor:
386 FileDisplay::FileDisplay()
387 : bufContents(0),
388 data(NULL),
389 diffs(NULL),
390 offset(0),
391 writable(false),
392 yPos(0)
394 fileName[0] = '\0';
395 } // end FileDisplay::FileDisplay
397 //--------------------------------------------------------------------
398 // Initialize:
400 // Creates the display window and opens the file.
402 // Input:
403 // y: The vertical position of the display window
404 // aDiff: The Difference object related to this buffer
405 // aFileName: The name of the file to display
407 void FileDisplay::init(int y, const Difference* aDiff,
408 const char* aFileName)
410 diffs = aDiff;
411 yPos = y;
413 win.init(0,y, screenWidth, (numLines + 1 + ((y==0) ? linesBetween : 0)),
414 cFileWin);
416 resize();
418 if (aFileName)
419 setFile(aFileName);
420 } // end FileDisplay::init
422 //--------------------------------------------------------------------
423 // Destructor:
425 FileDisplay::~FileDisplay()
427 shutDown();
428 CloseFile(file);
429 delete [] reinterpret_cast<Byte*>(data);
430 } // end FileDisplay::~FileDisplay
432 //--------------------------------------------------------------------
433 void FileDisplay::resize()
435 if (data)
436 delete [] reinterpret_cast<Byte*>(data);
438 data = reinterpret_cast<FileBuffer*>(new Byte[bufSize]);
440 // FIXME resize window
441 } // end FileDisplay::resize
443 //--------------------------------------------------------------------
444 // Shut down the file display:
446 // Deletes the display window.
448 void FileDisplay::shutDown()
450 win.close();
451 } // end FileDisplay::shutDown
453 //--------------------------------------------------------------------
454 // Display the file contents:
456 void FileDisplay::display()
458 if (!fileName[0]) return;
460 FPos lineOffset = offset;
462 short i,j,index,lineLength;
463 char buf[lineWidth + lineWidth/8 + 1];
464 buf[sizeof(buf)-1] = '\0';
466 char buf2[screenWidth+1];
467 buf2[screenWidth] = '\0';
469 memset(buf, ' ', sizeof(buf)-1);
471 for (i = 0; i < numLines; i++) {
472 // cerr << i << '\n';
473 char* str = buf2;
474 str +=
475 sprintf(str, "%04X %04X:",Word(lineOffset>>16),Word(lineOffset&0xFFFF));
477 lineLength = min(lineWidth, bufContents - i*lineWidth);
479 for (j = 0, index = -1; j < lineLength; j++) {
480 if (j % 8 == 0) {
481 *(str++) = ' ';
482 ++index;
484 str += sprintf(str, "%02X ", data->line[i][j]);
486 buf[index++] = displayTable[data->line[i][j]];
488 memset(buf + index, ' ', sizeof(buf) - index - 1);
489 memset(str, ' ', screenWidth - (str - buf2));
491 win.put(0,i+1, buf2);
492 win.put(leftMar2,i+1, buf);
494 if (diffs)
495 for (j = 0; j < lineWidth; j++)
496 if (diffs->data->line[i][j]) {
497 win.putAttribs(j*3 + leftMar + (j>7),i+1, cFileDiff,2);
498 win.putAttribs(j + leftMar2 + (j>7),i+1, cFileDiff,1);
500 lineOffset += lineWidth;
501 } // end for i up to numLines
503 win.update();
504 } // end FileDisplay::display
506 //--------------------------------------------------------------------
507 // Edit the file:
509 // Returns:
510 // true: File changed
511 // false: File did not change
513 bool FileDisplay::edit(const FileDisplay* other)
515 if (!bufContents && offset)
516 return false; // You must not be completely past EOF
518 if (!writable) {
519 File w = OpenFile(fileName, true);
520 if (w == InvalidFile) return false;
521 CloseFile(file);
522 file = w;
523 writable = true;
526 if (bufContents < bufSize)
527 memset(data->buffer + bufContents, 0, bufSize - bufContents);
529 short x = 0;
530 short y = 0;
531 bool hiNib = true;
532 bool ascii = false;
533 bool changed = false;
534 int key;
536 const Byte *const inputTable = ((displayTable == ebcdicDisplayTable)
537 ? ascii2ebcdicTable
538 : NULL); // No translation
540 showEditPrompt();
541 win.setCursor(leftMar,1);
542 ConWindow::showCursor();
544 for (;;) {
545 win.setCursor((ascii ? leftMar2 + x : leftMar + 3*x + !hiNib) + (x / 8),
546 y+1);
547 key = win.readKey();
549 switch (key) {
550 case KEY_ESCAPE: goto done;
551 case KEY_TAB:
552 hiNib = true;
553 ascii = !ascii;
554 break;
556 case KEY_DELETE:
557 case KEY_BACKSPACE:
558 case KEY_LEFT:
559 if (!hiNib)
560 hiNib = true;
561 else {
562 if (!ascii) hiNib = false;
563 if (--x < 0) x = lineWidth-1;
565 if (hiNib || (x < lineWidth-1))
566 break;
567 // else fall thru
568 case KEY_UP: if (--y < 0) y = numLines-1; break;
570 default: {
571 short newByte = -1;
572 if ((key == KEY_RETURN) && other &&
573 (other->bufContents > x + y*lineWidth)) {
574 newByte = other->data->line[y][x]; // Copy from other file
575 hiNib = ascii; // Always advance cursor to next byte
576 } else if (ascii) {
577 if (isprint(key)) newByte = (inputTable ? inputTable[key] : key);
578 } else { // hex
579 if (isdigit(key))
580 newByte = key - '0';
581 else if (isxdigit(key))
582 newByte = safeUC(key) - 'A' + 10;
583 if (newByte >= 0) {
584 if (hiNib)
585 newByte = (newByte * 0x10) | (0x0F & data->line[y][x]);
586 else
587 newByte |= 0xF0 & data->line[y][x];
588 } // end if valid digit entered
589 } // end else hex
590 if (newByte >= 0) {
591 changed = true;
592 setByte(x,y,newByte);
593 } else
594 break;
595 } // end default and fall thru
596 case KEY_RIGHT:
597 if (hiNib && !ascii)
598 hiNib = false;
599 else {
600 hiNib = true;
601 if (++x >= lineWidth) x = 0;
603 if (x || !hiNib)
604 break;
605 // else fall thru
606 case KEY_DOWN: if (++y >= numLines) y = 0; break;
608 } // end switch
610 } // end forever
612 done:
613 if (changed) {
614 promptWin.clear();
615 promptWin.border();
616 promptWin.put(30,1,"Save changes (Y/N):");
617 promptWin.update();
618 promptWin.setCursor(50,1);
619 key = promptWin.readKey();
620 if (safeUC(key) != 'Y') {
621 changed = false;
622 moveTo(offset); // Re-read buffer contents
623 } else {
624 SeekFile(file, offset);
625 WriteFile(file, data->buffer, bufContents);
628 showPrompt();
629 ConWindow::hideCursor();
630 return changed;
631 } // end FileDisplay::edit
633 //--------------------------------------------------------------------
634 void FileDisplay::setByte(short x, short y, Byte b)
636 if (x + y*lineWidth >= bufContents) {
637 if (x + y*lineWidth > bufContents) {
638 short y1 = bufContents / lineWidth;
639 short x1 = bufContents % lineWidth;
640 while (y1 <= numLines) {
641 while (x1 < lineWidth) {
642 if ((x1 == x) && (y1 == y)) goto done;
643 setByte(x1,y1,0);
644 ++x1;
646 x1 = 0;
647 ++y1;
648 } // end while y1
649 } // end if more than 1 byte past the end
650 done:
651 ++bufContents;
652 data->line[y][x] = b ^ 1; // Make sure it's different
653 } // end if past the end
655 if (data->line[y][x] != b) {
656 data->line[y][x] = b;
657 char str[3];
658 sprintf(str, "%02X", b);
659 win.setAttribs(cFileEdit);
660 win.put(leftMar + 3*x + (x / 8), y+1, str);
661 str[0] = displayTable[b];
662 str[1] = '\0';
663 win.put(leftMar2 + x + (x / 8), y+1, str);
664 win.setAttribs(cFileWin);
665 win.update();
667 } // end FileDisplay::setByte
669 //--------------------------------------------------------------------
670 // Change the file position:
672 // Changes the file offset and updates the buffer.
673 // Does not update the display.
675 // Input:
676 // step:
677 // The number of bytes to move
678 // A negative value means to move backward
680 // void FileDisplay::move(int step) /* Inline function */
682 //--------------------------------------------------------------------
683 // Change the file position:
685 // Changes the file offset and updates the buffer.
686 // Does not update the display.
688 // Input:
689 // newOffset:
690 // The new position of the file
692 void FileDisplay::moveTo(FPos newOffset)
694 if (!fileName[0]) return; // No file
696 offset = newOffset;
698 if (offset < 0)
699 offset = 0;
701 SeekFile(file, offset);
702 bufContents = ReadFile(file, data->buffer, bufSize);
703 } // end FileDisplay::moveTo
705 //--------------------------------------------------------------------
706 // Change the file position by searching:
708 // Changes the file offset and updates the buffer.
709 // Does not update the display.
711 // Input:
712 // searchFor: The bytes to search for
713 // searchLen: The number of bytes in searchFor
715 // Returns:
716 // true: The search was successful
717 // false: Search unsuccessful, file not moved
719 bool FileDisplay::moveTo(const Byte* searchFor, int searchLen)
721 if (!fileName[0]) return true; // No file, pretend success
723 // Using algorithm based on QuickSearch:
724 // http://www-igm.univ-mlv.fr/~lecroq/string/node19.htm
726 // Compute offset table:
727 int i;
728 int moveOver[256];
730 for (i = 0; i < 256; ++i)
731 moveOver[i] = searchLen + 1;
732 for (i = 0; i < searchLen; ++i)
733 moveOver[searchFor[i]] = searchLen - i;
735 // Prepare the search buffer:
737 const int
738 blockSize = 8 * 1024,
739 moveLength = searchLen,
740 restartAt = blockSize - moveLength,
741 fullStop = blockSize * 2 - moveLength;
743 Byte *const searchBuf = new Byte[2 * blockSize];
745 Byte *const copyTo = searchBuf + restartAt;
746 const Byte *const copyFrom = searchBuf + fullStop;
748 char *const readAt = reinterpret_cast<char*>(searchBuf) + blockSize;
750 FPos newPos = offset + 1;
752 SeekFile(file, newPos);
753 Size bytesRead = ReadFile(file, searchBuf, blockSize * 2);
754 int stopAt = bytesRead - moveLength;
756 // Start the search:
757 i = 0;
758 for (;;) {
759 if (stopAt < fullStop) ++stopAt;
761 while (i < stopAt) {
762 if (memcmp(searchFor, searchBuf + i, searchLen) == 0)
763 goto done;
765 i += moveOver[searchBuf[i + searchLen]]; // shift
766 } // end while more buffer to search
768 if (stopAt != fullStop) {
769 i = -1;
770 goto done;
771 } // Nothing more to read
773 newPos += blockSize;
774 i -= blockSize;
775 memcpy(copyTo, copyFrom, moveLength);
776 bytesRead = ReadFile(file, readAt, blockSize);
777 stopAt = bytesRead + blockSize - moveLength;
778 } // end forever
780 done:
781 delete [] searchBuf;
783 if (i < 0) return false; // No match
785 moveTo(newPos + i);
787 return true;
788 } // end FileDisplay::moveTo
790 //--------------------------------------------------------------------
791 // Move to the end of the file:
793 // Input:
794 // other: If non NULL, move both files to the end of the shorter file
796 void FileDisplay::moveToEnd(FileDisplay* other)
798 if (!fileName[0]) return; // No file
800 FPos end = SeekFile(file, 0, SeekEnd);
801 FPos diff = 0;
803 if (other) {
804 // If the files aren't currently at the same position,
805 // we want to keep them offset by the same amount:
806 diff = other->offset - offset;
808 end = min(end, SeekFile(other->file, 0, SeekEnd) - diff);
809 } // end if moving other file too
811 end -= steps[cmmMovePage];
812 end -= end % 0x10;
814 moveTo(end);
815 if (other) other->moveTo(end + diff);
816 } // end FileDisplay::moveToEnd
818 //--------------------------------------------------------------------
819 // Open a file for display:
821 // Opens the file, updates the filename display, and reads the start
822 // of the file into the buffer.
824 // Input:
825 // aFileName: The name of the file to open
827 // Returns:
828 // True: Operation successful
829 // False: Unable to open file (call ErrorMsg for error message)
831 bool FileDisplay::setFile(const char* aFileName)
833 strncpy(fileName, aFileName, maxPath);
834 fileName[maxPath-1] = '\0';
836 win.put(0,0, fileName);
837 win.putAttribs(0,0, cFileName, screenWidth);
838 win.update(); // FIXME
840 bufContents = 0;
841 file = OpenFile(fileName);
842 writable = false;
844 if (file == InvalidFile)
845 return false;
847 offset = 0;
848 bufContents = ReadFile(file, data->buffer, bufSize);
850 return true;
851 } // end FileDisplay::setFile
853 //====================================================================
854 // Main Program:
855 //--------------------------------------------------------------------
856 void calcScreenLayout(bool resize = true)
858 int screenX, screenY;
860 ConWindow::getScreenSize(screenX, screenY);
862 if (screenX < screenWidth) {
863 ostringstream err;
864 err << "The screen must be at least "
865 << screenWidth << " characters wide.";
866 exitMsg(2, err.str().c_str());
869 if (screenY < promptHeight + 4) {
870 ostringstream err;
871 err << "The screen must be at least "
872 << (promptHeight + 4) << " lines high.";
873 exitMsg(2, err.str().c_str());
876 numLines = screenY - promptHeight - (singleFile ? 1 : 2);
878 if (singleFile)
879 linesBetween = 0;
880 else {
881 linesBetween = numLines % 2;
882 numLines = (numLines - linesBetween) / 2;
885 bufSize = numLines * lineWidth;
887 steps[cmmMovePage] = bufSize-lineWidth;
889 // FIXME resize existing windows
890 } // end calcScreenLayout
892 //--------------------------------------------------------------------
893 void displayCharacterSet()
895 const bool isASCII = (displayTable == asciiDisplayTable);
897 promptWin.putAttribs(3,2, (isASCII ? cCurrentMode : cBackground), 5);
898 promptWin.putAttribs(9,2, (isASCII ? cBackground : cCurrentMode), 6);
900 promptWin.update();
901 } // end displayCharacterSet
903 //--------------------------------------------------------------------
904 void displayLockState()
906 #ifndef WIN32_CONSOLE // The Win32 version uses Ctrl & Alt instead
907 if (singleFile) return;
909 promptWin.putAttribs(63,1,
910 ((lockState == lockBottom) ? cCurrentMode : cBackground),
912 promptWin.putAttribs(63,2,
913 ((lockState == lockTop) ? cCurrentMode : cBackground),
914 11);
915 #endif
916 } // end displayLockState
918 //--------------------------------------------------------------------
919 // Print a message to stderr and exit:
921 // Input:
922 // status: The exit status to use
923 // message: The message to print
925 void exitMsg(int status, const char* message)
927 ConWindow::shutdown();
929 cerr << endl << message << endl;
930 exit(status);
931 } // end exitMsg
933 //--------------------------------------------------------------------
934 // Normalize the string in the input window:
936 // Does nothing unless splitHex mode is active.
938 // Input:
939 // pos: The position of the cursor in buf
941 // Returns:
942 // true: The input buffer was changed
943 // false: No changes were necessary
945 bool InputManager::normalize(int pos)
947 if (!splitHex) return false;
949 // Change D_ to 0D:
950 if (pos && buf[pos] == ' ' && buf[pos-1] != ' ') {
951 buf[pos] = buf[pos-1];
952 buf[pos-1] = '0';
953 if (pos == len) len += 2;
954 return true;
957 // Change _D to 0D:
958 if (pos < len && buf[pos] == ' ' && buf[pos+1] != ' ') {
959 buf[pos] = '0';
960 return true;
963 return false; // No changes necessary
964 } // end InputManager::normalize
966 //--------------------------------------------------------------------
967 // Get a string using inWin:
969 // Input:
970 // buf: The buffer where the string will be stored
971 // maxLen: The maximum number of chars to accept (not including NUL byte)
972 // history: The history vector to use
973 // restrict: If not NULL, accept only chars in this string
974 // upcase: If true, convert all chars with safeUC
976 void getString(char* buf, int maxLen, StrVec& history,
977 const char* restrict=NULL,
978 bool upcase=false, bool splitHex=false)
980 InputManager manager(buf, maxLen, history);
982 manager.setCharacters(restrict);
983 manager.setSplitHex(splitHex);
984 manager.setUpcase(upcase);
986 manager.run();
987 } // end getString
989 //--------------------------------------------------------------------
990 // Construct the InputManager object:
992 // Input:
993 // aBuf: The buffer where the string will be stored
994 // aMaxLen: The maximum number of chars to accept (not including NUL byte)
995 // aHistory: The history vector to use
997 InputManager::InputManager(char* aBuf, int aMaxLen, StrVec& aHistory)
998 : buf(aBuf),
999 restrict(NULL),
1000 history(aHistory),
1001 historyPos(aHistory.size()),
1002 maxLen(aMaxLen),
1003 len(0),
1004 i(0),
1005 upcase(false),
1006 splitHex(false),
1007 insert(true)
1009 } // end InputManager
1011 //--------------------------------------------------------------------
1012 // Run the main loop to get an input string:
1014 // Returns:
1015 // true: Enter was pressed
1016 // false: Escape was pressed
1018 bool InputManager::run()
1020 inWin.setCursor(2,1);
1022 bool inWinShown = false;
1023 bool done = false;
1024 bool aborted = true;
1026 ConWindow::showCursor(insert);
1028 memset(buf, ' ', maxLen);
1029 buf[maxLen] = '\0';
1031 // We need to be able to display complete bytes:
1032 if (splitHex && (maxLen % 3 == 1)) --maxLen;
1034 // Main input loop:
1035 while (!done) {
1036 inWin.put(2,1,buf);
1037 if (inWinShown) inWin.update(1); // Only update inside the box
1038 else { inWin.update(); inWinShown = true; } // Show the input window
1039 inWin.setCursor(2+i,1);
1040 int key = inWin.readKey();
1041 if (upcase) key = safeUC(key);
1043 switch (key) {
1044 case KEY_ESCAPE: buf[0] = '\0'; done = true; break; // ESC
1046 case KEY_RETURN: // Enter
1047 normalize(i);
1048 buf[len] = '\0';
1049 done = true;
1050 aborted = false;
1051 break;
1053 case KEY_BACKSPACE:
1054 case KEY_DELETE: // Backspace on most Unix terminals
1055 case 0x08: // Backspace (Ctrl-H)
1056 if (!i) continue; // Can't back up if we're at the beginning already
1057 if (splitHex) {
1058 if ((i % 3) == 0) {
1059 // At the beginning of a byte; erase last digit of previous byte:
1060 if (i == len) len -= 2;
1061 i -= 2;
1062 buf[i] = ' ';
1063 } else if (i < len && buf[i] != ' ') {
1064 // On the second digit; erase the first digit:
1065 buf[--i] = ' ';
1066 } else {
1067 // On a blank second digit; delete the entire byte:
1068 buf[--i] = ' ';
1069 memmove(buf + i, buf + i + 3, maxLen - i - 3);
1070 len -= 3;
1071 if (len < i) len = i;
1073 } else { // not splitHex mode
1074 memmove(buf + i - 1, buf + i, maxLen - i);
1075 buf[maxLen-1] = ' ';
1076 --len; --i;
1077 } // end else not splitHex mode
1078 break;
1080 case 0x04: // Ctrl-D
1081 case KEY_DC:
1082 if (i >= len) continue;
1083 if (splitHex) {
1084 i -= i%3;
1085 memmove(buf + i, buf + i + 3, maxLen - i - 3);
1086 len -= 3;
1087 if (len < i) len = i;
1088 } else {
1089 memmove(buf + i, buf + i + 1, maxLen - i - 1);
1090 buf[maxLen-1] = ' ';
1091 --len;
1092 } // end else not splitHex mode
1093 break;
1095 case KEY_IC:
1096 insert = !insert;
1097 ConWindow::showCursor(insert);
1098 break;
1100 case 0x02: // Ctrl-B
1101 case KEY_LEFT:
1102 if (i) {
1103 --i;
1104 if (splitHex) {
1105 normalize(i+1);
1106 if (i % 3 == 2) --i;
1109 break;
1111 case 0x06: // Ctrl-F
1112 case KEY_RIGHT:
1113 if (i < len) {
1114 ++i;
1115 if (splitHex) {
1116 normalize(i-1);
1117 if ((i < maxLen) && (i % 3 == 2)) ++i;
1120 break;
1122 case 0x0B: // Ctrl-K
1123 if (len > i) {
1124 memset(buf + i, ' ', len - i);
1125 len = i;
1127 break;
1129 case 0x01: // Ctrl-A
1130 case KEY_HOME:
1131 normalize(i);
1132 i = 0;
1133 break;
1135 case 0x05: // Ctrl-E
1136 case KEY_END:
1137 if (splitHex && (i < len))
1138 normalize(i);
1139 i = len;
1140 break;
1142 case 0x10: // Ctrl-P
1143 case KEY_UP:
1144 if (historyPos == 0) beep();
1145 else useHistory(-1);
1146 break;
1148 case 0x0E: // Ctrl-N
1149 case KEY_DOWN:
1150 if (historyPos == history.size()) beep();
1151 else useHistory(+1);
1152 break;
1154 default:
1155 if (isprint(key) && (!restrict || strchr(restrict, key))) {
1156 if (insert) {
1157 if (splitHex) {
1158 if (buf[i] == ' ') {
1159 if (i >= maxLen) continue;
1160 } else {
1161 if (len >= maxLen) continue;
1162 i -= i % 3;
1163 memmove(buf + i + 3, buf + i, maxLen - i - 3);
1164 buf[i+1] = ' ';
1165 len += 3;
1167 } // end if splitHex mode
1168 else {
1169 if (len >= maxLen) continue;
1170 memmove(buf + i + 1, buf + i, maxLen - i - 1);
1171 ++len;
1172 } // end else not splitHex mode
1173 } else { // overstrike mode
1174 if (i >= maxLen) continue;
1175 } // end else overstrike mode
1176 buf[i++] = key;
1177 if (splitHex && (i < maxLen) && (i % 3 == 2))
1178 ++i;
1179 if (i > len) len = i;
1180 } // end if is acceptable character to insert
1181 } // end switch key
1182 } // end while not done
1184 // Hide the input window & cursor:
1185 ConWindow::hideCursor();
1186 inWin.hide();
1188 // Record the result in the history:
1189 if (!aborted && len) {
1190 String newValue(buf);
1192 SVItr exists = find(history.begin(), history.end(), newValue);
1193 if (exists != history.end())
1194 // Already in history. Move it to the end:
1195 rotate(exists, exists + 1, history.end());
1196 else if (history.size() >= maxHistory) {
1197 // History is full. Replace the first entry & move it to the end:
1198 history.front().swap(newValue);
1199 rotate(history.begin(), history.begin() + 1, history.end());
1200 } else
1201 // Just append to history:
1202 history.push_back(newValue);
1203 } // end if we have a value to store in the history
1205 return !aborted;
1206 } // end run
1208 //--------------------------------------------------------------------
1209 // Switch the current input line with one from the history:
1211 // Input:
1212 // delta: The number to add to historyPos (-1 previous, +1 next)
1214 void InputManager::useHistory(int delta)
1216 // Clean up the current string if necessary:
1217 normalize(i);
1219 // Update the history overlay if necessary:
1220 // We always store the initial value, because it doesn't
1221 // correspond to a valid entry in history.
1222 if (len || historyPos == history.size())
1223 historyOverlay[historyPos].assign(buf, len);
1225 // Look for an entry in the overlay:
1226 SMItr itr = historyOverlay.find(historyPos += delta);
1228 String& s = ((itr == historyOverlay.end())
1229 ? history[historyPos] : itr->second);
1231 // Store the new string in the buffer:
1232 memset(buf, ' ', maxLen);
1233 i = len = min(static_cast<VecSize>(maxLen), s.length());
1234 memcpy(buf, s.c_str(), len);
1235 } // end useHistory
1237 //--------------------------------------------------------------------
1238 // Convert hex string to bytes:
1240 // Input:
1241 // buf: Must contain a well-formed string of hex characters
1242 // (each byte must be separated by spaces)
1244 // Output:
1245 // buf: Contains the translated bytes
1247 // Returns:
1248 // The number of bytes in buf
1250 int packHex(Byte* buf)
1252 unsigned long val;
1254 char* in = reinterpret_cast<char*>(buf);
1255 Byte* out = buf;
1257 while (*in) {
1258 if (*in == ' ')
1259 ++in;
1260 else {
1261 val = strtoul(in, &in, 16);
1262 *(out++) = Byte(val);
1266 return out - buf;
1267 } // end packHex
1269 //--------------------------------------------------------------------
1270 // Position the input window:
1272 // Input:
1273 // cmd: Indicates where the window should be positioned
1274 // width: The width of the window
1275 // title: The title for the window
1277 void positionInWin(Command cmd, short width, const char* title)
1279 inWin.resize(width, 3);
1280 inWin.move((screenWidth-width)/2,
1281 ((!singleFile && (cmd & cmgGotoBottom))
1282 ? ((cmd & cmgGotoTop)
1283 ? numLines + linesBetween // Moving both
1284 : numLines + numLines/2 + 1 + linesBetween) // Moving bottom
1285 : numLines/2)); // Moving top
1287 inWin.border();
1288 inWin.put((width-strlen(title))/2,0, title);
1289 } // end positionInWin
1291 //--------------------------------------------------------------------
1292 // Display prompt window for editing:
1294 void showEditPrompt()
1296 promptWin.clear();
1297 promptWin.border();
1298 promptWin.put(3,1, "Arrow keys move cursor TAB hex\x3C\x3E"
1299 "ASCII ESC done");
1300 if (displayTable == ebcdicDisplayTable)
1301 promptWin.put(42,1, "EBCDIC");
1303 promptWin.putAttribs( 3,1, cPromptKey, 10);
1304 promptWin.putAttribs(33,1, cPromptKey, 3);
1305 promptWin.putAttribs(54,1, cPromptKey, 3);
1307 if (!singleFile) {
1308 promptWin.put(25,2, "RET copy byte from other file");
1309 promptWin.putAttribs(25,2, cPromptKey, 3);
1311 promptWin.update();
1312 } // end showEditPrompt
1314 //--------------------------------------------------------------------
1315 // Display prompt window:
1317 void showPrompt()
1319 promptWin.clear();
1320 promptWin.border();
1322 #ifdef WIN32_CONSOLE
1323 promptWin.put(1,1, "Arrow keys move F find "
1324 "RET next difference ESC quit ALT freeze top");
1325 promptWin.put(1,2, "C ASCII/EBCDIC E edit file "
1326 "G goto position Q quit CTRL freeze bottom");
1327 const short
1328 topBotLength = 4,
1329 topLength = 15;
1330 #else // curses
1331 promptWin.put(1,1, "Arrow keys move F find "
1332 "RET next difference ESC quit T move top");
1333 promptWin.put(1,2, "C ASCII/EBCDIC E edit file "
1334 "G goto position Q quit B move bottom");
1335 const short
1336 topBotLength = 1,
1337 topLength = 10;
1338 #endif
1340 promptWin.putAttribs( 1,1, cPromptKey, 10);
1341 promptWin.putAttribs(18,1, cPromptKey, 1);
1342 promptWin.putAttribs(30,1, cPromptKey, 3);
1343 promptWin.putAttribs(51,1, cPromptKey, 3);
1344 promptWin.putAttribs( 1,2, cPromptKey, 1);
1345 promptWin.putAttribs(18,2, cPromptKey, 1);
1346 promptWin.putAttribs(32,2, cPromptKey, 1);
1347 promptWin.putAttribs(53,2, cPromptKey, 1);
1348 if (singleFile) {
1349 // Erase "move top" & "move bottom":
1350 promptWin.putChar(61,1, ' ', topLength);
1351 promptWin.putChar(61,2, ' ', topLength + 3);
1352 } else {
1353 promptWin.putAttribs(61,1, cPromptKey, topBotLength);
1354 promptWin.putAttribs(61,2, cPromptKey, topBotLength);
1356 displayLockState();
1357 displayCharacterSet(); // Calls promptWin.update()
1358 } // end showPrompt
1360 //--------------------------------------------------------------------
1361 // Initialize program:
1363 // Returns:
1364 // True: Initialization complete
1365 // False: Error
1367 bool initialize()
1369 if (!ConWindow::startup())
1370 return false;
1372 ConWindow::hideCursor();
1374 calcScreenLayout(false);
1376 inWin.init(0,0, inWidth+2,3, cPromptBdr);
1377 inWin.border();
1378 inWin.put((inWidth-4)/2,0, " Goto ");
1379 inWin.setAttribs(cPromptWin);
1380 inWin.hide();
1382 int y;
1383 if (singleFile) y = numLines + 1;
1384 else y = numLines * 2 + linesBetween + 2;
1386 promptWin.init(0,y, screenWidth,promptHeight, cBackground);
1387 showPrompt();
1389 if (!singleFile) diffs.resize();
1391 file1.init(0, (singleFile ? NULL : &diffs));
1393 if (!singleFile) file2.init(numLines + linesBetween + 1, &diffs);
1395 return true;
1396 } // end initialize
1398 //--------------------------------------------------------------------
1399 // Get a command from the keyboard:
1401 // Returns:
1402 // Command code
1404 #ifdef WIN32_CONSOLE
1405 Command getCommand()
1407 KEY_EVENT_RECORD e;
1408 Command cmd = cmNothing;
1410 while (cmd == cmNothing) {
1411 ConWindow::readKey(e);
1413 switch (safeUC(e.uChar.AsciiChar)) {
1414 case KEY_RETURN: // Enter
1415 case ' ': // Space
1416 cmd = cmNextDiff;
1417 break;
1419 case 0x05: // Ctrl+E
1420 case 'E':
1421 if (e.dwControlKeyState & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED))
1422 cmd = cmEditBottom;
1423 else
1424 cmd = cmEditTop;
1425 break;
1427 case 'F':
1428 if (e.dwControlKeyState & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED))
1429 cmd = cmFind|cmgGotoBottom;
1430 else
1431 cmd = cmFind|cmgGotoBoth;
1432 break;
1434 case 0x06: // Ctrl+F
1435 cmd = cmFind|cmgGotoTop;
1436 break;
1438 case 'G':
1439 if (e.dwControlKeyState & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED))
1440 cmd = cmgGoto|cmgGotoBottom;
1441 else
1442 cmd = cmgGoto|cmgGotoBoth;
1443 break;
1445 case 0x07: // Ctrl+G
1446 cmd = cmgGoto|cmgGotoTop;
1447 break;
1449 case KEY_ESCAPE: // Esc
1450 case 0x03: // Ctrl+C
1451 case 'Q':
1452 cmd = cmQuit;
1453 break;
1455 case 'C': cmd = cmToggleASCII; break;
1457 default: // Try extended codes
1458 switch (e.wVirtualKeyCode) {
1459 case VK_DOWN: cmd = cmmMove|cmmMoveLine|cmmMoveForward; break;
1460 case VK_RIGHT: cmd = cmmMove|cmmMoveByte|cmmMoveForward; break;
1461 case VK_NEXT: cmd = cmmMove|cmmMovePage|cmmMoveForward; break;
1462 case VK_END: cmd = cmmMove|cmmMoveAll|cmmMoveForward; break;
1463 case VK_LEFT: cmd = cmmMove|cmmMoveByte; break;
1464 case VK_UP: cmd = cmmMove|cmmMoveLine; break;
1465 case VK_PRIOR: cmd = cmmMove|cmmMovePage; break;
1466 case VK_HOME: cmd = cmmMove|cmmMoveAll; break;
1467 } // end switch virtual key code
1468 break;
1469 } // end switch ASCII code
1470 } // end while no command
1472 if (cmd & cmmMove) {
1473 if ((e.dwControlKeyState & (LEFT_ALT_PRESSED|RIGHT_ALT_PRESSED)) == 0)
1474 cmd |= cmmMoveTop;
1475 if ((e.dwControlKeyState & (LEFT_CTRL_PRESSED|RIGHT_CTRL_PRESSED)) == 0)
1476 cmd |= cmmMoveBottom;
1477 } // end if move command
1479 return cmd;
1480 } // end getCommand
1482 #else // using curses interface
1483 Command getCommand()
1485 Command cmd = cmNothing;
1487 while (cmd == cmNothing) {
1488 int e = promptWin.readKey();
1490 switch (safeUC(e)) {
1491 case KEY_RETURN: // Enter
1492 case ' ': // Space
1493 cmd = cmNextDiff;
1494 break;
1496 case 'E':
1497 if (lockState == lockTop)
1498 cmd = cmEditBottom;
1499 else
1500 cmd = cmEditTop;
1501 break;
1503 case 'F':
1504 cmd = cmFind;
1505 if (lockState != lockTop) cmd |= cmgGotoTop;
1506 if (lockState != lockBottom) cmd |= cmgGotoBottom;
1507 break;
1509 case 'G':
1510 cmd = cmgGoto;
1511 if (lockState != lockTop) cmd |= cmgGotoTop;
1512 if (lockState != lockBottom) cmd |= cmgGotoBottom;
1513 break;
1515 case KEY_ESCAPE:
1516 case 0x03: // Ctrl+C
1517 case 'Q':
1518 cmd = cmQuit;
1519 break;
1521 case 'C': cmd = cmToggleASCII; break;
1523 case 'B': if (!singleFile) cmd = cmUseBottom; break;
1524 case 'T': if (!singleFile) cmd = cmUseTop; break;
1526 case KEY_DOWN: cmd = cmmMove|cmmMoveLine|cmmMoveForward; break;
1527 case KEY_RIGHT: cmd = cmmMove|cmmMoveByte|cmmMoveForward; break;
1528 case KEY_NPAGE: cmd = cmmMove|cmmMovePage|cmmMoveForward; break;
1529 case KEY_END: cmd = cmmMove|cmmMoveAll|cmmMoveForward; break;
1530 case KEY_LEFT: cmd = cmmMove|cmmMoveByte; break;
1531 case KEY_UP: cmd = cmmMove|cmmMoveLine; break;
1532 case KEY_PPAGE: cmd = cmmMove|cmmMovePage; break;
1533 case KEY_HOME: cmd = cmmMove|cmmMoveAll; break;
1534 } // end switch ASCII code
1535 } // end while no command
1537 if (cmd & cmmMove) {
1538 if (lockState != lockTop) cmd |= cmmMoveTop;
1539 if (lockState != lockBottom) cmd |= cmmMoveBottom;
1540 } // end if move command
1542 return cmd;
1543 } // end getCommand
1544 #endif // end else curses interface
1546 //--------------------------------------------------------------------
1547 // Get a file position and move there:
1549 void gotoPosition(Command cmd)
1551 positionInWin(cmd, inWidth+2, " Goto ");
1553 const int maxLen = inWidth-2;
1554 char buf[maxLen+1];
1556 getString(buf, maxLen, positionHistory, hexDigits, true);
1558 if (!buf[0])
1559 return;
1561 FPos pos = strtoul(buf, NULL, 16);
1563 if (cmd & cmgGotoTop)
1564 file1.moveTo(pos);
1565 if (cmd & cmgGotoBottom)
1566 file2.moveTo(pos);
1567 } // end gotoPosition
1569 //--------------------------------------------------------------------
1570 // Search for text or bytes in the files:
1572 void searchFiles(Command cmd)
1574 const bool havePrev = !lastSearch.empty();
1576 positionInWin(cmd, (havePrev ? 47 : 32), " Find ");
1578 inWin.put(2, 1,"H Hex search T Text search");
1579 inWin.putAttribs( 2,1, cPromptKey, 1);
1580 inWin.putAttribs(17,1, cPromptKey, 1);
1581 if (havePrev) {
1582 inWin.put(33, 1,"N Next match");
1583 inWin.putAttribs(33,1, cPromptKey, 1);
1585 inWin.update();
1586 int key = safeUC(inWin.readKey());
1588 bool hex = false;
1590 if (key == KEY_ESCAPE) {
1591 inWin.hide();
1592 return;
1593 } else if (key == 'H')
1594 hex = true;
1596 if (key == 'N' && havePrev) {
1597 inWin.hide();
1598 } else {
1599 positionInWin(cmd, screenWidth, (hex ? " Find Hex Bytes" : " Find Text "));
1601 const int maxLen = screenWidth-4;
1602 Byte buf[maxLen+1];
1603 int searchLen;
1605 if (hex) {
1606 getString(reinterpret_cast<char*>(buf), maxLen, hexSearchHistory, hexDigits, true, true);
1607 searchLen = packHex(buf);
1608 } else {
1609 getString(reinterpret_cast<char*>(buf), maxLen, textSearchHistory);
1611 searchLen = strlen(reinterpret_cast<char*>(buf));
1612 if (displayTable == ebcdicDisplayTable) {
1613 for (int i = 0; i < searchLen; ++i)
1614 buf[i] = ascii2ebcdicTable[buf[i]];
1615 } // end if in EBCDIC mode
1616 } // end else text search
1618 if (!searchLen) return;
1620 lastSearch.assign(reinterpret_cast<char*>(buf), searchLen);
1621 } // end else need to read search string
1623 bool problem = false;
1624 const Byte *const searchPattern =
1625 reinterpret_cast<const Byte*>(lastSearch.c_str());
1627 if ((cmd & cmgGotoTop) &&
1628 !file1.moveTo(searchPattern, lastSearch.length()))
1629 problem = true;
1630 if ((cmd & cmgGotoBottom) &&
1631 !file2.moveTo(searchPattern, lastSearch.length()))
1632 problem = true;
1634 if (problem) beep();
1635 } // end searchFiles
1637 //--------------------------------------------------------------------
1638 // Handle a command:
1640 // Input:
1641 // cmd: The command to be handled
1643 void handleCmd(Command cmd)
1645 if (cmd & cmmMove) {
1646 int step = steps[cmd & cmmMoveSize];
1648 if ((cmd & cmmMoveForward) == 0)
1649 step *= -1; // We're moving backward
1651 if ((cmd & cmmMoveForward) && !step) {
1652 if (cmd & cmmMoveTop)
1653 file1.moveToEnd((!singleFile && (cmd & cmmMoveBottom)) ? &file2 : NULL);
1654 else
1655 file2.moveToEnd(NULL);
1656 } else {
1657 if (cmd & cmmMoveTop) {
1658 if (step)
1659 file1.move(step);
1660 else
1661 file1.moveTo(0);
1662 } // end if moving top file
1664 if (cmd & cmmMoveBottom) {
1665 if (step)
1666 file2.move(step);
1667 else
1668 file2.moveTo(0);
1669 } // end if moving bottom file
1670 } // end else not moving to end
1671 } // end if move
1672 else if ((cmd & cmgGotoMask) == cmgGoto)
1673 gotoPosition(cmd);
1674 else if ((cmd & cmgGotoMask) == cmFind)
1675 searchFiles(cmd);
1676 else if (cmd == cmNextDiff) {
1677 if (lockState) {
1678 lockState = lockNeither;
1679 displayLockState();
1681 do {
1682 file1.move(bufSize);
1683 file2.move(bufSize);
1684 } while (!diffs.compute());
1685 } // end else if cmNextDiff
1686 else if (cmd == cmUseTop) {
1687 if (lockState == lockBottom)
1688 lockState = lockNeither;
1689 else
1690 lockState = lockBottom;
1691 displayLockState();
1693 else if (cmd == cmUseBottom) {
1694 if (lockState == lockTop)
1695 lockState = lockNeither;
1696 else
1697 lockState = lockTop;
1698 displayLockState();
1700 else if (cmd == cmToggleASCII) {
1701 displayTable = ((displayTable == asciiDisplayTable)
1702 ? ebcdicDisplayTable
1703 : asciiDisplayTable );
1704 displayCharacterSet();
1706 else if (cmd == cmEditTop)
1707 file1.edit(singleFile ? NULL : &file2);
1708 else if (cmd == cmEditBottom)
1709 file2.edit(&file1);
1711 // Make sure we haven't gone past the end of both files:
1712 while (diffs.compute() < 0) {
1713 file1.move(-steps[cmmMovePage]);
1714 file2.move(-steps[cmmMovePage]);
1717 file1.display();
1718 file2.display();
1719 } // end handleCmd
1721 //====================================================================
1722 // Initialization and option processing:
1723 //====================================================================
1724 // Display license information and exit:
1726 bool license(GetOpt*, const GetOpt::Option*, const char*,
1727 GetOpt::Connection, const char*, int*)
1729 puts(titleString);
1730 puts("\n"
1731 "This program is free software; you can redistribute it and/or\n"
1732 "modify it under the terms of the GNU General Public License as\n"
1733 "published by the Free Software Foundation; either version 2 of\n"
1734 "the License, or (at your option) any later version.\n"
1735 "\n"
1736 "This program is distributed in the hope that it will be useful,\n"
1737 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
1738 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
1739 "GNU General Public License for more details.\n"
1740 "\n"
1741 "You should have received a copy of the GNU General Public License\n"
1742 "along with this program; if not, see <http://www.gnu.org/licenses/>."
1745 exit(0);
1746 return false; // Never happens
1747 } // end license
1749 //--------------------------------------------------------------------
1750 // Display version & usage information and exit:
1752 // Input:
1753 // showHelp: True means display usage information
1754 // exitStatus: Status code to pass to exit()
1756 void usage(bool showHelp, int exitStatus)
1758 if (exitStatus > 1)
1759 cerr << "Try `" << program_name << " --help' for more information.\n";
1760 else {
1761 cout << titleString << endl;
1763 if (showHelp)
1764 cout << "Usage: " << program_name << " FILE1 [FILE2]\n\
1765 Compare FILE1 and FILE2 byte by byte.\n\
1766 If FILE2 is omitted, just display FILE1.\n\
1768 Options:\n\
1769 --help display this help information and exit\n\
1770 -L, --license display license & warranty information and exit\n\
1771 -V, --version display version information and exit\n";
1774 exit(exitStatus);
1775 } // end usage
1777 bool usage(GetOpt* getopt, const GetOpt::Option* option,
1778 const char*, GetOpt::Connection, const char*, int*)
1780 usage(option->shortName == '?');
1781 return false; // Never happens
1782 } // end usage
1784 //--------------------------------------------------------------------
1785 // Handle options:
1787 // Input:
1788 // argc, argv: The parameters passed to main
1790 // Output:
1791 // argc, argv:
1792 // Modified to list only the non-option arguments
1793 // Note: argv[0] may not be the executable name
1795 void processOptions(int& argc, char**& argv)
1797 static const GetOpt::Option options[] =
1799 { '?', "help", NULL, 0, &usage },
1800 { 'L', "license", NULL, 0, &license },
1801 { 'V', "version", NULL, 0, &usage },
1802 { 0 }
1805 GetOpt getopt(options);
1806 int argi = getopt.process(argc, const_cast<const char**>(argv));
1807 if (getopt.error)
1808 usage(true, 1);
1810 if (argi >= argc)
1811 argc = 1; // No arguments
1812 else {
1813 argc -= --argi; // Reduce argc by number of arguments used
1814 argv += argi; // And adjust argv[1] to the next argument
1816 } // end processOptions
1818 //====================================================================
1819 // Main Program:
1820 //====================================================================
1821 int main(int argc, char* argv[])
1823 if ((program_name = strrchr(argv[0], '\\')))
1824 // Isolate the filename:
1825 ++program_name;
1826 else
1827 program_name = argv[0];
1829 processOptions(argc, argv);
1831 if (argc < 2 || argc > 3)
1832 usage(1);
1834 cout << "\
1835 VBinDiff " PACKAGE_VERSION ", Copyright 1995-2008 Christopher J. Madsen\n\
1836 VBinDiff comes with ABSOLUTELY NO WARRANTY; for details type `vbindiff -L'.\n";
1838 singleFile = (argc == 2);
1839 if (!initialize()) {
1840 cerr << '\n' << program_name << ": Unable to initialize windows\n";
1841 return 1;
1845 ostringstream errMsg;
1847 if (!file1.setFile(argv[1])) {
1848 const char* errStr = ErrorMsg();
1849 errMsg << "Unable to open " << argv[1] << ": " << errStr;
1851 else if (!singleFile && !file2.setFile(argv[2])) {
1852 const char* errStr = ErrorMsg();
1853 errMsg << "Unable to open " << argv[2] << ": " << errStr;
1855 string error(errMsg.str());
1856 if (error.length())
1857 exitMsg(1, error.c_str());
1858 } // end block around errMsg
1860 diffs.compute();
1862 file1.display();
1863 file2.display();
1865 Command cmd;
1866 while ((cmd = getCommand()) != cmQuit)
1867 handleCmd(cmd);
1869 file1.shutDown();
1870 file2.shutDown();
1871 inWin.close();
1872 promptWin.close();
1874 ConWindow::shutdown();
1876 return 0;
1877 } // end main
1879 //--------------------------------------------------------------------
1880 // Local Variables:
1881 // c-file-style: "cjm"
1882 // End: