1 /*
2 * This program is free software: you can redistribute it and/or modify it
3 * under the terms of the GNU General Public License as published by the Free
4 * Software Foundation, either version 3 of the License, or (at your option)
5 * any later version.
6 *
7 * This program is distributed in the hope that it will be useful, but
8 * WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
10 * Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License along
13 * with this program. If not, see <http://www.gnu.org/licenses/>.
14 */
15
16 #include "gui.h"
17
18 /* #####################################################################
19 ## ANCILLARY MATHEMATICAL FUNCTIONS ##
20 ##################################################################### */
21
calculate_dimensions(int * width,int * height,int * total_blocks,unsigned long * bytes_per_block,unsigned long largest_file_size,int * blocks_with_excess_byte)22 static void calculate_dimensions(int *width, int *height, int *total_blocks,
23 unsigned long *bytes_per_block,
24 unsigned long largest_file_size,
25 int *blocks_with_excess_byte)
26 {
27 /* Acquire the dimensions of window */
28 getmaxyx(stdscr, *height, *width);
29
30 /* If the window dimensions are too small, exit. */
31 if ((*height < 16) || (*width < 10)) {
32 endwin();
33 printf("Terminal dimensions are too small to proceed. "
34 "Increase the size to a minimum of 16w x 10h.\n\n");
35 exit(1);
36 }
37
38 /* Calculate how many bytes are held in a block.
39 Each block holds a minimum of one byte. The number is
40 biggest file size / # blocks ((width-SIDE_MARGIN) * (height-11))
41 rounded to the next number up. */
42 *total_blocks = (*width - SIDE_MARGIN*2) *
43 (*height - VERTICAL_BLACK_SPACE);
44 *bytes_per_block = (unsigned long) largest_file_size /
45 (*total_blocks);
46 *blocks_with_excess_byte = (unsigned long) largest_file_size %
47 (*total_blocks);
48
49 return;
50 }
51
calculate_current_block(int total_blocks,unsigned long file_offset,unsigned long * offset_index)52 static int calculate_current_block(int total_blocks, unsigned long file_offset,
53 unsigned long *offset_index)
54 {
55 /* With a given offset, calculate which element it corresponds to
56 in the offset_index. */
57 int i, current_block = 0;
58 for (i = 0; i < total_blocks; i++) {
59
60 /* Go block by block, and see if our offset is greater than
61 that of the block's. */
62 if (file_offset >= offset_index[i]) {
63 current_block = i;
64 } else {
65 break;
66 }
67
68 /* Break if we reach EOF. */
69 if ((i+1 < total_blocks) && (offset_index[i+1] == offset_index[i]))
70 break;
71 }
72
73 return current_block;
74 }
75
76 /* computes the width of the hex offset margin on the left */
calculate_max_offset_characters(unsigned long fsz)77 static int calculate_max_offset_characters(unsigned long fsz)
78 {
79 char s[32];
80 sprintf(s, "%lX", fsz);
81 return(strlen(s));
82 }
83
84 /* #####################################################################
85 ## SCREEN HANDLING FUNCTIONS ##
86 ##################################################################### */
87
raw_to_ascii(char input)88 static char raw_to_ascii(char input)
89 {
90 if ((input > 31) && (input < 127)) return input;
91 return '.';
92 }
93
94 /* #####################################################################
95 ## HANDLE MOUSE ACTIONS ##
96 ##################################################################### */
97
mouse_clicked(unsigned long * file_offset,unsigned long * offset_index,int width,int height,int total_blocks,char * mode,int mouse_x,int mouse_y,int action)98 static void mouse_clicked(unsigned long *file_offset, unsigned long
99 *offset_index, int width, int height,
100 int total_blocks, char *mode,
101 int mouse_x, int mouse_y, int action)
102 {
103 int index;
104
105 /* In overview mode, you can single-click boxes in the top view
106 to move to the offset that they represent. Double click brings
107 you to the hex representation. */
108 if (*mode == OVERVIEW_MODE && (action == BUTTON1_CLICKED ||
109 action == BUTTON1_DOUBLE_CLICKED)) {
110
111 /* If the mouse is out of bounds, return. */
112 if (mouse_x < SIDE_MARGIN || mouse_x > width - SIDE_MARGIN - 1
113 || mouse_y < 2 || mouse_y > height - 8)
114 return;
115
116 /* Calculate the box it falls in. */
117 index = (width-SIDE_MARGIN*2) * (mouse_y-2) +
118 mouse_x-SIDE_MARGIN;
119
120 /* Set the offset to the value in the box. */
121 if (index < total_blocks && index >= 0)
122 *file_offset = offset_index[index];
123
124 /* If double-clicked, set to HEX MODE. */
125 if (action == BUTTON1_DOUBLE_CLICKED)
126 *mode = HEX_MODE;
127 }
128
129 return;
130 }
131
132
133 /* #####################################################################
134 ## GENERATE TITLE BAR ##
135 ##################################################################### */
136
generate_titlebar(struct file * file_one,struct file * file_two,unsigned long file_offset,int width,int height,char mode,int display)137 static void generate_titlebar(struct file *file_one, struct file *file_two,
138 unsigned long file_offset, int width, int height,
139 char mode, int display)
140 {
141 int i;
142 char title_offset[32];
143 char bottom_message[128];
144
145 /* Define and set colour for the title bar. */
146 init_pair(TITLE_BAR, COLOR_BLACK, COLOR_WHITE);
147 attron(COLOR_PAIR(TITLE_BAR) | A_BOLD);
148
149 /* Create the title bar background. */
150 for (i = 0; i < width; i++) {
151 mvprintw(0, i, " ");
152 mvprintw(height-1, i, " ");
153 }
154
155 /* Create the title. */
156 mvprintw(0, SIDE_MARGIN, "hexcompare: %s vs. %s",
157 file_one->name, file_two->name);
158
159 /* Indicate file offset. */
160 sprintf(title_offset, " 0x%04x", (unsigned int) file_offset);
161 mvprintw(0, width-strlen(title_offset)-SIDE_MARGIN, "%s",
162 title_offset);
163
164 /* Write bottom menu options. */
165 strcpy(bottom_message, "Quit: q | ");
166
167 if (display == HEX_VIEW) {
168 strcat(bottom_message, "Hex Mode: m | ");
169 } else {
170 strcat(bottom_message, "ASCII Mode: m | ");
171 }
172
173 if (mode == OVERVIEW_MODE) {
174 strcat(bottom_message, "Full View: v | Page & Arrow Keys to Move");
175 } else {
176 strcat(bottom_message, "Mixed View: v | Arrow Keys to Move");
177 }
178
179 mvprintw(height-1, SIDE_MARGIN, "%s", bottom_message);
180
181 /* Set the colour scheme back to default. */
182 attroff(COLOR_PAIR(TITLE_BAR) | A_BOLD);
183
184 return;
185 }
186
187 /* #####################################################################
188 ## GENERATE BLOCK DATA FOR OVERVIEW MODE ##
189 ##################################################################### */
190
generate_blocks(struct file * file_one,struct file * file_two,char * block_cache,int total_blocks,unsigned long bytes_per_block,int blocks_with_excess_byte)191 static char *generate_blocks(struct file *file_one, struct file *file_two,
192 char *block_cache, int total_blocks,
193 unsigned long bytes_per_block,
194 int blocks_with_excess_byte)
195 {
196 int i;
197 unsigned char *block_one, *block_two;
198
199 block_one = malloc(bytes_per_block + 1);
200 block_two = malloc(bytes_per_block + 1);
201
202 /* De-allocate existing memory that holds the block data. */
203 if (block_cache != NULL) free(block_cache);
204
205 /* Allocate the correct amount of memory and initialize it. */
206 block_cache = malloc(total_blocks);
207 memset(block_cache, BLOCK_EMPTY, total_blocks);
208
209 /* Seek to start of file. */
210 fseek(file_one->pointer, 0, SEEK_SET);
211 fseek(file_two->pointer, 0, SEEK_SET);
212
213 /* Compare bytes of file_one with file_two. Store results in */
214 /* a dynamically-sized block_cache. */
215 for (i = 0; i < total_blocks; i++) {
216 int bytes_read_one, bytes_read_two;
217 size_t bytes_in_block, j;
218
219 /* Calculate how many bytes to read for this block. */
220 if (i < blocks_with_excess_byte) {
221 bytes_in_block = bytes_per_block + 1;
222 } else {
223 bytes_in_block = bytes_per_block;
224 }
225
226 /* Set the default. */
227 block_cache[i] = BLOCK_SAME;
228
229 /* Clear the block of data. */
230 memset(block_one, '\0', bytes_in_block);
231 memset(block_two, '\0', bytes_in_block);
232
233 /* Read in the next block of data. */
234 bytes_read_one = fread(block_one, bytes_in_block,
235 1, file_one->pointer);
236 bytes_read_two = fread(block_two, bytes_in_block,
237 1, file_two->pointer);
238
239 /* Stop here if we read 0 bytes. Both files are fully read. */
240 if (bytes_read_one == 0 && bytes_read_two == 0) {
241 block_cache[i] = BLOCK_EMPTY;
242 break;
243 }
244
245 /* If the file lengths don't match up, we know the blocks are
246 different. Go to the next block. */
247 if (bytes_read_one != bytes_read_two) {
248 block_cache[i] = BLOCK_DIFFERENT;
249 continue;
250 }
251
252 /* Both blocks have the same length. Compare them character by
253 character. */
254 for (j = 0; j < bytes_in_block; j++) {
255 if (block_one[j] != block_two[j]) {
256 block_cache[i] = BLOCK_DIFFERENT;
257 break;
258 }
259 }
260 }
261
262 /* free memory */
263 free(block_one);
264 free(block_two);
265
266 return block_cache;
267 }
268
269 /* #####################################################################
270 ## BLOCK OFFSET FUNCTIONS FOR OVERVIEW MODE ##
271 ##################################################################### */
272
generate_offsets(unsigned long * offset_index,int total_blocks,unsigned long bytes_per_block,int blocks_with_excess_byte)273 static unsigned long *generate_offsets(unsigned long *offset_index,
274 int total_blocks,
275 unsigned long bytes_per_block,
276 int blocks_with_excess_byte)
277 {
278 int i;
279 unsigned long offset = 0;
280
281 /* De-allocate existing memory that holds the offset data. */
282 if (offset_index != NULL) free(offset_index);
283
284 /* Allocate the correct amount of memory and initialize it. */
285 offset_index = malloc(total_blocks * sizeof(unsigned long));
286 memset(offset_index, 0, total_blocks);
287
288 /* Generate offset data. */
289 for (i = 0; i < total_blocks; i++) {
290 offset_index[i] = offset;
291 if (i < blocks_with_excess_byte) {
292 offset += bytes_per_block + 1;
293 } else {
294 offset += bytes_per_block;
295 }
296 }
297
298 return offset_index;
299 }
300
calculate_offset(unsigned long file_offset,unsigned long * offset_index,int width,int total_blocks,int shift_type,unsigned long largest_file_size)301 static unsigned long calculate_offset(unsigned long file_offset,
302 unsigned long *offset_index, int width,
303 int total_blocks, int shift_type,
304 unsigned long largest_file_size)
305 {
306
307 /* Initialize variables. */
308 unsigned long new_offset = file_offset;
309 int blocks_in_row = width - SIDE_MARGIN*2;
310 int current_block = 0;
311
312 /* Calculate parameters for the offset. */
313 int offset_char_size = calculate_max_offset_characters(largest_file_size);
314 int hex_width = width - offset_char_size - 3 - SIDE_MARGIN * 2;
315 int offset_jump = (hex_width - (hex_width % 4)) / 4;
316
317 /* Locate the current block we're in. */
318 current_block = calculate_current_block(total_blocks, file_offset,
319 offset_index);
320
321 /* Return the offset of the block we want. */
322 switch (shift_type) {
323 case LEFT_BLOCK:
324 if (current_block > 0) current_block--;
325 break;
326 case RIGHT_BLOCK:
327 if (current_block < total_blocks - 1) current_block++;
328 break;
329 case UP_ROW:
330 if (current_block - blocks_in_row < 0) {
331 current_block = 0;
332 } else {
333 current_block -= blocks_in_row;
334 }
335 break;
336 case DOWN_ROW:
337 if (current_block + blocks_in_row >= total_blocks) {
338 current_block = total_blocks - 1;
339 } else {
340 current_block += blocks_in_row;
341 }
342 break;
343 case UP_LINE:
344 if (file_offset - offset_jump > file_offset) {
345 current_block = 0;
346 break;
347 } else {
348 return file_offset - offset_jump + 1;
349 }
350 case DOWN_LINE:
351 if (file_offset + offset_jump >= largest_file_size) {
352 return file_offset;
353 } else {
354 return file_offset + offset_jump - 1;
355 }
356 }
357
358 new_offset = offset_index[current_block];
359 return new_offset;
360 }
361
362 /* returns a pointer to the 'filename' part of a path. One could argue
363 * that basename() would be appropriate here, but the problem is that
364 * some platforms have a basename() that modifies the passed string,
365 * which we want to avoid. */
getfilename(char * f)366 static char *getfilename(char *f) {
367 char *res = f;
368 for (; *f != 0; f += 1) {
369 if ((*f == '/') || (*f == '\\')) {
370 res = f + 1;
371 }
372 }
373 return(res);
374 }
375
376 /* #####################################################################
377 ## DRAW ROWS OF RAW DATA IN HEX/ASCII FORM ##
378 ##################################################################### */
379
display_file_names(int row,struct file * file_one,struct file * file_two,int offset_char_size,int offset_jump)380 static void display_file_names(int row, struct file *file_one,
381 struct file *file_two, int offset_char_size,
382 int offset_jump)
383 {
384 char *filename_one, *filename_two;
385
386 /* ltrim the filenames if any / character is found */
387 filename_one = getfilename(file_one->name);
388 filename_two = getfilename(file_two->name);
389
390 /* Display the file names. */
391 attron(COLOR_PAIR(TITLE_BAR));
392 mvprintw(row, SIDE_MARGIN+offset_char_size+3, " %s ",
393 filename_one);
394 mvprintw(row, SIDE_MARGIN+offset_char_size+4+
395 offset_jump*2, " %s ", filename_two);
396 attroff(COLOR_PAIR(TITLE_BAR));
397 }
398
display_offsets(int start_row,int finish_row,int offset_jump,int offset_char_size,unsigned long file_offset)399 static void display_offsets(int start_row, int finish_row, int offset_jump,
400 int offset_char_size, unsigned long file_offset)
401 {
402 int i;
403 char offset_line[32];
404 unsigned long temp_offset = file_offset;
405
406 attron(COLOR_PAIR(TITLE_BAR));
407 for (i = start_row; i < finish_row; i++) {
408 sprintf(offset_line, "0x%%0%ix ", offset_char_size);
409 mvprintw(i, SIDE_MARGIN, offset_line, temp_offset);
410 temp_offset += offset_jump - 1;
411 }
412 attroff(COLOR_PAIR(TITLE_BAR));
413 }
414
draw_hex_data(int start_row,int finish_row,struct file * file_one,struct file * file_two,unsigned long file_offset,int offset_char_size,int offset_jump,int display)415 static void draw_hex_data(int start_row, int finish_row, struct file *file_one,
416 struct file *file_two, unsigned long file_offset,
417 int offset_char_size, int offset_jump, int display)
418 {
419
420 unsigned long temp_offset = file_offset;
421 int i, j;
422
423 for (i = start_row; i < finish_row; i++) {
424 int bold = 0;
425 for (j = SIDE_MARGIN+offset_char_size+3; j <
426 SIDE_MARGIN+offset_char_size+offset_jump*2+1; j += 2) {
427 int colour_pair;
428 unsigned char byte_one, byte_two;
429 char byte_one_hex[16], byte_two_hex[16];
430 char byte_one_ascii, byte_two_ascii;
431 int bytes_read_one, bytes_read_two;
432
433 /* Seek to the proper locations in the file. */
434 fseek(file_one->pointer, temp_offset, SEEK_SET);
435 fseek(file_two->pointer, temp_offset, SEEK_SET);
436
437 /* Read a byte from the files. */
438 bytes_read_one = fread(&byte_one, 1, 1, file_one->pointer);
439 bytes_read_two = fread(&byte_two, 1, 1, file_two->pointer);
440
441 /* Convert binary to ASCII hex. */
442 sprintf(byte_one_hex, "%02x", byte_one);
443 sprintf(byte_two_hex, "%02x", byte_two);
444 byte_one_hex[2] = '\0';
445 byte_two_hex[2] = '\0';
446
447 /* Interpret ASCII version of bytes. */
448 byte_one_ascii = raw_to_ascii(byte_one);
449 byte_two_ascii = raw_to_ascii(byte_two);
450
451 /* Make every other byte bold. */
452 if (bold != 0) attron(A_BOLD);
453
454 /* Post results. */
455
456 /* Byte 1:
457 Determine if its EMPTY/DIFFERENT/SAME. */
458 if (bytes_read_one == 0) {
459 colour_pair = BLOCK_EMPTY;
460 } else if (bytes_read_two == 0) {
461 colour_pair = BLOCK_DIFFERENT;
462 } else if (byte_one == byte_two) {
463 colour_pair = BLOCK_SAME;
464 } else {
465 colour_pair = BLOCK_DIFFERENT;
466 }
467
468 /* Display the block. */
469 attron(COLOR_PAIR(colour_pair));
470 if (colour_pair == BLOCK_EMPTY) {
471 mvprintw(i,j, " ");
472 } else if (display == HEX_VIEW) {
473 mvprintw(i,j, " %c", byte_one_ascii);
474 } else {
475 mvprintw(i,j, "%s", byte_one_hex);
476 }
477 attroff(COLOR_PAIR(colour_pair));
478
479 /* Byte 2:
480 Determine if its EMPTY/DIFFERENT/SAME. */
481 if (bytes_read_two == 0) {
482 colour_pair = BLOCK_EMPTY;
483 } else if (bytes_read_one == 0) {
484 colour_pair = BLOCK_DIFFERENT;
485 } else if (byte_one == byte_two) {
486 colour_pair = BLOCK_SAME;
487 } else {
488 colour_pair = BLOCK_DIFFERENT;
489 }
490
491 /* Display the block. */
492 attron(COLOR_PAIR(colour_pair));
493 if (colour_pair == BLOCK_EMPTY) {
494 mvprintw(i,j+offset_jump*2+1, " ");
495 } else if (display == HEX_VIEW) {
496 mvprintw(i,j+offset_jump*2+1, " %c", byte_two_ascii);
497 } else {
498 mvprintw(i,j+offset_jump*2+1, "%s", byte_two_hex);
499 }
500 attroff(COLOR_PAIR(colour_pair));
501
502 /* Switch bold characters with non-bold characters. */
503 if (bold != 0) attroff(A_BOLD);
504 bold ^= 1;
505
506 temp_offset++;
507 }
508 }
509
510 return;
511 }
512
513 /* #####################################################################
514 ## GENERATE SCREEN IN OVERVIEW MODE ##
515 ##################################################################### */
516
generate_overview(struct file * file_one,struct file * file_two,unsigned long * file_offset,int width,int height,char * block_cache,int total_blocks,unsigned long * offset_index,int display,unsigned long largest_file_size)517 static void generate_overview(struct file *file_one, struct file *file_two,
518 unsigned long *file_offset, int width,
519 int height, char *block_cache, int total_blocks,
520 unsigned long *offset_index, int display,
521 unsigned long largest_file_size)
522 {
523
524 /* In overview mode:
525
526 BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM
527 BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM
528 BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM
529 BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM-BLOCKDIAGRAM
530
531 FILENAME 1 FILENAME 2
532 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
533 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
534 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
535
536 Where BLOCKDIAGRAM is the blue/red squares comparing
537 hex blocks from file 1 and file 2. Size is variable.
538 SCROLLBAR is the scrollbar representing how far in
539 the file we are, HEX1 is the hex for file 1 from the
540 offset, and HEX2 is the hex for file 2 from the
541 offset. */
542
543 /* Create variables. */
544 int i, j;
545 int offset_char_size;
546 int hex_width;
547 int offset_jump;
548 int current_block;
549
550 /* Define colors block diagram. */
551 init_pair(BLOCK_SAME, COLOR_WHITE, COLOR_BLUE);
552 init_pair(BLOCK_DIFFERENT, COLOR_WHITE, COLOR_RED);
553 init_pair(BLOCK_EMPTY, COLOR_BLACK, COLOR_CYAN);
554 init_pair(BLOCK_ACTIVE, COLOR_BLACK, COLOR_YELLOW);
555 init_pair(TITLE_BAR, COLOR_BLACK, COLOR_WHITE);
556
557 /* Find which block in the diagram is active based off of
558 the current offset. */
559
560 /* Generate the block diagram. */
561 for (i = 0; i < height - VERTICAL_BLACK_SPACE; i++) {
562 for (j = 0; j < width - SIDE_MARGIN*2; j++) {
563
564 /* Draw the blocks that are matching/different/empty. */
565 int index = i*(width-SIDE_MARGIN*2)+j;
566 attron(COLOR_PAIR(block_cache[index]));
567 mvprintw(i+2,j+SIDE_MARGIN," ");
568 attroff(COLOR_PAIR(block_cache[index]));
569 }
570 }
571
572 /* Show the active block. */
573 current_block = calculate_current_block(total_blocks, *file_offset, offset_index);
574 attron(COLOR_PAIR(BLOCK_ACTIVE));
575 mvprintw(current_block / (width - SIDE_MARGIN*2) + 2,
576 current_block % (width - SIDE_MARGIN*2) + SIDE_MARGIN," ");
577 attroff(COLOR_PAIR(BLOCK_ACTIVE));
578
579 /* Generate the offset markers.
580 Calculate parameters for the offset. */
581 offset_char_size = calculate_max_offset_characters(largest_file_size);
582 hex_width = width - offset_char_size - 3 - SIDE_MARGIN * 2;
583 offset_jump = (hex_width - (hex_width % 4)) / 4;
584
585 /* Display the offsets.
586 Display the hex offsets on the left. */
587 display_offsets(height-7, height-2, offset_jump, offset_char_size,
588 *file_offset);
589
590 /* Generate HEX characters
591 Seek to initial offset. */
592 draw_hex_data(height - 7, height - 2, file_one, file_two,
593 *file_offset, offset_char_size, offset_jump, display);
594
595 /* Write the file titles. */
596 display_file_names(height-8, file_one, file_two, offset_char_size,
597 offset_jump);
598
599 return;
600 }
601
602 /* #####################################################################
603 ## GENERATE SCREEN IN HEX MODE ##
604 ##################################################################### */
605
generate_hex(struct file * file_one,struct file * file_two,unsigned long * file_offset,int width,int height,int display,unsigned long largest_file_size)606 static void generate_hex(struct file *file_one, struct file *file_two,
607 unsigned long *file_offset, int width, int height,
608 int display, unsigned long largest_file_size)
609 {
610
611 /* In hex mode:
612
613 FILENAME 1 FILENAME 2
614 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
615 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
616 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
617 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
618 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
619 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
620 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
621 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
622 OFFSET HEX1-HEX1-HEX1-HEX1-HEX1 HEX2-HEX2-HEX2-HEX2
623 */
624
625 /* Generate the offset markers.
626 Calculate parameters for the offset. */
627 int offset_char_size = calculate_max_offset_characters(largest_file_size);
628 int hex_width = width - offset_char_size - 3 - SIDE_MARGIN * 2;
629 int offset_jump = (hex_width - (hex_width % 4)) / 4;
630
631 /* Define colors block diagram. */
632 init_pair(BLOCK_SAME, COLOR_WHITE, COLOR_BLUE);
633 init_pair(BLOCK_DIFFERENT, COLOR_WHITE, COLOR_RED);
634 init_pair(BLOCK_EMPTY, COLOR_BLACK, COLOR_CYAN);
635 init_pair(BLOCK_ACTIVE, COLOR_BLACK, COLOR_YELLOW);
636 init_pair(TITLE_BAR, COLOR_BLACK, COLOR_WHITE);
637
638 /* Display the hex offsets on the left. */
639 display_offsets(3, height-2, offset_jump, offset_char_size,
640 *file_offset);
641
642 /* Generate HEX characters
643 Seek to initial offset. */
644 draw_hex_data(3, height - 2, file_one, file_two,
645 *file_offset, offset_char_size, offset_jump, display);
646
647
648 /* Write the file titles. */
649 display_file_names(2, file_one, file_two, offset_char_size,
650 offset_jump);
651
652 return;
653 }
654
655 /* #####################################################################
656 ## GENERATE SCREEN VIEW ##
657 ##################################################################### */
658
generate_screen(struct file * file_one,struct file * file_two,char mode,unsigned long * file_offset,int width,int height,char * block_cache,int total_blocks,unsigned long * offset_index,int display,unsigned long largest_file_size)659 static void generate_screen(struct file *file_one, struct file *file_two,
660 char mode, unsigned long *file_offset, int width,
661 int height, char *block_cache, int total_blocks,
662 unsigned long *offset_index, int display,
663 unsigned long largest_file_size)
664 {
665 /* Clear the window. */
666 erase();
667
668 /* Generate the title bar. */
669 generate_titlebar(file_one, file_two, *file_offset, width, height,
670 mode, display);
671
672 /* Generate the window contents according to the mode we're in. */
673 if (mode == OVERVIEW_MODE) {
674 generate_overview(file_one, file_two, file_offset,
675 width, height, block_cache, total_blocks,
676 offset_index, display, largest_file_size);
677
678 } else if (mode == HEX_MODE) {
679 generate_hex(file_one, file_two, file_offset, width, height,
680 display, largest_file_size);
681 }
682 }
683
684 /* #####################################################################
685 ## MAIN FUNCTION ##
686 ##################################################################### */
687
start_gui(struct file * file_one,struct file * file_two,unsigned long largest_file_size)688 void start_gui(struct file *file_one, struct file *file_two,
689 unsigned long largest_file_size)
690 {
691 /* Initiate variables */
692 unsigned long file_offset = 0; /* File offset. */
693 char mode = OVERVIEW_MODE; /* Display mode. */
694 int key_pressed; /* What key is pressed. */
695 char *block_cache = NULL; /* A quick comparison overview. */
696 unsigned long *offset_index = NULL; /* Keep track of offsets per block. */
697 int display = HEX_VIEW; /* ASCII vs. HEX mode. */
698 MEVENT mouse; /* Mouse event struct. */
699 WINDOW *main_window; /* Pointer for main window. */
700
701 int width, height, total_blocks, blocks_with_excess_byte;
702 unsigned long bytes_per_block;
703
704 /* Initiate the display. */
705 main_window = initscr(); /* Start curses mode. */
706 if (has_colors() != TRUE) {
707 puts("Error: Your terminal do not seem to handle colors.");
708 endwin();
709 return;
710 }
711 start_color(); /* Enable the use of colours. */
712 raw(); /* Disable line buffering. */
713 noecho(); /* Don't echo while we get characters. */
714 keypad(stdscr, TRUE); /* Enable capture of arrow keys. */
715 curs_set(0); /* Make the cursor invisible. */
716 mousemask(ALL_MOUSE_EVENTS, NULL); /* Get all mouse events. */
717 clear(); /* Clear out the screen */
718
719 /* Calculate values based on window dimensions. */
720 calculate_dimensions(&width, &height, &total_blocks, &bytes_per_block,
721 largest_file_size, &blocks_with_excess_byte);
722
723 /* Compile the block/offset cache. The block cache contains an index
724 of what the general differences are between the two compared
725 files. It exists to avoid re-comparing the two files every time
726 the screen is regenerated. The offset cache keeps track of what
727 the offsets are for each block in the block diagram, as they
728 may be uneven. */
729
730 block_cache = generate_blocks(file_one, file_two, block_cache,
731 total_blocks, bytes_per_block,
732 blocks_with_excess_byte);
733 offset_index = generate_offsets(offset_index, total_blocks,
734 bytes_per_block, blocks_with_excess_byte);
735
736 /* Generate initial screen contents. */
737 generate_screen(file_one, file_two, mode, &file_offset, width, height,
738 block_cache, total_blocks, offset_index, display,
739 largest_file_size);
740
741 /* Wait for user-keypresses and react accordingly. */
742 for(;;) {
743 /* poll the next keypress event from curses */
744 key_pressed = wgetch(main_window);
745
746 /* if we got 'q' or ESC, then quit */
747 if ((key_pressed == 'q') || (key_pressed == 27)) break;
748
749 switch (key_pressed) {
750 /* Move left/right/down/up on the blog diagram in overview
751 mode. */
752
753 case KEY_LEFT:
754 if (mode == OVERVIEW_MODE)
755 file_offset = calculate_offset(file_offset,
756 offset_index, width, total_blocks,
757 LEFT_BLOCK, largest_file_size);
758 break;
759 case KEY_RIGHT:
760 if (mode == OVERVIEW_MODE)
761 file_offset = calculate_offset(file_offset,
762 offset_index, width, total_blocks,
763 RIGHT_BLOCK, largest_file_size);
764 break;
765 case KEY_UP:
766 if (mode == OVERVIEW_MODE)
767 file_offset = calculate_offset(file_offset,
768 offset_index, width, total_blocks,
769 UP_ROW, largest_file_size);
770 else if (mode == HEX_MODE)
771 file_offset = calculate_offset(file_offset,
772 offset_index, width, total_blocks,
773 UP_LINE, largest_file_size);
774 break;
775 case KEY_DOWN:
776 if (mode == OVERVIEW_MODE)
777 file_offset = calculate_offset(file_offset,
778 offset_index, width, total_blocks,
779 DOWN_ROW, largest_file_size);
780 else if (mode == HEX_MODE)
781 file_offset = calculate_offset(file_offset,
782 offset_index, width, total_blocks,
783 DOWN_LINE, largest_file_size);
784 break;
785 case KEY_NPAGE:
786 file_offset = calculate_offset(file_offset,
787 offset_index, width, total_blocks,
788 DOWN_LINE, largest_file_size);
789 break;
790 case KEY_PPAGE:
791 file_offset = calculate_offset(file_offset,
792 offset_index, width, total_blocks,
793 UP_LINE, largest_file_size);
794 break;
795 case 'm':
796 if (display == ASCII_VIEW) display = HEX_VIEW;
797 else display = ASCII_VIEW;
798 break;
799 case 'v':
800 if (mode == OVERVIEW_MODE) mode = HEX_MODE;
801 else mode = OVERVIEW_MODE;
802 break;
803 case KEY_MOUSE:
804 if (nc_getmouse(&mouse) == OK) {
805
806 /* Left single-click. */
807 if (mouse.bstate & BUTTON1_CLICKED)
808 mouse_clicked(&file_offset, offset_index,
809 width, height, total_blocks, &mode,
810 mouse.x, mouse.y, BUTTON1_CLICKED);
811
812 /* Left double-click. */
813 if (mouse.bstate & BUTTON1_DOUBLE_CLICKED)
814 mouse_clicked(&file_offset, offset_index,
815 width, height, total_blocks, &mode,
816 mouse.x, mouse.y,
817 BUTTON1_DOUBLE_CLICKED);
818 }
819 break;
820
821
822 /* Redraw the window on resize. Recaltulate dimensions,
823 and redo the block/offset cache. */
824 case KEY_RESIZE:
825 calculate_dimensions(&width, &height, &total_blocks,
826 &bytes_per_block, largest_file_size,
827 &blocks_with_excess_byte);
828 block_cache = generate_blocks(file_one, file_two,
829 block_cache, total_blocks, bytes_per_block,
830 blocks_with_excess_byte);
831 offset_index = generate_offsets(offset_index,
832 total_blocks, bytes_per_block,
833 blocks_with_excess_byte);
834 break;
835 }
836
837 generate_screen(file_one, file_two, mode, &file_offset, width,
838 height, block_cache, total_blocks,
839 offset_index, display, largest_file_size);
840 }
841
842 /* End curses mode and exit. */
843 clear();
844 refresh();
845 endwin();
846 free(block_cache);
847 return;
848 }
849