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