/* * Being that the windows in TDE are numbered and lettered, we can easily * prompt for windows to diff. Might as well do a few standard diff * options: ignore leading space, ignore all space, ignore blank lines, * ignore end-of-line, and Ignore/Match case. Once the diff is defined, * just press one key to find the next diff. Any two visible windows may * be diffed, which is really nice for comparing similar functions or * data in seperate areas of a file. * * * New editor name: TDE, the Thomson-Davis Editor. * Author: Frank Davis * Date: June 5, 1991, version 1.0 * Date: July 29, 1991, version 1.1 * Date: October 5, 1991, version 1.2 * Date: January 20, 1992, version 1.3 * Date: February 17, 1992, version 1.4 * Date: April 1, 1992, version 1.5 * Date: June 5, 1992, version 2.0 * Date: October 31, 1992, version 2.1 * Date: April 1, 1993, version 2.2 * Date: June 5, 1993, version 3.0 * Date: August 29, 1993, version 3.1 * Date: November 13, 1993, version 3.2 * Date: June 5, 1994, version 4.0 * * This code is released into the public domain, Frank Davis. * You may distribute it freely. */ #include "tdestr.h" #include "common.h" #include "define.h" #include "tdefunc.h" /* * Name: define_diff * Purpose: get info needed to initialize diff * Date: October 31, 1992 * Passed: window: pointer to current window * Notes: allow the user to start the diff at the beginning of the * file or at the current cursor location. once the diff * has been defined, the user may press one key to diff again. * user may diff any two visible windows on the screen. */ int define_diff( TDE_WIN *window ) { int rc; int num1; int let1; int num2; int let2; int start; char temp[MAX_COLS+2]; char answer[MAX_COLS+2]; #if defined( __UNIX__ ) chtype display_buff[MAX_COLS+2]; /* chtype is defined in curses.h */ #else char display_buff[(MAX_COLS+2)*2]; #endif rc = OK; /* * get window number and letter of the first diff window. then, * verify that window - does it exit? is it visible? */ if (rc == OK) { *temp = '\0'; rc = get_name( diff_prompt1, window->bottom_line, temp, g_display.message_color ); } if (rc == OK) { rc = verify_number( temp, &num1 ); if (rc == OK) rc = verify_letter( temp, &let1, &diff.w1 ); } if (rc == ERROR) { combine_strings( answer, diff_prompt6a, temp, diff_prompt6b ); error( WARNING, window->bottom_line, answer ); return( ERROR ); } /* * get and verify the next window number and letter to diff. */ *temp = '\0'; rc = get_name( diff_prompt2, window->bottom_line, temp, g_display.message_color ); if (rc == OK) { rc = verify_number( temp, &num2 ); if (rc == OK) rc = verify_letter( temp, &let2, &diff.w2 ); } if (rc == ERROR) { combine_strings( answer, diff_prompt6a, temp, diff_prompt6b ); error( WARNING, window->bottom_line, answer ); return( ERROR ); } /* * are leading spaces significant? */ save_screen_line( 0, window->bottom_line, display_buff ); set_prompt( diff_prompt7a, window->bottom_line ); start = get_yn( ); restore_screen_line( 0, window->bottom_line, display_buff ); if (start != ERROR) diff.leading = start == A_YES ? TRUE : FALSE; else return( ERROR ); /* * are all spaces significant? */ save_screen_line( 0, window->bottom_line, display_buff ); set_prompt( diff_prompt7b, window->bottom_line ); start = get_yn( ); restore_screen_line( 0, window->bottom_line, display_buff ); if (start != ERROR) { if (start == A_YES) diff.leading = diff.all_space = TRUE; else diff.all_space = FALSE; } else return( ERROR ); /* * are blank lines significant? */ save_screen_line( 0, window->bottom_line, display_buff ); set_prompt( diff_prompt7c, window->bottom_line ); start = get_yn( ); restore_screen_line( 0, window->bottom_line, display_buff ); if (start != ERROR) diff.blank_lines = start == A_YES ? TRUE : FALSE; else return( ERROR ); /* * is end of line significant? */ save_screen_line( 0, window->bottom_line, display_buff ); set_prompt( diff_prompt7d, window->bottom_line ); start = get_yn( ); restore_screen_line( 0, window->bottom_line, display_buff ); if (start != ERROR) diff.ignore_eol = start == A_YES ? TRUE : FALSE; else return( ERROR ); /* * now, find out were to start the diff -- beginning of file or * current cursor location. */ save_screen_line( 0, window->bottom_line, display_buff ); set_prompt( diff_prompt3, window->bottom_line ); start = get_bc( ); restore_screen_line( 0, window->bottom_line, display_buff ); if (start != ERROR) { entab_linebuff( ); if (un_copy_line( window->ll, window, TRUE ) == ERROR) return( ERROR ); /* * if everything is everything, initialize the diff pointers. */ diff.defined = TRUE; if (start == BEGINNING) { diff.d1 = diff.w1->file_info->line_list; diff.d2 = diff.w2->file_info->line_list; diff.rline1 = 1L; diff.rline2 = 1L; diff.bin_offset1 = 0; diff.bin_offset2 = 0; rc = differ( 0, 0, window->bottom_line ); } else { diff.d1 = diff.w1->ll; diff.d2 = diff.w2->ll; diff.rline1 = diff.w1->rline; diff.rline2 = diff.w2->rline; diff.bin_offset1 = diff.w1->bin_offset; diff.bin_offset2 = diff.w2->bin_offset; rc = differ( diff.w1->rcol, diff.w2->rcol, window->bottom_line ); } } return( rc ); } /* * Name: repeat_diff * Purpose: compare two cursor positions * Date: October 31, 1992 * Passed: window: pointer to current window * Notes: user may press this key at any time once the diff has been * defined. */ int repeat_diff( TDE_WIN *window ) { register int rc = ERROR; if (diff.defined) { entab_linebuff( ); if (un_copy_line( window->ll, window, TRUE ) == ERROR) return( ERROR ); /* * initialize the diff pointers. */ diff.d1 = diff.w1->ll; diff.d2 = diff.w2->ll; diff.rline1 = diff.w1->rline; diff.rline2 = diff.w2->rline; diff.bin_offset1 = diff.w1->bin_offset; diff.bin_offset2 = diff.w2->bin_offset; rc = differ( diff.w1->rcol, diff.w2->rcol, window->bottom_line ); } else error( WARNING, window->bottom_line, diff_prompt5 ); return( rc ); } /* * Name: differ * Purpose: diff text pointers * Date: October 31, 1992 * Passed: initial_rcol1: beginning column to begin diff in window1 * initial_rcol2: beginning column to begin diff in window2 * bottom: line to display diagnostics * Notes: a straight diff on text pointers is simple; however, diffing * with leading spaces and tabs is kinda messy. let's do the * messy diff. */ int differ( int initial_rcol1, int initial_rcol2, int bottom ) { int rcol1; /* virtual real column on diff window 1 */ int rcol2; /* virtual real column on diff window 2 */ int r1; /* real real column rcol1 - needed for tabs */ int r2; /* real real column rcol2 - needed for tabs */ char c1; /* character under r1 */ char c2; /* character under r2 */ int leading1; /* adjustment for leading space in window 1 */ int leading2; /* adjustment for leading space in window 2 */ int len1; /* length of diff1 line */ int len2; /* length of diff2 line */ line_list_ptr node1; /* scratch node in window 1 */ line_list_ptr node2; /* scratch node in window 2 */ text_ptr diff1; /* scratch text ptr in window 1 */ text_ptr diff2; /* scratch text ptr in window 2 */ long rline1; /* real line number of diff pointer 1 */ long rline2; /* real line number of diff pointer 2 */ long bin_offset1; /* binary offset of diff pointer 1 */ long bin_offset2; /* binary offset of diff pointer 2 */ int len; /* line length variable */ register int tabs; /* local variable for mode.inflate_tabs, T or F */ #if defined( __UNIX__ ) chtype display_buff[MAX_COLS+2]; /* chtype is defined in curses.h */ #else char display_buff[(MAX_COLS+2)*2]; #endif /* * initialize the text pointers and the initial column. skip any * initial blank lines. */ rline1 = diff.rline1; rline2 = diff.rline2; node1 = diff.d1; node2 = diff.d2; bin_offset1 = diff.bin_offset1; bin_offset2 = diff.bin_offset2; tabs = mode.inflate_tabs; if (diff.blank_lines) { while (node1->len != EOF && is_line_blank( node1->line, node1->len )) { bin_offset1 += node1->len; node1 = node1->next; ++rline1; initial_rcol1 = 0; } while (node2->len != EOF && is_line_blank( node2->line , node2->len)) { bin_offset2 += node2->len; node2 = node2->next; ++rline2; initial_rcol2 = 0; } } /* * if everything is everything, initialize the diff variables and diff. */ if (node1->len != EOF && node2->len != EOF) { diff1 = node1->line; diff2 = node2->line; rcol1 = initial_rcol1; rcol2 = initial_rcol2; len1 = node1->len; len2 = node2->len; assert( rcol1 >= 0 ); assert( rcol1 < MAX_LINE_LENGTH ); assert( rcol2 >= 0 ); assert( rcol2 < MAX_LINE_LENGTH ); assert( len1 >= 0 ); assert( len1 < MAX_LINE_LENGTH ); assert( len2 >= 0 ); assert( len2 < MAX_LINE_LENGTH ); /* * if cursors are past EOL, move them back to EOL. */ len = find_end( diff1, len1 ); if (rcol1 > len) rcol1 = len; len = find_end( diff2, len2 ); if (rcol2 > len) rcol2 = len; /* * if skip leading space, make sure our cursors start on first non-space. */ if (diff.leading) { leading1 = skip_leading_space( diff1, len1 ); leading2 = skip_leading_space( diff2, len2 ); if (tabs) { leading1 = detab_adjust_rcol( diff1, leading1 ); leading2 = detab_adjust_rcol( diff2, leading2 ); } if (rcol1 < leading1) rcol1 = leading1; if (rcol2 < leading2) rcol2 = leading2; } /* * we now have a valid rcol for the diff start, we may need to adjust * for tabs, though. */ assert( rcol1 >= 0 ); assert( rcol1 < MAX_LINE_LENGTH ); assert( rcol2 >= 0 ); assert( rcol2 < MAX_LINE_LENGTH ); r1 = tabs ? entab_adjust_rcol( diff1, len1, rcol1 ) : rcol1; r2 = tabs ? entab_adjust_rcol( diff2, len2, rcol2 ) : rcol2; assert( r1 >= 0 ); assert( r1 <= len1 ); assert( r2 >= 0 ); assert( r2 <= len2 ); assert( r1 <= rcol1 ); assert( r2 <= rcol2 ); s_output( diff_message, g_display.mode_line, 67, g_display.diag_color ); while (node1->len != EOF && node2->len != EOF && !g_status.control_break) { /* * look at each character in each diff window */ c1 = (char)(r1 < len1 ? *(diff1 + r1) : 0); c2 = (char)(r2 < len2 ? *(diff2 + r2) : 0); /* * tabs == space */ if (tabs) { if (c1 == '\t') c1 = ' '; if (c2 == '\t') c2 = ' '; } /* * skip spaces, if needed */ if (diff.all_space) { while (c1 == ' ' && r1 < len1) { ++rcol1; r1 = tabs ? entab_adjust_rcol( diff1, len1, rcol1 ) : rcol1; c1 = (char)(r1 < len1 ? *(diff1 + r1) : 0); if (c1 == '\t' && tabs) c1 = ' '; } while (c2 == ' ' && r2 < len2) { ++rcol2; r2 = tabs ? entab_adjust_rcol( diff2, len2, rcol2 ) : rcol2; c2 = (char)(r2 < len2 ? *(diff2 + r2) : 0); if (c2 == '\t' && tabs) c2 = ' '; } } /* * if one of the node pointers has come to EOL, move to next * diff line. */ if (diff.ignore_eol) { if (r1 >= len1) { node1 = skip_eol( node1, &r1, &rcol1, &rline1, &bin_offset1 ); len1 = node1->len; if (len1 != EOF) { diff1 = node1->line; c1 = (char)(r1 < len1 ? *(diff1 + r1) : 0); if (c1 == '\t' && tabs) c1 = ' '; } } if (r2 >= len2) { node2 = skip_eol( node2, &r2, &rcol2, &rline2, &bin_offset2 ); len2 = node2->len; if (len2 != EOF) { diff2 = node2->line; c2 = (char)(r2 < len2 ? *(diff2 + r2) : 0); if (c2 == '\t' && tabs) c2 = ' '; } } } /* * convert the characters to lower case, if needed. */ if (mode.search_case == IGNORE) { c1 = (char)tolower( c1 ); c2 = (char)tolower( c2 ); } /* * diff each character in the diff lines until we reach EOL */ while (r1 < len1 && r2 < len2) { if (c1 == c2) { if (diff.all_space) { do { ++rcol1; r1 = tabs ? entab_adjust_rcol( diff1,len1,rcol1 ) : rcol1; c1 = (char)(r1 < len1 ? *(diff1 + r1) : 0); if (c1 == '\t' && tabs) c1 = ' '; } while (c1 == ' ' && r1 < len1); do { ++rcol2; r2 = tabs ? entab_adjust_rcol( diff2,len2,rcol2 ) : rcol2; c2 = (char)(r2 < len2 ? *(diff2 + r2) : 0); if (c2 == '\t' && tabs) c2 = ' '; } while (c2 == ' ' && r2 < len2); } else { ++rcol1; ++rcol2; r1 = tabs ? entab_adjust_rcol( diff1, len1, rcol1 ) : rcol1; r2 = tabs ? entab_adjust_rcol( diff2, len2, rcol2 ) : rcol2; c1 = (char)(r1 < len1 ? *(diff1 + r1) : 0); c2 = (char)(r2 < len2 ? *(diff2 + r2) : 0); if (tabs) { if (c1 == '\t') c1 = ' '; if (c2 == '\t') c2 = ' '; } } if (diff.ignore_eol) { if (r1 >= len1) { node1 = skip_eol(node1, &r1, &rcol1, &rline1,&bin_offset1); len1 = node1->len; if (len1 != EOF) { diff1 = node1->line; c1 = (char)(r1 < len1 ? *(diff1 + r1) : 0); if (c1 == '\t' && tabs) c1 = ' '; } } if (r2 >= len2) { node2 = skip_eol(node2, &r2, &rcol2, &rline2,&bin_offset2); len2 = node2->len; if (len2 != EOF) { diff2 = node2->line; c2 = (char)(r2 < len2 ? *(diff2 + r2) : 0); if (c2 == '\t' && tabs) c2 = ' '; } } } if (mode.search_case == IGNORE) { c1 = (char)tolower( c1 ); c2 = (char)tolower( c2 ); } } else { /* * when we show the diff, use rcol1 and rcol2, as * find_adjust does not adjust rcol for tabs. */ update_line( diff.w1 ); diff.w1->bin_offset = bin_offset1; find_adjust( diff.w1, node1, rline1, rcol1 ); check_virtual_col( diff.w1, rcol1, rcol1 ); show_diff_window( diff.w1 ); update_line( diff.w2 ); diff.w2->bin_offset = bin_offset2; bin_offset_adjust( diff.w2, rline2 ); find_adjust( diff.w2, node2, rline2, rcol2 ); check_virtual_col( diff.w2, rcol2, rcol2 ); show_diff_window( diff.w2 ); s_output( diff_blank, g_display.mode_line, 67, g_display.mode_color ); return( OK ); } } /* * if we haven't come to the end of a file buffer, check the last * characters. see if pointers are at EOL. */ if (node1->len != EOF && node2->len != EOF) { if (r1 != len1 && r2 != len2) { update_line( diff.w1 ); diff.w1->bin_offset = bin_offset1; find_adjust( diff.w1, node1, rline1, rcol1 ); show_diff_window( diff.w1 ); update_line( diff.w2 ); diff.w2->bin_offset = bin_offset2; find_adjust( diff.w2, node2, rline2, rcol2 ); show_diff_window( diff.w2 ); s_output( diff_blank, g_display.mode_line, 67, g_display.mode_color ); return( OK ); } else { if (r1 == len1) { node1 = skip_eol( node1, &r1, &rcol1, &rline1, &bin_offset1 ); len1 = node1->len; diff1 = node1->line; } if (r2 == len2) { node2 = skip_eol( node2, &r2, &rcol2, &rline2, &bin_offset2 ); len2 = node2->len; diff2 = node2->line; } } } assert( rcol1 >= 0 ); assert( rcol1 < MAX_LINE_LENGTH ); assert( rcol2 >= 0 ); assert( rcol2 < MAX_LINE_LENGTH ); assert( r1 >= 0 ); assert( r1 < MAX_LINE_LENGTH ); assert( r2 >= 0 ); assert( r2 < MAX_LINE_LENGTH ); assert( r1 <= rcol1 ); assert( r2 <= rcol2 ); if (node1->len == EOF) assert( len1 == EOF ); else { assert( len1 >= 0 ); assert( len1 < MAX_LINE_LENGTH ); } if (node2->len == EOF) assert( len2 == EOF ); else { assert( len2 >= 0 ); assert( len2 < MAX_LINE_LENGTH ); } } save_screen_line( 0, bottom, display_buff ); set_prompt( diff_prompt4, bottom ); getkey( ); restore_screen_line( 0, bottom, display_buff ); s_output( diff_blank, g_display.mode_line, 67, g_display.mode_color ); } return( ERROR ); } /* * Name: skip_leading_space * Purpose: put the diff on the first non-blank character * Date: October 31, 1992 * Passed: s: the string to search * len: length of string * Returns: the first non-blank column */ int skip_leading_space( text_ptr s, int len ) { register int count = 0; assert( len >= 0 ); assert( len < MAX_LINE_LENGTH ); if (s != NULL) { if (mode.inflate_tabs) { while (len > 0 && (*s == ' ' || *s == '\t')) { ++count; ++s; --len; } } else { while (len > 0 && *s == ' ') { ++count; ++s; --len; } } } if (len == 0) count = 0; return( count ); } /* * Name: skip_eol * Purpose: move the diff to the next line * Date: October 31, 1992 * Passed: d: pointer to current node * r: tab adjusted real col * rcol: real real col * rline: current line number * bin_offset: offset from the beginning of the file * Returns: next non-blank node */ line_list_ptr skip_eol( line_list_ptr d, int *r, int *rcol, long *rline, long *bin_offset ) { int leading; long rl; long bo; *r = *rcol = 0; rl = *rline; bo = *bin_offset; if (d->len != EOF) { bo += d->len; d = d->next; ++rl; if (diff.blank_lines) { while (d->len != EOF && is_line_blank( d->line, d->len )) { bo += d->len; d = d->next; ++rl; } } if (d->len != EOF) { if (diff.leading) { leading = skip_leading_space( d->line, d->len ); if (mode.inflate_tabs) leading = detab_adjust_rcol( d->line, leading ); *rcol = leading; } else *rcol = 0; *r = *rcol; if (mode.inflate_tabs) *r = entab_adjust_rcol( d->line, d->len, *rcol ); } } *rline = rl; *bin_offset = bo; return( d ); } /* * Name: show_diff_window * Purpose: update the contents of a diff window * Date: October 31, 1992 * Passed: win: pointer to window */ void show_diff_window( TDE_WIN *win ) { if (win->file_info->dirty == LOCAL) display_current_window( win ); else show_curl_line( win ); show_line_col( win ); make_ruler( win ); show_ruler( win ); show_ruler_pointer( win ); win->file_info->dirty = FALSE; } /* * Name: verify_number * Purpose: given a window number, verify the number * Date: October 31, 1992 * Passed: temp: string that contains number * num: successfully converted number. */ int verify_number( char *temp, int *num ) { register file_infos *fp; /* * see if string has a number. if string does have a number, convert it. */ if (*temp == '\0' || !bj_isdigit( *temp )) return( ERROR ); *num = 0; while (bj_isdigit( *temp )) *num = *num * 10 + *temp++ - '0'; /* * now that we have a window number, see if any files have that number */ for (fp=g_status.file_list; fp != NULL; fp=fp->next) { if (fp->file_no == *num) return( OK ); } return( ERROR ); } /* * Name: verify_letter * Purpose: given a window letter, verify the letter * Date: October 31, 1992 * Modified: November 13, 1993, Frank Davis per Byrial Jensen * Passed: temp: string that contains letter * let: window letter * win: pointer to window that contains letter and number * * Change: Control that the letter is in string windowletters */ int verify_letter( char *temp, int *let, TDE_WIN **win ) { register file_infos *fp; register TDE_WIN *wp; int num; if (verify_number( temp, &num ) == OK) { while (bj_isdigit( *temp )) temp++; if (*temp == '\0' || !strchr( windowletters, tolower( *temp ))) return( ERROR ); *let = (int)tolower( *temp ); for (fp=g_status.file_list; fp != NULL; fp=fp->next) { if (fp->file_no == num) break; } for (wp=g_status.window_list; wp != NULL; wp=wp->next) { if (wp->file_info == fp && wp->letter == *let && wp->visible) { *win = wp; return( OK ); } } } return( ERROR ); }